From 9574f6e7d2dc14bac3b47426656c004d757ac501 Mon Sep 17 00:00:00 2001 From: natureh Date: Sun, 2 Sep 2012 12:30:59 +0200 Subject: [PATCH] Solving issue 1529: "Better color handling of the Tone curve" + ICM panel code cleanup --- rtdata/languages/Deutsch | 10 +- rtdata/languages/Francais | 13 +- rtdata/languages/default | 12 +- rtdata/profiles/BW-1.pp3 | 3 +- rtdata/profiles/BW-2.pp3 | 3 +- rtdata/profiles/BW-3.pp3 | 3 +- rtdata/profiles/BW-4.pp3 | 3 +- rtdata/profiles/Default-ISO-High.pp3 | 3 +- rtdata/profiles/Default-ISO-Medium.pp3 | 3 +- rtdata/profiles/Default.pp3 | 3 +- rtdata/profiles/Highkey-1.pp3 | 3 +- rtdata/profiles/Natural-1.pp3 | 3 +- rtdata/profiles/Natural-2.pp3 | 3 +- rtdata/profiles/Neutral.pp3 | 3 +- rtdata/profiles/Punchy-1.pp3 | 3 +- rtdata/profiles/Punchy-2.pp3 | 3 +- rtengine/LUT.h | 16 +- rtengine/color.cc | 69 +++++++ rtengine/color.h | 8 +- rtengine/curves.cc | 125 ++++++++----- rtengine/curves.h | 117 ++++++++++-- rtengine/dcp.cc | 73 +------- rtengine/dcp.h | 5 +- rtengine/dcrop.cc | 2 +- rtengine/diagonalcurves.cc | 4 +- rtengine/flatcurves.cc | 4 +- rtengine/improccoordinator.cc | 6 +- rtengine/improccoordinator.h | 2 + rtengine/improcfun.cc | 203 +++++++++++--------- rtengine/improcfun.h | 5 +- rtengine/procevents.h | 4 +- rtengine/procparams.cc | 32 +++- rtengine/procparams.h | 8 + rtengine/refreshmap.cc | 4 +- rtengine/rt_math.h | 5 + rtengine/rtthumbnail.cc | 12 +- rtengine/simpleprocess.cc | 9 +- rtgui/icmpanel.cc | 249 ++++++++++++++----------- rtgui/icmpanel.h | 35 ++-- rtgui/mydiagonalcurve.cc | 6 +- rtgui/paramsedited.cc | 3 + rtgui/paramsedited.h | 1 + rtgui/ppversion.h | 2 +- rtgui/tonecurve.cc | 72 +++++-- rtgui/tonecurve.h | 6 +- 45 files changed, 745 insertions(+), 416 deletions(-) diff --git a/rtdata/languages/Deutsch b/rtdata/languages/Deutsch index cd095e766..5ce927c7e 100644 --- a/rtdata/languages/Deutsch +++ b/rtdata/languages/Deutsch @@ -261,14 +261,14 @@ HISTORY_MSG_37;Farbverstärkung HISTORY_MSG_38;Weißabgleich Methode HISTORY_MSG_39;Weißabgleich Farbtemp. HISTORY_MSG_40;Weißabgleich Farbton -HISTORY_MSG_41;Farbkorrektur "A" +HISTORY_MSG_41;Tonewertkurve Modus HISTORY_MSG_42;Farbkorrektur "B" HISTORY_MSG_43;Luminanz-Rauschfilter HISTORY_MSG_44;Luminanz-Rauschfilter Radius HISTORY_MSG_45;Luminanz-Rauschfilter Kantentoleranz HISTORY_MSG_46;Farb-Rauschfilter -HISTORY_MSG_47;Farb-Rauschfilter Radius -HISTORY_MSG_48;Farb-Rauschfilter Kantentoleranz +HISTORY_MSG_47;ICC Lichter aus Matrix einmischen +HISTORY_MSG_48;DCP Tonwertkurve verwenden HISTORY_MSG_49;Farb-Rauschfilter Kantensuche HISTORY_MSG_50;Schatten/Lichter HISTORY_MSG_51;Lichter abschwächen @@ -901,6 +901,10 @@ TP_EXPOSURE_CURVEEDITOR;Tonwertkurve TP_EXPOSURE_EXPCOMP;Belichtungskorrektur TP_EXPOSURE_LABEL;Belichtung TP_EXPOSURE_SATURATION;Sättigung +TP_EXPOSURE_TCMODE_FILMLIKE;Film-ähnliche +TP_EXPOSURE_TCMODE_LABEL;Tonewertkurve Modus +TP_EXPOSURE_TCMODE_STANDARD;Standard +TP_EXPOSURE_TCMODE_VALBLENDING;Hellwert-Kanal TP_EXPO_AFTER;Nach Interpolation (vor RGB-Konvertierung) TP_FLATFIELD_AUTOSELECT;Automatische Auswahl TP_FLATFIELD_BLURRADIUS;Unschärferadius diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index 0ec7adc44..8aa4773db 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -263,14 +263,14 @@ HISTORY_MSG_37;Exposition auto HISTORY_MSG_38;Méthode de balance des blancs HISTORY_MSG_39;Température de couleur HISTORY_MSG_40;Teinte de balance des blancs -HISTORY_MSG_41;Décalage couleur "A" +HISTORY_MSG_41;Mode courbe tonale HISTORY_MSG_42;Décalage couleur "B" HISTORY_MSG_43;Débruitage de la Luminance HISTORY_MSG_44;Débruitage Lum. - Rayon HISTORY_MSG_45;Débruitage Lum. - Tolérance des bords HISTORY_MSG_46;Débruitage Chromatique -HISTORY_MSG_47;Débruitage Chrom. - Rayon -HISTORY_MSG_48;Débruitage Chrom. - Tolérance des bords +HISTORY_MSG_47;Mélange HL du profil ICC et la matrice +HISTORY_MSG_48;Courbe tonale du profil DCP HISTORY_MSG_49;Débruitage Chrom. - Sensible aux bords HISTORY_MSG_50;Outil Ombres/Hautes lumières HISTORY_MSG_51;Accentuation des hautes lumières @@ -932,6 +932,10 @@ TP_EXPOSURE_CURVEEDITOR;Courbe tonale TP_EXPOSURE_EXPCOMP;Compensation d'exposition TP_EXPOSURE_LABEL;Exposition TP_EXPOSURE_SATURATION;Saturation +TP_EXPOSURE_TCMODE_FILMLIKE;Similaire Film +TP_EXPOSURE_TCMODE_LABEL;Mode +TP_EXPOSURE_TCMODE_STANDARD;Standard +TP_EXPOSURE_TCMODE_VALBLENDING;Canal Valeur TP_EXPO_AFTER; Après l'interpolation (avant la conversion RVB) TP_FLATFIELD_AUTOSELECT;Sélection automatique TP_FLATFIELD_BLURRADIUS;Rayon de floutage @@ -957,7 +961,7 @@ TP_HSVEQUALIZER_LABEL;Égaliseur TSV TP_HSVEQUALIZER_NEUTRAL;Neutre TP_HSVEQUALIZER_SAT;S TP_HSVEQUALIZER_VAL;V -TP_ICM_BLENDCMSMATRIX;Mélange des hautes lumières\ndans l'espace de la matrice +TP_ICM_BLENDCMSMATRIX;Mélange des hautes lumières\ndu profil ICC avec la matrice TP_ICM_BLENDCMSMATRIX_TOOLTIP;Activer la récupération des zones brûlées lorsque les profils ICC basés sur la LUT sont utilisés TP_ICM_FILEDLGFILTERANY;Tous les fichiers TP_ICM_FILEDLGFILTERICM;Fichiers de profil @@ -984,6 +988,7 @@ TP_ICM_PREFERREDPROFILE_2;Tungstène TP_ICM_PREFERREDPROFILE_3;Fluorescent TP_ICM_PREFERREDPROFILE_4;Flash TP_ICM_SAVEREFERENCE;Utiliser l'image comme profil de référence +TP_ICM_TONECURVE;Utiliser la courbe tonale du profil DCP TP_ICM_WORKINGPROFILE;Profil de Travail TP_IMPULSEDENOISE_LABEL;Réduction du bruit d'impulsion TP_IMPULSEDENOISE_THRESH;Seuil diff --git a/rtdata/languages/default b/rtdata/languages/default index 2b3d524aa..cb1e21e77 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -263,14 +263,14 @@ HISTORY_MSG_37;Auto Exposure HISTORY_MSG_38;White Balance Method HISTORY_MSG_39;Color Temperature HISTORY_MSG_40;White Balance Tint -HISTORY_MSG_41;Color Shift "A" +HISTORY_MSG_41;Tone Curve Mode HISTORY_MSG_42;Color Shift "B" HISTORY_MSG_43;Luminance Denoising HISTORY_MSG_44;Lum. Denoising Radius HISTORY_MSG_45;Lum. Denoising Edge Tolerance HISTORY_MSG_46;Color Denoising -HISTORY_MSG_47;Color Denoising Radius -HISTORY_MSG_48;Color Denoising Edge Tolerance +HISTORY_MSG_47;Blend ICC highlights with matrix +HISTORY_MSG_48;Use DCP's tone curve HISTORY_MSG_49;Edge Sensitive Color Denoising HISTORY_MSG_50;Shadow/Highlight Tool HISTORY_MSG_51;Highlight Boost @@ -925,6 +925,10 @@ TP_EXPOSURE_CURVEEDITOR;Tone Curve TP_EXPOSURE_EXPCOMP;Exposure Compensation TP_EXPOSURE_LABEL;Exposure TP_EXPOSURE_SATURATION;Saturation +TP_EXPOSURE_TCMODE_FILMLIKE;Film-like +TP_EXPOSURE_TCMODE_LABEL;Curve mode +TP_EXPOSURE_TCMODE_STANDARD;Standard +TP_EXPOSURE_TCMODE_VALBLENDING;Value channel TP_EXPO_AFTER; After interpolation (before RGB conversion) TP_FLATFIELD_AUTOSELECT;Auto selection TP_FLATFIELD_BLURRADIUS;Blur Radius @@ -976,7 +980,7 @@ TP_ICM_PREFERREDPROFILE_2;Tungsten TP_ICM_PREFERREDPROFILE_3;Fluorescent TP_ICM_PREFERREDPROFILE_4;Flash TP_ICM_SAVEREFERENCE;Save reference image for profiling -TP_ICM_TONECURVE;Use DCP tone curve +TP_ICM_TONECURVE;Use DCP's tone curve TP_ICM_TONECURVE_TOOLTIP;Enable to use tone curves that may be contained in DCP profiles. TP_ICM_WORKINGPROFILE;Working Profile TP_IMPULSEDENOISE_LABEL;Impulse Noise Reduction diff --git a/rtdata/profiles/BW-1.pp3 b/rtdata/profiles/BW-1.pp3 index 78808edfa..a082a2a71 100644 --- a/rtdata/profiles/BW-1.pp3 +++ b/rtdata/profiles/BW-1.pp3 @@ -1,7 +1,7 @@ [Version] AppVersion=4.0.9 -Version=303 +Version=304 [General] Rank=0 @@ -19,6 +19,7 @@ Black=0 HighlightCompr=0 HighlightComprThreshold=33 ShadowCompr=50 +CurveMode=Standard Curve=1;0;0;0.04;0.03;0.17684498029510265;0.21732319394192093;0.70232558139534862;0.74883720930232545;1;1; [Channel Mixer] diff --git a/rtdata/profiles/BW-2.pp3 b/rtdata/profiles/BW-2.pp3 index ac7ede6cb..36ae302a6 100644 --- a/rtdata/profiles/BW-2.pp3 +++ b/rtdata/profiles/BW-2.pp3 @@ -1,7 +1,7 @@ [Version] AppVersion=4.0.9 -Version=303 +Version=304 [General] Rank=0 @@ -19,6 +19,7 @@ Black=0 HighlightCompr=0 HighlightComprThreshold=33 ShadowCompr=50 +CurveMode=Standard Curve=1;0;0;0.45754265471370759;0.57906737998843294;1;1; [Channel Mixer] diff --git a/rtdata/profiles/BW-3.pp3 b/rtdata/profiles/BW-3.pp3 index dd1d6a0e4..0cbf52d8a 100644 --- a/rtdata/profiles/BW-3.pp3 +++ b/rtdata/profiles/BW-3.pp3 @@ -1,7 +1,7 @@ [Version] AppVersion=4.0.9 -Version=303 +Version=304 [General] Rank=0 @@ -19,6 +19,7 @@ Black=0 HighlightCompr=0 HighlightComprThreshold=33 ShadowCompr=50 +CurveMode=Standard Curve=0; [Channel Mixer] diff --git a/rtdata/profiles/BW-4.pp3 b/rtdata/profiles/BW-4.pp3 index fbbe3907b..19447157c 100644 --- a/rtdata/profiles/BW-4.pp3 +++ b/rtdata/profiles/BW-4.pp3 @@ -1,7 +1,7 @@ [Version] AppVersion=4.0.9 -Version=303 +Version=304 [General] Rank=0 @@ -19,6 +19,7 @@ Black=0 HighlightCompr=0 HighlightComprThreshold=33 ShadowCompr=50 +CurveMode=Standard Curve=0; [Channel Mixer] diff --git a/rtdata/profiles/Default-ISO-High.pp3 b/rtdata/profiles/Default-ISO-High.pp3 index 281997725..2c99a83b7 100644 --- a/rtdata/profiles/Default-ISO-High.pp3 +++ b/rtdata/profiles/Default-ISO-High.pp3 @@ -1,7 +1,7 @@ [Version] AppVersion=4.0.9 -Version=303 +Version=304 [General] Rank=0 @@ -19,6 +19,7 @@ Black=0 HighlightCompr=0 HighlightComprThreshold=33 ShadowCompr=50 +CurveMode=Standard Curve=0; [Channel Mixer] diff --git a/rtdata/profiles/Default-ISO-Medium.pp3 b/rtdata/profiles/Default-ISO-Medium.pp3 index 80f8fba5d..62eacf3d9 100644 --- a/rtdata/profiles/Default-ISO-Medium.pp3 +++ b/rtdata/profiles/Default-ISO-Medium.pp3 @@ -1,7 +1,7 @@ [Version] AppVersion=4.0.9 -Version=303 +Version=304 [General] Rank=0 @@ -19,6 +19,7 @@ Black=0 HighlightCompr=0 HighlightComprThreshold=33 ShadowCompr=50 +CurveMode=Standard Curve=0; [Channel Mixer] diff --git a/rtdata/profiles/Default.pp3 b/rtdata/profiles/Default.pp3 index e59589796..bb1bca21b 100644 --- a/rtdata/profiles/Default.pp3 +++ b/rtdata/profiles/Default.pp3 @@ -1,7 +1,7 @@ [Version] AppVersion=4.0.9 -Version=303 +Version=304 [General] Rank=0 @@ -19,6 +19,7 @@ Black=0 HighlightCompr=0 HighlightComprThreshold=33 ShadowCompr=50 +CurveMode=Standard Curve=0; [Channel Mixer] diff --git a/rtdata/profiles/Highkey-1.pp3 b/rtdata/profiles/Highkey-1.pp3 index 466a8e81c..6098b6be1 100644 --- a/rtdata/profiles/Highkey-1.pp3 +++ b/rtdata/profiles/Highkey-1.pp3 @@ -1,7 +1,7 @@ [Version] AppVersion=4.0.9 -Version=303 +Version=304 [General] Rank=0 @@ -19,6 +19,7 @@ Black=0 HighlightCompr=0 HighlightComprThreshold=33 ShadowCompr=50 +CurveMode=Standard Curve=2;0.105;0.25;0.75;15;60;30;-70; [Channel Mixer] diff --git a/rtdata/profiles/Natural-1.pp3 b/rtdata/profiles/Natural-1.pp3 index 3a9c7f554..cba04b30e 100644 --- a/rtdata/profiles/Natural-1.pp3 +++ b/rtdata/profiles/Natural-1.pp3 @@ -1,7 +1,7 @@ [Version] AppVersion=4.0.9 -Version=303 +Version=304 [General] Rank=0 @@ -19,6 +19,7 @@ Black=0 HighlightCompr=0 HighlightComprThreshold=33 ShadowCompr=50 +CurveMode=Standard Curve=1;0;0;0.04;0.03;0.17684498029510265;0.21732319394192093;0.70232558139534862;0.74883720930232545;1;1; [Channel Mixer] diff --git a/rtdata/profiles/Natural-2.pp3 b/rtdata/profiles/Natural-2.pp3 index eb8726aad..954d54ff4 100644 --- a/rtdata/profiles/Natural-2.pp3 +++ b/rtdata/profiles/Natural-2.pp3 @@ -1,7 +1,7 @@ [Version] AppVersion=4.0.9 -Version=303 +Version=304 [General] Rank=0 @@ -19,6 +19,7 @@ Black=0 HighlightCompr=0 HighlightComprThreshold=33 ShadowCompr=50 +CurveMode=Standard Curve=1;0;0;0.45754265471370759;0.57906737998843294;1;1; [Channel Mixer] diff --git a/rtdata/profiles/Neutral.pp3 b/rtdata/profiles/Neutral.pp3 index 9eade4815..9b71f7579 100644 --- a/rtdata/profiles/Neutral.pp3 +++ b/rtdata/profiles/Neutral.pp3 @@ -1,7 +1,7 @@ [Version] AppVersion=4.0.9 -Version=303 +Version=304 [General] Rank=0 @@ -19,6 +19,7 @@ Black=0 HighlightCompr=0 HighlightComprThreshold=0 ShadowCompr=50 +CurveMode=Standard Curve=0; [Channel Mixer] diff --git a/rtdata/profiles/Punchy-1.pp3 b/rtdata/profiles/Punchy-1.pp3 index ac427cb3f..2fbdc0fbd 100644 --- a/rtdata/profiles/Punchy-1.pp3 +++ b/rtdata/profiles/Punchy-1.pp3 @@ -1,7 +1,7 @@ [Version] AppVersion=4.0.9 -Version=303 +Version=304 [General] Rank=0 @@ -19,6 +19,7 @@ Black=0 HighlightCompr=0 HighlightComprThreshold=33 ShadowCompr=50 +CurveMode=Standard Curve=0; [Channel Mixer] diff --git a/rtdata/profiles/Punchy-2.pp3 b/rtdata/profiles/Punchy-2.pp3 index 35457b916..097ce2ebd 100644 --- a/rtdata/profiles/Punchy-2.pp3 +++ b/rtdata/profiles/Punchy-2.pp3 @@ -1,7 +1,7 @@ [Version] AppVersion=4.0.9 -Version=303 +Version=304 [General] Rank=0 @@ -19,6 +19,7 @@ Black=0 HighlightCompr=0 HighlightComprThreshold=33 ShadowCompr=50 +CurveMode=Standard Curve=0; [Channel Mixer] diff --git a/rtengine/LUT.h b/rtengine/LUT.h index 3eb5f4ef0..ab938c968 100644 --- a/rtengine/LUT.h +++ b/rtengine/LUT.h @@ -107,9 +107,7 @@ public: LUT(void) { data = NULL; - owner = 1; - size = 0; - maxs=0; + reset(); } ~LUT() { @@ -117,6 +115,10 @@ public: delete[] data; } + void setClip(int flags) { + clip = flags; + } + LUT & operator=(const LUT &rhs) { if (this != &rhs) { if (rhs.size>this->size) @@ -193,6 +195,14 @@ public: void clear(void) { memset(data, 0, size * sizeof(T)); } + + void reset(void) { + delete[] data; + data = NULL; + owner = 1; + size = 0; + maxs=0; + } }; #endif /* LUT_H_ */ diff --git a/rtengine/color.cc b/rtengine/color.cc index 48721d6e6..e87ea3e83 100644 --- a/rtengine/color.cc +++ b/rtengine/color.cc @@ -151,6 +151,75 @@ namespace rtengine { } + void Color::rgb2hsl(float r, float g, float b, float &h, float &s, float &l) { + + double var_R = double(r) / 65535.0; + double var_G = double(g) / 65535.0; + double var_B = double(b) / 65535.0; + + double m = min(var_R,var_G,var_B); + double M = max(var_R,var_G,var_B); + double C = M - m; + + double l_ = (M+m)/2.; + l = float(l_); + + if (C<0.00001 && C>-0.00001) { // no fabs, slow! + h = 0.f; + s = 0.f; + } + else { + double h_; + if (l_ <= 0.5) + s = float( (M-m) / (M+m) ); + else + s = float( (M-m) / (2.0-M-m) ); + + if ( var_R == M ) h_ = (var_G - var_B)/C; + else if ( var_G == M ) h_ = 2. + (var_B - var_R)/C; + else h_ = 4. + (var_R - var_G)/C; + h = float(h_ /= 6.0); + + if ( h < 0.f ) h += 1.f; + if ( h > 1.f ) h -= 1.f; + } + } + + double Color::hue2rgb(double p, double q, double t){ + if (t < 0.) t += 6.; + else if( t > 6.) t -= 6.; + + if (t < 1.) return p + (q - p) * t; + else if (t < 3.) return q; + else if (t < 4.) return p + (q - p) * (4. - t); + else return p; + } + + void Color::hsl2rgb (float h, float s, float l, float &r, float &g, float &b) { + + if (s == 0) + r = g = b = 65535.0f * l; // achromatic + else { + double m2; + double h_ = double(h); + double s_ = double(s); + double l_ = double(l); + + if (l <= 0.5f) + m2 = l_ * (1.0 + s_); + else { + m2 = l_ + s_ - l_ * s_; + } + + double m1 = 2.0 * l_ - m2; + + r = float(65535.0 * hue2rgb (m1, m2, h_ * 6.0 + 2.0)); + g = float(65535.0 * hue2rgb (m1, m2, h_ * 6.0)); + b = float(65535.0 * hue2rgb (m1, m2, h_ * 6.0 - 2.0)); + } + } + + void Color::rgb2hsv(float r, float g, float b, float &h, float &s, float &v) { double var_R = r / 65535.0; double var_G = g / 65535.0; diff --git a/rtengine/color.h b/rtengine/color.h index 4fb4738db..0e5c00f54 100644 --- a/rtengine/color.h +++ b/rtengine/color.h @@ -20,10 +20,11 @@ #ifndef _COLOR_H_ #define _COLOR_H_ -#include +#include "rt_math.h" #include "LUT.h" #include "labimage.h" #include "iccstore.h" +#include "iccmatrices.h" namespace rtengine { @@ -82,6 +83,7 @@ private: // Separated from init() to keep the code clear static void initMunsell (); + static double hue2rgb(double p, double q, double t); public: const static double sRGBGamma; // standard average gamma @@ -103,6 +105,10 @@ public: static void init (); static void cleanup (); + static float rgbLuminance(float r, float g, float b) { return r*float(xyz_sRGBd65[1][0]) + g*float(xyz_sRGBd65[1][1]) + b*float(xyz_sRGBd65[1][2]); } + static double rgbLuminance(double r, double g, double b) { return r*xyz_sRGBd65[1][0] + g*xyz_sRGBd65[1][1] + b*xyz_sRGBd65[1][2]; } + static void rgb2hsl (float r, float g, float b, float &h, float &s, float &l); + static void hsl2rgb (float h, float s, float l, float &r, float &g, float &b); static void rgb2hsv (float r, float g, float b, float &h, float &s, float &v); static void hsv2rgb (float h, float s, float v, float &r, float &g, float &b); static void hsv2rgb (float h, float s, float v, int &r, int &g, int &b); diff --git a/rtengine/curves.cc b/rtengine/curves.cc index ca2cf158d..faaa3cc0c 100644 --- a/rtengine/curves.cc +++ b/rtengine/curves.cc @@ -293,10 +293,10 @@ namespace rtengine { //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, double hlcomprthresh, - double shcompr, double br, double contr, double gamma_, bool igamma_, + double shcompr, double br, double contr, double gamma_, bool igamma_, int curveMode, const std::vector& curvePoints, LUTu & histogram, LUTu & histogramCropped, LUTf & hlCurve, LUTf & shCurve, LUTf & outCurve, - LUTu & outBeforeCCurveHistogram, int skip) { + LUTu & outBeforeCCurveHistogram, NonStandardToneCurve & outNSToneCurve, int skip) { //double def_mul = pow (2.0, defmul); @@ -339,26 +339,27 @@ namespace rtengine { if (br>0.00001 || br<-0.00001) { std::vector brightcurvePoints; - brightcurvePoints.push_back((double)DCT_NURBS); + brightcurvePoints.resize(9); + brightcurvePoints.at(0) = double(DCT_NURBS); - brightcurvePoints.push_back(0.); //black point. Value in [0 ; 1] range - brightcurvePoints.push_back(0.); //black point. Value in [0 ; 1] range + brightcurvePoints.at(1) = 0.; //black point. Value in [0 ; 1] range + brightcurvePoints.at(2) = 0.; //black point. Value in [0 ; 1] range if(br>0) { - brightcurvePoints.push_back(0.1); //toe point - brightcurvePoints.push_back(0.1+br/150.0); //value at toe point + brightcurvePoints.at(3) = 0.1; //toe point + brightcurvePoints.at(4) = 0.1+br/150.0; //value at toe point - brightcurvePoints.push_back(0.7); //shoulder point - brightcurvePoints.push_back(min(1.0,0.7+br/300.0)); //value at shoulder point + brightcurvePoints.at(5) = 0.7; //shoulder point + brightcurvePoints.at(6) = min(1.0,0.7+br/300.0); //value at shoulder point } else { - brightcurvePoints.push_back(max(0.0,0.1-br/150.0)); //toe point - brightcurvePoints.push_back(0.1); //value at toe point + brightcurvePoints.at(3) = max(0.0,0.1-br/150.0); //toe point + brightcurvePoints.at(4) = 0.1; //value at toe point - brightcurvePoints.push_back(0.7-br/300.0); //shoulder point - brightcurvePoints.push_back(0.7); //value at shoulder point + brightcurvePoints.at(5) = 0.7-br/300.0; //shoulder point + brightcurvePoints.at(6) = 0.7; //value at shoulder point } - brightcurvePoints.push_back(1.); // white point - brightcurvePoints.push_back(1.); // value at white point + brightcurvePoints.at(7) = 1.; // white point + brightcurvePoints.at(8) = 1.; // value at white point brightcurve = new DiagonalCurve (brightcurvePoints, CURVES_MIN_POLY_POINTS/skip); } @@ -442,19 +443,20 @@ namespace rtengine { //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% std::vector contrastcurvePoints; - contrastcurvePoints.push_back((double)DCT_NURBS); + contrastcurvePoints.resize(9); + contrastcurvePoints.at(0) = double(DCT_NURBS); - contrastcurvePoints.push_back(0); //black point. Value in [0 ; 1] range - contrastcurvePoints.push_back(0); //black point. Value in [0 ; 1] range + contrastcurvePoints.at(1) = 0; //black point. Value in [0 ; 1] range + contrastcurvePoints.at(2) = 0; //black point. Value in [0 ; 1] range - contrastcurvePoints.push_back(avg-avg*(0.6-contr/250.0)); //toe point - contrastcurvePoints.push_back(avg-avg*(0.6+contr/250.0)); //value at toe point + contrastcurvePoints.at(3) = avg-avg*(0.6-contr/250.0); //toe point + contrastcurvePoints.at(4) = avg-avg*(0.6+contr/250.0); //value at toe point - contrastcurvePoints.push_back(avg+(1-avg)*(0.6-contr/250.0)); //shoulder point - contrastcurvePoints.push_back(avg+(1-avg)*(0.6+contr/250.0)); //value at shoulder point + contrastcurvePoints.at(5) = avg+(1-avg)*(0.6-contr/250.0); //shoulder point + contrastcurvePoints.at(6) = avg+(1-avg)*(0.6+contr/250.0); //value at shoulder point - contrastcurvePoints.push_back(1); // white point - contrastcurvePoints.push_back(1); // value at white point + contrastcurvePoints.at(7) = 1.; // white point + contrastcurvePoints.at(8) = 1.; // value at white point DiagonalCurve* contrastcurve = new DiagonalCurve (contrastcurvePoints, CURVES_MIN_POLY_POINTS/skip); //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -472,14 +474,23 @@ namespace rtengine { // create a curve if needed bool histNeeded = false; DiagonalCurve* tcurve = NULL; - if (!curvePoints.empty() && curvePoints[0]!=0) { + outNSToneCurve.Reset(); + + if (!curvePoints.empty() && curvePoints[0]>DCT_Linear && curvePoints[0]isIdentity()) { - delete tcurve; - tcurve = NULL; + if (tcurve) { + if (tcurve->isIdentity()) { + delete tcurve; + tcurve = NULL; + } + else if (curveMode!=procparams::ToneCurveParams::TC_MODE_STD) { + outNSToneCurve.Set(tcurve); // it's used in rgbProc, but not merge with the other curves + delete tcurve; + tcurve = NULL; + } } for (int i=0; i<=0xffff; i++) { @@ -499,7 +510,7 @@ namespace rtengine { if (tcurve) { val = tcurve->getVal (dcurve[i]); // TODO: getVal(double) is very slow! Optimize with a LUTf } else { - val = (dcurve[i]); + val = dcurve[i]; } // if inverse gamma is needed, do it (standard sRGB inverse gamma is applied) @@ -542,26 +553,27 @@ namespace rtengine { utili=true; std::vector brightcurvePoints; - brightcurvePoints.push_back((double)((CurveType)DCT_NURBS)); + brightcurvePoints.resize(9); + brightcurvePoints.at(0) = double(DCT_NURBS); - brightcurvePoints.push_back(0.); // black point. Value in [0 ; 1] range - brightcurvePoints.push_back(0.); // black point. Value in [0 ; 1] range + brightcurvePoints.at(1) = 0.; // black point. Value in [0 ; 1] range + brightcurvePoints.at(2) = 0.; // black point. Value in [0 ; 1] range if (br>0) { - brightcurvePoints.push_back(0.1); // toe point - brightcurvePoints.push_back(0.1+br/150.0); //value at toe point + brightcurvePoints.at(3) = 0.1; // toe point + brightcurvePoints.at(4) = 0.1+br/150.0; //value at toe point - brightcurvePoints.push_back(0.7); // shoulder point - brightcurvePoints.push_back(min(1.0,0.7+br/300.0)); //value at shoulder point + brightcurvePoints.at(5) = 0.7; // shoulder point + brightcurvePoints.at(6) = min(1.0,0.7+br/300.0); //value at shoulder point } else { - brightcurvePoints.push_back(0.1-br/150.0); // toe point - brightcurvePoints.push_back(0.1); // value at toe point + brightcurvePoints.at(3) = 0.1-br/150.0; // toe point + brightcurvePoints.at(4) = 0.1; // value at toe point - brightcurvePoints.push_back(min(1.0,0.7-br/300.0)); // shoulder point - brightcurvePoints.push_back(0.7); // value at shoulder point + brightcurvePoints.at(5) = min(1.0,0.7-br/300.0); // shoulder point + brightcurvePoints.at(6) = 0.7; // value at shoulder point } - brightcurvePoints.push_back(1.); // white point - brightcurvePoints.push_back(1.); // value at white point + brightcurvePoints.at(7) = 1.; // white point + brightcurvePoints.at(8) = 1.; // value at white point DiagonalCurve* brightcurve = new DiagonalCurve (brightcurvePoints, CURVES_MIN_POLY_POINTS/skip); //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -609,19 +621,20 @@ namespace rtengine { //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% std::vector contrastcurvePoints; - contrastcurvePoints.push_back((double)((CurveType)DCT_NURBS)); + contrastcurvePoints.resize(9); + contrastcurvePoints.at(0) = double(DCT_NURBS); - contrastcurvePoints.push_back(0.); // black point. Value in [0 ; 1] range - contrastcurvePoints.push_back(0.); // black point. Value in [0 ; 1] range + contrastcurvePoints.at(1) = 0.; // black point. Value in [0 ; 1] range + contrastcurvePoints.at(2) = 0.; // black point. Value in [0 ; 1] range - contrastcurvePoints.push_back(avg-avg*(0.6-contr/250.0)); // toe point - contrastcurvePoints.push_back(avg-avg*(0.6+contr/250.0)); // value at toe point + contrastcurvePoints.at(3) = avg-avg*(0.6-contr/250.0); // toe point + contrastcurvePoints.at(4) = avg-avg*(0.6+contr/250.0); // value at toe point - contrastcurvePoints.push_back(avg+(1-avg)*(0.6-contr/250.0)); // shoulder point - contrastcurvePoints.push_back(avg+(1-avg)*(0.6+contr/250.0)); // value at shoulder point + contrastcurvePoints.at(5) = avg+(1-avg)*(0.6-contr/250.0); // shoulder point + contrastcurvePoints.at(6) = avg+(1-avg)*(0.6+contr/250.0); // value at shoulder point - contrastcurvePoints.push_back(1.); // white point - contrastcurvePoints.push_back(1.); // value at white point + contrastcurvePoints.at(7) = 1.; // white point + contrastcurvePoints.at(8) = 1.; // value at white point DiagonalCurve* contrastcurve = new DiagonalCurve (contrastcurvePoints, CURVES_MIN_POLY_POINTS/skip); //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -733,4 +746,14 @@ namespace rtengine { //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +void NonStandardToneCurve::Reset() { + lutToneCurve.reset(); +} + +// Fill a LUT with X/Y, ranged 0xffff +void NonStandardToneCurve::Set(Curve *pCurve) { + lutToneCurve(0xffff); + for (int i=0;i<0xffff;i++) lutToneCurve[i] = pCurve->getVal(i/(double)0xffff) * 0xffff; +} + } diff --git a/rtengine/curves.h b/rtengine/curves.h index 91a8659b1..316d6d514 100644 --- a/rtengine/curves.h +++ b/rtengine/curves.h @@ -22,10 +22,11 @@ #include #include #include -#include +#include "rt_math.h" #include "../rtgui/mycurve.h" #include "../rtgui/myflatcurve.h" #include "../rtgui/mydiagonalcurve.h" +#include "color.h" #include "LUT.h" @@ -38,6 +39,7 @@ using namespace std; namespace rtengine { + class NonStandardToneCurve; class CurveFactory { @@ -176,8 +178,8 @@ class CurveFactory { public: static void complexCurve (double ecomp, double black, double hlcompr, double hlcomprthresh, double shcompr, double br, double contr, - double gamma_, bool igamma_, const std::vector& curvePoints, LUTu & histogram, LUTu & histogramCropped, - LUTf & hlCurve, LUTf & shCurve,LUTf & outCurve, LUTu & outBeforeCCurveHistogram, int skip=1); + double gamma_, bool igamma_, int curveMode, const std::vector& curvePoints, LUTu & histogram, LUTu & histogramCropped, + LUTf & hlCurve, LUTf & shCurve,LUTf & outCurve, LUTu & outBeforeCCurveHistogram, NonStandardToneCurve & outNSToneCurve, int skip=1); static void complexsgnCurve ( bool & autili, bool & butili, bool & ccutili, bool & cclutili, double saturation, double rstprotection, const std::vector& acurvePoints, const std::vector& bcurvePoints,const std::vector& cccurvePoints,const std::vector& cclurvePoints, LUTf & aoutCurve, LUTf & boutCurve, LUTf & satCurve, LUTf & lhskCurve, int skip=1); static void complexLCurve (double br, double contr, const std::vector& curvePoints, LUTu & histogram, LUTu & histogramCropped, @@ -221,11 +223,12 @@ class Curve { public: Curve (); + virtual ~Curve () {}; void AddPolygons (); - virtual double getVal (double t) = 0; - virtual void getVal (const std::vector& t, std::vector& res) = 0; + virtual double getVal (double t) const = 0; + virtual void getVal (const std::vector& t, std::vector& res) const = 0; - virtual bool isIdentity () = 0; + virtual bool isIdentity () const = 0; }; class DiagonalCurve : public Curve { @@ -244,9 +247,9 @@ class DiagonalCurve : public Curve { DiagonalCurve (const std::vector& points, int ppn=CURVES_MIN_POLY_POINTS); virtual ~DiagonalCurve (); - double getVal (double t); - void getVal (const std::vector& t, std::vector& res); - bool isIdentity () { return kind==DCT_Empty; }; + double getVal (double t) const; + void getVal (const std::vector& t, std::vector& res) const; + bool isIdentity () const { return kind==DCT_Empty; }; }; class FlatCurve : public Curve { @@ -264,10 +267,100 @@ class FlatCurve : public Curve { FlatCurve (const std::vector& points, bool isPeriodic = true, int ppn=CURVES_MIN_POLY_POINTS); virtual ~FlatCurve (); - double getVal (double t); - void getVal (const std::vector& t, std::vector& res); - bool isIdentity () { return kind==FCT_Empty; }; + double getVal (double t) const; + void getVal (const std::vector& t, std::vector& res) const; + bool isIdentity () const { return kind==FCT_Empty; }; }; + +class NonStandardToneCurve { + public: + LUTf lutToneCurve; // 0xffff range + + virtual ~NonStandardToneCurve() {}; + + void Reset(); + void Set(Curve *pCurve); + operator bool (void) const { return lutToneCurve; } +}; + +class AdobeToneCurve : public NonStandardToneCurve { + private: + void RGBTone(float& r, float& g, float& b) const; // helper for tone curve + + public: + void Apply(float& r, float& g, float& b) const; +}; + +class ValueBlendingToneCurve : public NonStandardToneCurve { + private: + float Triangle(float refX, float refY, float X2) const; + public: + void Apply(float& r, float& g, float& b) const; +}; + + +// Tone curve according to Adobe's reference implementation +// values in 0xffff space +// inlined to make sure there will be no cache flush when used +inline void AdobeToneCurve::Apply (float& r, float& g, float& b) const { + +#ifdef _DEBUG + assert (lutToneCurve); +#endif + if (r >= g) { + if (g > b) RGBTone (r, g, b); // Case 1: r >= g > b + else if (b > r) RGBTone (b, r, g); // Case 2: b > r >= g + else if (b > g) RGBTone (r, b, g); // Case 3: r >= b > g + else { // Case 4: r >= g == b + r = lutToneCurve[r]; + g = lutToneCurve[g]; + b = g; + } + } + else { + if (r >= b) RGBTone (g, r, b); // Case 5: g > r >= b + else if (b > g) RGBTone (b, g, r); // Case 6: b > g > r + else RGBTone (g, b, r); // Case 7: g >= b > r + } +} + +inline void AdobeToneCurve::RGBTone (float& r, float& g, float& b) const { + float rold=r,gold=g,bold=b; + + r = lutToneCurve[rold]; + b = lutToneCurve[bold]; + g = b + ((r - b) * (gold - bold) / (rold - bold)); +} + + +inline float ValueBlendingToneCurve::Triangle(float a, float a1, float b) const { + if (a != b) { + float b1; + float a2 = a1 - a; + if (b < a) { b1 = b + a2 * b / a ; } + else { b1 = b + a2 * (1.f-b) / (1.-a); } + return b1; + } + return a1; +} + +// Tone curve modifying the value channel only, preserving hue and saturation +// values in 0xffff space +inline void ValueBlendingToneCurve::Apply (float& r, float& g, float& b) const { + + assert (lutToneCurve); + + float h, s, v, h2, s2, v2; + Color::rgb2hsv(r, g, b, h, s, v); + + r = lutToneCurve[r]; + g = lutToneCurve[g]; + b = lutToneCurve[b]; + Color::rgb2hsv(r, g, b, h2, s2, v2); + + Color::hsv2rgb(h, s, v2, r, g, b); +} + } #undef CLIPI diff --git a/rtengine/dcp.cc b/rtengine/dcp.cc index 5dc3b2dbf..4a8be406b 100644 --- a/rtengine/dcp.cc +++ b/rtengine/dcp.cc @@ -25,7 +25,6 @@ #include "rawimagesource.h" #include "improcfun.h" #include "rt_math.h" -#include "curves.h" using namespace std; using namespace rtengine; @@ -128,11 +127,9 @@ DCPProfile::DCPProfile(Glib::ustring fname, bool isRTProfile) { for (int i=0;igetCount();i++) cPoints.push_back( tag->toDouble(i*TIFFFloatSize) ); // Create the curve - DiagonalCurve toneCurve(cPoints, CURVES_MIN_POLY_POINTS); + DiagonalCurve rawCurve(cPoints, CURVES_MIN_POLY_POINTS); - // Fill a LUT with X/Y, ranged 0xffff - lutToneCurve(0xffff); - for (int i=0;i<0xffff;i++) lutToneCurve[i] = toneCurve.getVal(i/(double)0xffff) * 0xffff; + toneCurve.Set((Curve*)&rawCurve); } if (pFile!=NULL) fclose(pFile); @@ -231,7 +228,7 @@ void DCPProfile::Apply(Imagefloat *pImg, DCPLightType preferredProfile, Glib::us double mXYZCAM[3][3]; const HSBModify* tableBase=GetBestProfile(preferredProfile,mXYZCAM); - bool hasLUT=(iArrayCount>0); useToneCurve&=lutToneCurve; + bool hasLUT=(iArrayCount>0); useToneCurve&=toneCurve; if (!hasLUT && !useToneCurve) { //===== The fast path: no LUT and not tone curve- Calculate matrix for direct conversion raw>working space @@ -448,7 +445,7 @@ void DCPProfile::Apply(Imagefloat *pImg, DCPLightType preferredProfile, Glib::us } // tone curve - if (useToneCurve) ApplyToneCurve(newr, newg, newb); + if (useToneCurve) toneCurve.Apply(newr, newg, newb); pImg->r[y][x] = m2Work[0][0]*newr + m2Work[0][1]*newg + m2Work[0][2]*newb; pImg->g[y][x] = m2Work[1][0]*newr + m2Work[1][1]*newg + m2Work[1][2]*newb; @@ -458,64 +455,6 @@ void DCPProfile::Apply(Imagefloat *pImg, DCPLightType preferredProfile, Glib::us } } -// Tone curve according to Adobes reference implementation -void DCPProfile::ApplyToneCurve (float& r, float& g, float& b) const { - if (r >= g) - { - if (g > b) - { - // Case 1: r >= g > b - RGBTone (r, g, b); - } - - else if (b > r) - { - // Case 2: b > r >= g - RGBTone (b, r, g); - } - - else if (b > g) - { - // Case 3: r >= b > g - RGBTone (r, b, g); - } - - else - { - // Case 4: r >= g == b - r = lutToneCurve[r]; - g = lutToneCurve[g]; - b = g; - } - } - else - { - if (r >= b) - { - // Case 5: g > r >= b - RGBTone (g, r, b); - } - else if (b > g) - { - // Case 6: b > g > r - RGBTone (b, g, r); - } - else - { - // Case 7: g >= b > r - RGBTone (g, b, r); - } - } -} - -void DCPProfile::RGBTone (float& r, float& g, float& b) const { - float rold=r,gold=g,bold=b; - - r = lutToneCurve[rold]; - b = lutToneCurve[bold]; - g = b + ((r - b) * (gold - bold) / (rold - bold)); -} - // Integer variant is legacy, only used for thumbs. Simply take the matrix here void DCPProfile::Apply(Image16 *pImg, DCPLightType preferredProfile, Glib::ustring workingSpace, bool useToneCurve) const { TMatrix mWork = iccStore->workingSpaceInverseMatrix (workingSpace); @@ -523,7 +462,7 @@ void DCPProfile::Apply(Image16 *pImg, DCPLightType preferredProfile, Glib::ustri double mXYZCAM[3][3]; const HSBModify* tableBase=GetBestProfile(preferredProfile,mXYZCAM); - useToneCurve&=lutToneCurve; + useToneCurve&=toneCurve; // Calculate matrix for direct conversion raw>working space double mat[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; @@ -542,7 +481,7 @@ void DCPProfile::Apply(Image16 *pImg, DCPLightType preferredProfile, Glib::ustri newb = mat[2][0]*pImg->r[y][x] + mat[2][1]*pImg->g[y][x] + mat[2][2]*pImg->b[y][x]; // tone curve - if (useToneCurve) ApplyToneCurve(newr, newg, newb); + if (useToneCurve) toneCurve.Apply(newr, newg, newb); pImg->r[y][x] = CLIP((int)newr); pImg->g[y][x] = CLIP((int)newg); pImg->b[y][x] = CLIP((int)newb); } diff --git a/rtengine/dcp.h b/rtengine/dcp.h index 67e6af3a3..53208f3ff 100644 --- a/rtengine/dcp.h +++ b/rtengine/dcp.h @@ -21,6 +21,7 @@ #define _DCP_ #include "imagefloat.h" +#include "curves.h" #include #include #include @@ -47,9 +48,7 @@ namespace rtengine { int iHueStep, iValStep, iArrayCount; - LUTf lutToneCurve; // 0..0xffff values to 0..1 - void ApplyToneCurve(float& r, float& g, float& b) const; - void RGBTone(float& r, float& g, float& b) const; // helper for tone curve + AdobeToneCurve toneCurve; void ConvertDNGMatrix2XYZCAM(const double (*mColorMatrix)[3], double (*mXYZCAM)[3]); diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc index b232ef6df..ff402718e 100644 --- a/rtengine/dcrop.cc +++ b/rtengine/dcrop.cc @@ -141,7 +141,7 @@ void Crop::update (int todo) { if (todo & M_RGBCURVE) parent->ipf.rgbProc (baseCrop, laboCrop, parent->hltonecurve, parent->shtonecurve, parent->tonecurve, cshmap, - params.toneCurve.saturation, parent->rCurve, parent->gCurve, parent->bCurve ); + params.toneCurve.saturation, parent->rCurve, parent->gCurve, parent->bCurve, parent->nonStandardCurve ); /*xref=000;yref=000; if (colortest && cropw>115 && croph>115) diff --git a/rtengine/diagonalcurves.cc b/rtengine/diagonalcurves.cc index 9c74ba88c..545759b4e 100644 --- a/rtengine/diagonalcurves.cc +++ b/rtengine/diagonalcurves.cc @@ -214,7 +214,7 @@ void DiagonalCurve::NURBS_set () { poly_y.push_back(y[N-1]); } -double DiagonalCurve::getVal (double t) { +double DiagonalCurve::getVal (double t) const { switch (kind) { @@ -316,7 +316,7 @@ double DiagonalCurve::getVal (double t) { } } -void DiagonalCurve::getVal (const std::vector& t, std::vector& res) { +void DiagonalCurve::getVal (const std::vector& t, std::vector& res) const { res.resize (t.size()); for (unsigned int i=0; i& t, std::vector& res) { +void FlatCurve::getVal (const std::vector& t, std::vector& res) const { res.resize (t.size()); for (unsigned int i=0; igetGamma(), true, params.toneCurve.curve, - vhist16, histCropped, hltonecurve, shtonecurve, tonecurve, histToneCurve, scale==1 ? 1 : 1); + imgsrc->getGamma(), true, params.toneCurve.curveMode, params.toneCurve.curve, + vhist16, histCropped, hltonecurve, shtonecurve, tonecurve, histToneCurve, nonStandardCurve, scale==1 ? 1 : 1); CurveFactory::RGBCurve (params.rgbCurves.rcurve, rCurve, scale==1 ? 1 : 1); CurveFactory::RGBCurve (params.rgbCurves.gcurve, gCurve, scale==1 ? 1 : 1); @@ -252,7 +252,7 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall) { // if it's just crop we just need the histogram, no image updates if ( todo!=CROP ) { ipf.rgbProc (oprevi, oprevl, hltonecurve, shtonecurve, tonecurve, shmap, params.toneCurve.saturation, - rCurve, gCurve, bCurve, params.toneCurve.expcomp, params.toneCurve.hlcompr, params.toneCurve.hlcomprthresh); + rCurve, gCurve, bCurve, nonStandardCurve, params.toneCurve.expcomp, params.toneCurve.hlcompr, params.toneCurve.hlcomprthresh); } // compute L channel histogram diff --git a/rtengine/improccoordinator.h b/rtengine/improccoordinator.h index e55fff7d1..fe3887221 100644 --- a/rtengine/improccoordinator.h +++ b/rtengine/improccoordinator.h @@ -80,6 +80,8 @@ class ImProcCoordinator : public StagedImageProcessor { LUTf gCurve; LUTf bCurve; + NonStandardToneCurve nonStandardCurve; + LUTu rcurvehist, rcurvehistCropped, rbeforehist; LUTu gcurvehist, gcurvehistCropped, gbeforehist; LUTu bcurvehist, bcurvehistCropped, bbeforehist; diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index 84e53fe37..e4e15012c 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -22,7 +22,6 @@ #include "rtengine.h" #include "improcfun.h" -#include "curves.h" #include "colorclip.h" #include "gauss.h" #include "bilateral2.h" @@ -187,13 +186,13 @@ void ImProcFunctions::firstAnalysis (Imagefloat* original, const ProcParams* par } void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, LUTf & hltonecurve, LUTf & shtonecurve, LUTf & tonecurve, - SHMap* shmap, int sat, LUTf & rCurve, LUTf & gCurve, LUTf & bCurve) { - rgbProc (working, lab, hltonecurve, shtonecurve, tonecurve, shmap, sat, rCurve, gCurve, bCurve, params->toneCurve.expcomp, params->toneCurve.hlcompr, params->toneCurve.hlcomprthresh); + SHMap* shmap, int sat, LUTf & rCurve, LUTf & gCurve, LUTf & bCurve, const NonStandardToneCurve & nonStandardCurve ) { + rgbProc (working, lab, hltonecurve, shtonecurve, tonecurve, shmap, sat, rCurve, gCurve, bCurve, nonStandardCurve, params->toneCurve.expcomp, params->toneCurve.hlcompr, params->toneCurve.hlcomprthresh); } // Process RGB image and convert to LAB space void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, LUTf & hltonecurve, LUTf & shtonecurve, LUTf & tonecurve, - SHMap* shmap, int sat, LUTf & rCurve, LUTf & gCurve, LUTf & bCurve, + SHMap* shmap, int sat, LUTf & rCurve, LUTf & gCurve, LUTf & bCurve, const NonStandardToneCurve & nonStandardCurve, double expcomp, int hlcompr, int hlcomprthresh) { int h_th, s_th; @@ -258,73 +257,111 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, LUTf & hltone const float shoulder = ((65536.0/max(1.0f,exp_scale))*(hlcomprthresh/200.0))+0.1; const float hlrange = 65536.0-shoulder; - + // extracting datas from 'params' to avoid cache flush (to be confirmed) + ToneCurveParams::eTCModeId curveMode = params->toneCurve.curveMode; + bool hasNonStandardToneCurve = bool(nonStandardCurve); + + float chMixRR = float(params->chmixer.red[0]); + float chMixRG = float(params->chmixer.red[1]); + float chMixRB = float(params->chmixer.red[2]); + float chMixGR = float(params->chmixer.green[0]); + float chMixGG = float(params->chmixer.green[1]); + float chMixGB = float(params->chmixer.green[2]); + float chMixBR = float(params->chmixer.blue[0]); + float chMixBG = float(params->chmixer.blue[1]); + float chMixBB = float(params->chmixer.blue[2]); + + int shHighlights = params->sh.highlights; + int shShadows = params->sh.shadows; + #pragma omp parallel for if (multiThread) - for (int i=0; ir[i][j]; - float g = working->g[i][j]; - float b = working->b[i][j]; + float r = working->r[i][j]; + float g = working->g[i][j]; + float b = working->b[i][j]; //if (i==100 & j==100) printf("rgbProc input R= %f G= %f B= %f \n",r,g,b); - if (mixchannels) { - float rmix = (r*params->chmixer.red[0] + g*params->chmixer.red[1] + b*params->chmixer.red[2]) / 100; - float gmix = (r*params->chmixer.green[0] + g*params->chmixer.green[1] + b*params->chmixer.green[2]) / 100; - float bmix = (r*params->chmixer.blue[0] + g*params->chmixer.blue[1] + b*params->chmixer.blue[2]) / 100; + if (mixchannels) { + float rmix = (r*chMixRR + g*chMixRG + b*chMixRB) / 100.f; + float gmix = (r*chMixGR + g*chMixGG + b*chMixGB) / 100.f; + float bmix = (r*chMixBR + g*chMixBG + b*chMixBB) / 100.f; r = rmix; g = gmix; b = bmix; - } + } - if (processSH || processLCE) { - double mapval = 1.0 + shmap->map[i][j]; - double factor = 1.0; - - if (processSH) { - if (mapval > h_th) - factor = (h_th + (100.0 - params->sh.highlights) * (mapval - h_th) / 100.0) / mapval; - else if (mapval < s_th) - factor = (s_th - (100.0 - params->sh.shadows) * (s_th - mapval) / 100.0) / mapval; - } - if (processLCE) { - double sub = lceamount*(mapval-factor*(r*lumimul[0] + g*lumimul[1] + b*lumimul[2])); - r = factor*r-sub; - g = factor*g-sub; - b = factor*b-sub; - } - else { - r = factor*r; - g = factor*g; - b = factor*b; - } - } + if (processSH || processLCE) { + double mapval = 1.0 + shmap->map[i][j]; + double factor = 1.0; + + if (processSH) { + if (mapval > h_th) + factor = (h_th + (100.0 - shHighlights) * (mapval - h_th) / 100.0) / mapval; + else if (mapval < s_th) + factor = (s_th - (100.0 - shShadows) * (s_th - mapval) / 100.0) / mapval; + } + if (processLCE) { + double sub = lceamount*(mapval-factor*(r*lumimul[0] + g*lumimul[1] + b*lumimul[2])); + r = factor*r-sub; + g = factor*g-sub; + b = factor*b-sub; + } + else { + r = factor*r; + g = factor*g; + b = factor*b; + } + } //TODO: proper treatment of out-of-gamut colors //float tonefactor = hltonecurve[(0.299f*r+0.587f*g+0.114f*b)]; float tonefactor=((r(nonStandardCurve); + userToneCurve.Apply(r,g,b); + } + else if (curveMode==ToneCurveParams::TC_MODE_VALBLENDING){ // apply the curve on the value channel + + const ValueBlendingToneCurve& userToneCurve = static_cast(nonStandardCurve); + userToneCurve.Apply(r,g,b); + } + } + + // individual R, G and B tone curves + r = rCurve[r]; + g = gCurve[g]; + b = bCurve[b]; + + //if (r<0 || g<0 || b<0) { // printf("negative values row=%d col=%d r=%f g=%f b=%f \n", i,j,r,g,b); //} @@ -332,79 +369,79 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, LUTf & hltone if (sat!=0 || hCurveEnabled || sCurveEnabled || vCurveEnabled) { float h,s,v; Color::rgb2hsv(r,g,b,h,s,v); - if (sat > 0.5) { - s = (1-(float)sat/100)*s+(float)sat/100*(1-SQR(SQR(1-min(s,1.0f)))); - if (s<0) s=0; + if (sat > 0.5f) { + s = (1.f-float(sat)/100.f)*s+float(sat)/100.f*(1.f-SQR(SQR(1.f-min(s,1.0f)))); + if (s<0) s=0.f; } else { if (sat < -0.5) - s *= 1+(float)sat/100; + s *= 1.f+float(sat)/100.f; } //HSV equalizer if (hCurveEnabled) { - h = (hCurve->getVal((double)h) - 0.5) * 2 + h; - if (h > 1.0) - h -= 1.0; - else if (h < 0.0) - h += 1.0; + h = (hCurve->getVal(double(h)) - 0.5) * 2.f + h; + if (h > 1.0f) + h -= 1.0f; + else if (h < 0.0f) + h += 1.0f; } if (sCurveEnabled) { //shift saturation - float satparam = (sCurve->getVal((double)h)-0.5) * 2; - if (satparam > 0.00001) { - s = (1-satparam)*s+satparam*(1-SQR(1-min(s,1.0f))); - if (s<0) s=0; + float satparam = (sCurve->getVal(double(h))-0.5) * 2; + if (satparam > 0.00001f) { + s = (1.f-satparam)*s+satparam*(1.f-SQR(1.f-min(s,1.0f))); + if (s<0.f) s=0.f; } else { - if (satparam < -0.00001) - s *= 1+satparam; + if (satparam < -0.00001f) + s *= 1.f+satparam; } } if (vCurveEnabled) { - if (v<0) v=0; // important + if (v<0) v=0; // important //shift value - float valparam = vCurve->getVal((double)h)-0.5; - valparam *= (1-SQR(SQR(1-min(s,1.0f)))); - if (valparam > 0.00001) { - v = (1-valparam)*v+valparam*(1-SQR(1-min(v,1.0f))); + float valparam = vCurve->getVal((double)h)-0.5f; + valparam *= (1.f-SQR(SQR(1.f-min(s,1.0f)))); + if (valparam > 0.00001f) { + v = (1.f-valparam)*v+valparam*(1.f-SQR(1.f-min(v,1.0f))); if (v<0) v=0; } else { - if (valparam < -0.00001) - v *= (1+valparam); + if (valparam < -0.00001f) + v *= (1.f+valparam); } } Color::hsv2rgb(h,s,v,r,g,b); } - float x = toxyz[0][0] * r + toxyz[0][1] * g + toxyz[0][2] * b; - float y = toxyz[1][0] * r + toxyz[1][1] * g + toxyz[1][2] * b; - float z = toxyz[2][0] * r + toxyz[2][1] * g + toxyz[2][2] * b; + float x = toxyz[0][0] * r + toxyz[0][1] * g + toxyz[0][2] * b; + float y = toxyz[1][0] * r + toxyz[1][1] * g + toxyz[1][2] * b; + float z = toxyz[2][0] * r + toxyz[2][1] * g + toxyz[2][2] * b; float fx,fy,fz; //if (x>0) { - fx = (x<65535.0 ? cachef[x] : (327.68*exp(log(x/MAXVAL)/3.0 ))); + fx = (x<65535.0f ? cachef[x] : (327.68f*exp(log(x/MAXVAL)/3.0f ))); //} else { // fx = (x>-65535.0 ? -cachef[-x] : (-327.68*exp(log(-x/MAXVAL)/3.0 ))); //} //if (y>0) { - fy = (y<65535.0 ? cachef[y] : (327.68*exp(log(y/MAXVAL)/3.0 ))); + fy = (y<65535.0f ? cachef[y] : (327.68f*exp(log(y/MAXVAL)/3.0f ))); //} else { // fy = (y>-65535.0 ? -cachef[-y] : (-327.68*exp(log(-y/MAXVAL)/3.0 ))); //} //if (z>0) { - fz = (z<65535.0 ? cachef[z] : (327.68*exp(log(z/MAXVAL)/3.0 ))); + fz = (z<65535.0f ? cachef[z] : (327.68f*exp(log(z/MAXVAL)/3.0f ))); //} else { // fz = (z>-65535.0 ? -cachef[-z] : (-327.68*exp(log(-z/MAXVAL)/3.0 ))); //} - lab->L[i][j] = (116.0 * fy - 5242.88); //5242.88=16.0*327.68; - lab->a[i][j] = (500.0 * (fx - fy) ); - lab->b[i][j] = (200.0 * (fy - fz) ); - + lab->L[i][j] = (116.0f * fy - 5242.88f); //5242.88=16.0*327.68; + lab->a[i][j] = (500.0f * (fx - fy) ); + lab->b[i][j] = (200.0f * (fy - fz) ); + + - //test for color accuracy /*float fy = (0.00862069 * lab->L[i][j])/327.68 + 0.137932; // (L+16)/116 float fx = (0.002 * lab->a[i][j])/327.68 + fy; @@ -419,8 +456,8 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, LUTf & hltone r=(float)R; g=(float)G; b=(float)B; float xxx=1;*/ - } - } + } + } if (hCurveEnabled) delete hCurve; if (sCurveEnabled) delete sCurve; diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index d0e3bc8d1..ec659e6b3 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -29,6 +29,7 @@ #include "labimage.h" #include "LUT.h" #include "lcp.h" +#include "curves.h" namespace rtengine { @@ -82,9 +83,9 @@ class ImProcFunctions { void firstAnalysis (Imagefloat* working, const ProcParams* params, LUTu & vhist16, double gamma); void rgbProc (Imagefloat* working, LabImage* lab, LUTf & hltonecurve, LUTf & shtonecurve, LUTf & tonecurve, - SHMap* shmap, int sat, LUTf & rCurve, LUTf & gCurve, LUTf & bCurve); + SHMap* shmap, int sat, LUTf & rCurve, LUTf & gCurve, LUTf & bCurve, const NonStandardToneCurve & nonStandardCurve); void rgbProc (Imagefloat* working, LabImage* lab, LUTf & hltonecurve, LUTf & shtonecurve, LUTf & tonecurve, - SHMap* shmap, int sat, LUTf & rCurve, LUTf & gCurve, LUTf & bCurve, + SHMap* shmap, int sat, LUTf & rCurve, LUTf & gCurve, LUTf & bCurve, const NonStandardToneCurve & nonStandardCurve, double expcomp, int hlcompr, int hlcomprthresh); void luminanceCurve (LabImage* lold, LabImage* lnew, LUTf &curve); diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 606d9a78d..de45f62bb 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -62,13 +62,13 @@ enum ProcEvent { EvWBMethod=37, EvWBTemp=38, EvWBGreen=39, - EvCShiftA=40, // obsolete + EvToneCurveMode=40, EvCShiftB=41, // obsolete EvLDNEnabled=42, // obsolete EvLDNRadius=43, // obsolete EvLDNEdgeTolerance=44, // obsolete EvCDNEnabled=45, // obsolete - EvCDNRadius=46, // obsolete + EvBlendCMSMatrix=46, EvDCPToneCurve=47, EvPrefProfile=48, EvSHEnabled=49, diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 8be258294..89caff871 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -138,6 +138,7 @@ void ProcParams::setDefaults () { toneCurve.shcompr = 50; toneCurve.curve.clear (); toneCurve.curve.push_back(DCT_Linear); + toneCurve.curveMode = ToneCurveParams::TC_MODE_STD; labCurve.brightness = 0; labCurve.contrast = 0; @@ -331,8 +332,8 @@ void ProcParams::setDefaults () { hsvequalizer.vcurve.clear (); hsvequalizer.vcurve.push_back (FCT_Linear); raw.df_autoselect = false; - raw.ff_AutoSelect = false; - raw.ff_BlurRadius = 32; + raw.ff_AutoSelect = false; + raw.ff_BlurRadius = 32; raw.ff_BlurType = RAWParams::ff_BlurTypestring[RAWParams::area_ff]; raw.cared = 0; raw.cablue = 0; @@ -390,6 +391,21 @@ int ProcParams::save (Glib::ustring fname, Glib::ustring fname2, ParamsEdited* p if (!pedited || pedited->toneCurve.hlcompr) keyFile.set_integer ("Exposure", "HighlightCompr", toneCurve.hlcompr); if (!pedited || pedited->toneCurve.hlcomprthresh) keyFile.set_integer ("Exposure", "HighlightComprThreshold", toneCurve.hlcomprthresh); if (!pedited || pedited->toneCurve.shcompr) keyFile.set_integer ("Exposure", "ShadowCompr", toneCurve.shcompr); + if (!pedited || pedited->toneCurve.curveMode) { + Glib::ustring method; + switch (toneCurve.curveMode) { + case (ToneCurveParams::TC_MODE_STD): + method = "Standard"; + break; + case (ToneCurveParams::TC_MODE_FILMLIKE): + method = "FilmLike"; + break; + case (ToneCurveParams::TC_MODE_VALBLENDING): + method = "ValueBlending"; + break; + } + keyFile.set_string ("Exposure", "CurveMode", method); + } if (!pedited || pedited->toneCurve.curve) { Glib::ArrayHandle tcurve = toneCurve.curve; keyFile.set_double_list("Exposure", "Curve", tcurve); @@ -583,8 +599,8 @@ int ProcParams::save (Glib::ustring fname, Glib::ustring fname2, ParamsEdited* p // lens profile if (!pedited || pedited->lensProf.lcpFile) keyFile.set_string ("LensProfile", "LCPFile", lensProf.lcpFile); if (!pedited || pedited->lensProf.useDist) keyFile.set_boolean ("LensProfile", "UseDistortion", lensProf.useDist); - if (!pedited || pedited->lensProf.useVign) keyFile.set_boolean ("LensProfile", "UseVignette", lensProf.useDist); - if (!pedited || pedited->lensProf.useCA) keyFile.set_boolean ("LensProfile", "UseCA", lensProf.useDist); + if (!pedited || pedited->lensProf.useVign) keyFile.set_boolean ("LensProfile", "UseVignette", lensProf.useVign); + if (!pedited || pedited->lensProf.useCA) keyFile.set_boolean ("LensProfile", "UseCA", lensProf.useCA); // save perspective correction if (!pedited || pedited->perspective.horizontal) keyFile.set_integer ("Perspective", "Horizontal", perspective.horizontal); @@ -784,6 +800,13 @@ if (keyFile.has_group ("Exposure")) { if (keyFile.has_key ("Exposure", "HighlightComprThreshold")) { toneCurve.hlcomprthresh = keyFile.get_integer ("Exposure", "HighlightComprThreshold"); if (pedited) pedited->toneCurve.hlcomprthresh = true; } if (keyFile.has_key ("Exposure", "ShadowCompr")) { toneCurve.shcompr = keyFile.get_integer ("Exposure", "ShadowCompr"); if (pedited) pedited->toneCurve.shcompr = true; } if (toneCurve.shcompr > 100) toneCurve.shcompr = 100; // older pp3 files can have values above 100. + if (keyFile.has_key ("Exposure", "CurveMode")) { + Glib::ustring sMode = keyFile.get_string ("Exposure", "CurveMode"); + if (sMode == "Standard") toneCurve.curveMode = ToneCurveParams::TC_MODE_STD; + else if (sMode == "FilmLike") toneCurve.curveMode = ToneCurveParams::TC_MODE_FILMLIKE; + else if (sMode == "ValueBlending") toneCurve.curveMode = ToneCurveParams::TC_MODE_VALBLENDING; + if (pedited) pedited->toneCurve.curveMode = true; + } if (ppVersion>200) if (keyFile.has_key ("Exposure", "Curve")) { toneCurve.curve = keyFile.get_double_list ("Exposure", "Curve"); if (pedited) pedited->toneCurve.curve = true; } } @@ -1235,6 +1258,7 @@ bool ProcParams::operator== (const ProcParams& other) { && toneCurve.autoexp == other.toneCurve.autoexp && toneCurve.clip == other.toneCurve.clip && toneCurve.expcomp == other.toneCurve.expcomp + && toneCurve.curveMode == other.toneCurve.curveMode && labCurve.lcurve == other.labCurve.lcurve && labCurve.acurve == other.labCurve.acurve && labCurve.bcurve == other.labCurve.bcurve diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 2586d6e67..728e92af0 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -180,10 +180,18 @@ class Threshold { class ToneCurveParams { public: + + enum eTCModeId { + TC_MODE_STD, // Standard modes, the curve is applied on all component individually + TC_MODE_FILMLIKE, // Film-like mode, as defined in Adobe's reference code + TC_MODE_VALBLENDING // Modify the Value channel only + }; + bool autoexp; double clip; double expcomp; std::vector curve; + eTCModeId curveMode; int brightness; int black; int contrast; diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index fda350356..c7dfbf519 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -60,13 +60,13 @@ M_VOID, // EvFixedExp WHITEBALANCE, // EvWBMethod, WHITEBALANCE, // EvWBTemp, WHITEBALANCE, // EvWBGreen, -0, // EvCShiftA: obsolete +RGBCURVE, // EvToneCurveMode 0, // EvCShiftB: obsolete 0, // EvLDNEnabled: obsolete, 0, // EvLDNRadius: obsolete, 0, // EvLDNEdgeTolerance: obsolete, 0, // EvCDNEnabled:obsolete, -0, // EvCDNRadius: obsolete, +ALL, // EvBlendCMSMatrix, ALL, // EvDCPToneCurve, ALL, // EvPrefProfile, RETINEX, // EvSHEnabled, diff --git a/rtengine/rt_math.h b/rtengine/rt_math.h index c1049e279..9f8797c2d 100644 --- a/rtengine/rt_math.h +++ b/rtengine/rt_math.h @@ -30,6 +30,11 @@ namespace rtengine { return std::max(b,std::min(a,c)); } + template + inline const _Tp LIM01(const _Tp& a) { + return std::max(_Tp(1),std::min(a,_Tp(0))); + } + template inline const _Tp ULIM(const _Tp& a, const _Tp& b, const _Tp& c) { return ((b < c) ? LIM(a,b,c) : LIM(a,c,b)); diff --git a/rtengine/rtthumbnail.cc b/rtengine/rtthumbnail.cc index 02d4d5fac..67e93381c 100644 --- a/rtengine/rtthumbnail.cc +++ b/rtengine/rtthumbnail.cc @@ -738,12 +738,12 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, int rhei LUTf bCurve (65536); LUTu dummy; - //CurveFactory::complexCurve (expcomp, black/65535.0, params.toneCurve.hlcompr, params.toneCurve.hlcomprthresh, - // params.toneCurve.shcompr, params.toneCurve.brightness, params.toneCurve.contrast, - // gamma, true, params.toneCurve.curve, hist16, dummy, curve1, curve2, curve, dummy, 16); + + NonStandardToneCurve nonStandardCurve; + CurveFactory::complexCurve (expcomp, black/65535.0, hlcompr, hlcomprthresh, - params.toneCurve.shcompr, bright, contr, gamma, true, - params.toneCurve.curve, hist16, dummy, curve1, curve2, curve, dummy, 16); + params.toneCurve.shcompr, bright, contr, gamma, true, params.toneCurve.curveMode, + params.toneCurve.curve, hist16, dummy, curve1, curve2, curve, dummy, nonStandardCurve, 16); CurveFactory::RGBCurve (params.rgbCurves.rcurve, rCurve, 16); CurveFactory::RGBCurve (params.rgbCurves.gcurve, gCurve, 16); @@ -751,7 +751,7 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, int rhei LabImage* labView = new LabImage (fw,fh); - ipf.rgbProc (baseImg, labView, curve1, curve2, curve, shmap, params.toneCurve.saturation, rCurve, gCurve, bCurve, expcomp, hlcompr, hlcomprthresh); + ipf.rgbProc (baseImg, labView, curve1, curve2, curve, shmap, params.toneCurve.saturation, rCurve, gCurve, bCurve, nonStandardCurve, expcomp, hlcompr, hlcomprthresh); if (shmap) delete shmap; diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index 85feb41b6..49eddd282 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -162,8 +162,11 @@ IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* p LUTf bCurve (65536,0); LUTu dummy; - CurveFactory::complexCurve (expcomp, black/65535.0, hlcompr, hlcomprthresh, params.toneCurve.shcompr, bright, contr, imgsrc->getGamma(), true, params.toneCurve.curve, - hist16, dummy, curve1, curve2, curve, dummy); + NonStandardToneCurve nonStandardCurve; + + CurveFactory::complexCurve (expcomp, black/65535.0, hlcompr, hlcomprthresh, params.toneCurve.shcompr, bright, contr, imgsrc->getGamma(), true, + params.toneCurve.curveMode, params.toneCurve.curve, + hist16, dummy, curve1, curve2, curve, dummy, nonStandardCurve); CurveFactory::RGBCurve (params.rgbCurves.rcurve, rCurve, 1); CurveFactory::RGBCurve (params.rgbCurves.gcurve, gCurve, 1); @@ -171,7 +174,7 @@ IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* p LabImage* labView = new LabImage (fw,fh); - ipf.rgbProc (baseImg, labView, curve1, curve2, curve, shmap, params.toneCurve.saturation, rCurve, gCurve, bCurve, expcomp, hlcompr, hlcomprthresh); + ipf.rgbProc (baseImg, labView, curve1, curve2, curve, shmap, params.toneCurve.saturation, rCurve, gCurve, bCurve, nonStandardCurve, expcomp, hlcompr, hlcomprthresh); // Freeing baseImg because not used anymore delete baseImg; diff --git a/rtgui/icmpanel.cc b/rtgui/icmpanel.cc index 3a69ba2d4..ae46a6217 100644 --- a/rtgui/icmpanel.cc +++ b/rtgui/icmpanel.cc @@ -108,7 +108,7 @@ ICMPanel::ICMPanel () : Gtk::VBox(), FoldableToolPanel(this), iunchanged(NULL), Gtk::Label* wlab = Gtk::manage (new Gtk::Label ()); wlab->set_alignment (0.0, 0.5); wlab->set_markup (Glib::ustring("") + M("TP_ICM_WORKINGPROFILE") + ""); - + pack_start (*wlab, Gtk::PACK_SHRINK, 4); wnames = Gtk::manage (new MyComboBoxText ()); pack_start (*wnames, Gtk::PACK_SHRINK, 4); @@ -127,41 +127,39 @@ ICMPanel::ICMPanel () : Gtk::VBox(), FoldableToolPanel(this), iunchanged(NULL), std::vector wpnames = rtengine::getWorkingProfiles (); for (size_t i=0; iappend_text (wpnames[i]); - - - Gtk::HSeparator* hsep22 = Gtk::manage (new Gtk::HSeparator ()); + + + Gtk::HSeparator* hsep22 = Gtk::manage (new Gtk::HSeparator ()); pack_start (*hsep22, Gtk::PACK_SHRINK, 2); - + Gtk::Label* galab = Gtk::manage (new Gtk::Label ()); galab->set_alignment (0.0, 0.5); galab->set_markup (Glib::ustring("") + M("TP_GAMMA_OUTPUT") + ""); - + pack_start (*galab, Gtk::PACK_SHRINK, 4); wgamma = Gtk::manage (new MyComboBoxText ()); pack_start (*wgamma, Gtk::PACK_SHRINK, 4); - - Gtk::HSeparator* hsep23 = Gtk::manage (new Gtk::HSeparator ()); - pack_start (*hsep23, Gtk::PACK_SHRINK, 2); - - freegamma = Gtk::manage(new Gtk::CheckButton((M("TP_GAMMA_FREE")))); - freegamma->set_active (false); - pack_start( *freegamma); - - gampos = Gtk::manage(new Adjuster (M("TP_GAMMA_CURV"),1,3.5,0.01,2.22)); - gampos->setAdjusterListener (this); - if (gampos->delay < 1000) gampos->delay = 1000; - gampos->show(); - slpos = Gtk::manage(new Adjuster (M("TP_GAMMA_SLOP"),0,15,0.01,4.5)); - slpos->setAdjusterListener (this); - if (slpos->delay < 1000) slpos->delay = 1000; - slpos->show(); - pack_start( *gampos, Gtk::PACK_SHRINK, 4);//gamma - pack_start( *slpos, Gtk::PACK_SHRINK, 4);//slope - - gamcsconn = freegamma->signal_toggled().connect ( sigc::mem_fun(*this, &ICMPanel::GamChanged)); - - + Gtk::HSeparator* hsep23 = Gtk::manage (new Gtk::HSeparator ()); + pack_start (*hsep23, Gtk::PACK_SHRINK, 2); + + freegamma = Gtk::manage(new Gtk::CheckButton((M("TP_GAMMA_FREE")))); + freegamma->set_active (false); + pack_start( *freegamma); + + gampos = Gtk::manage(new Adjuster (M("TP_GAMMA_CURV"),1,3.5,0.01,2.22)); + gampos->setAdjusterListener (this); + if (gampos->delay < 1000) gampos->delay = 1000; + gampos->show(); + slpos = Gtk::manage(new Adjuster (M("TP_GAMMA_SLOP"),0,15,0.01,4.5)); + slpos->setAdjusterListener (this); + if (slpos->delay < 1000) slpos->delay = 1000; + slpos->show(); + pack_start( *gampos, Gtk::PACK_SHRINK, 4);//gamma + pack_start( *slpos, Gtk::PACK_SHRINK, 4);//slope + + + std::vector wpgamma = rtengine::getGamma (); for (size_t i=0; iappend_text (wpgamma[i]); @@ -175,7 +173,7 @@ ICMPanel::ICMPanel () : Gtk::VBox(), FoldableToolPanel(this), iunchanged(NULL), wnames->set_active (0); onames->set_active (0); - wgamma->set_active (0); + wgamma->set_active (0); Gtk::FileFilter filter_icc; filter_icc.set_name(M("TP_ICM_FILEDLGFILTERICM")); @@ -200,13 +198,15 @@ ICMPanel::ICMPanel () : Gtk::VBox(), FoldableToolPanel(this), iunchanged(NULL), onames->signal_changed().connect( sigc::mem_fun(*this, &ICMPanel::opChanged) ); wgamma->signal_changed().connect( sigc::mem_fun(*this, &ICMPanel::gpChanged) ); prefprof->signal_changed().connect( sigc::mem_fun(*this, &ICMPanel::prefProfChanged) ); - + + gamcsconn = freegamma->signal_toggled().connect ( sigc::mem_fun(*this, &ICMPanel::GamChanged)); + tcurveconn = ckbToneCurve->signal_toggled().connect ( sigc::mem_fun(*this, &ICMPanel::toneCurveChanged)); + blendcmsconn = ckbBlendCMSMatrix->signal_toggled().connect ( sigc::mem_fun(*this, &ICMPanel::blendCMSMatrixChanged)); + icamera->signal_toggled().connect( sigc::mem_fun(*this, &ICMPanel::ipChanged) ); icameraICC->signal_toggled().connect( sigc::mem_fun(*this, &ICMPanel::ipChanged) ); iembedded->signal_toggled().connect( sigc::mem_fun(*this, &ICMPanel::ipChanged) ); ifromfile->signal_toggled().connect( sigc::mem_fun(*this, &ICMPanel::ipChanged) ); - ckbBlendCMSMatrix->signal_toggled().connect( sigc::mem_fun(*this, &ICMPanel::iccTogglesChanged) ); - ckbToneCurve->signal_toggled().connect( sigc::mem_fun(*this, &ICMPanel::toneCurveChanged) ); ipc = ipDialog->signal_selection_changed().connect( sigc::mem_fun(*this, &ICMPanel::ipSelectionChanged) ); saveRef->signal_pressed().connect( sigc::mem_fun(*this, &ICMPanel::saveReferencePressed) ); @@ -219,6 +219,10 @@ void ICMPanel::read (const ProcParams* pp, const ParamsEdited* pedited) { disableListener (); ipc.block (true); + gamcsconn.block (true); + tcurveconn.block(true); + blendcmsconn.block(true); + if (pp->icm.input == "(none)" && icamera->get_state()!=Gtk::STATE_INSENSITIVE) { inone->set_active (true); prefprof->set_sensitive (false); ckbToneCurve->set_sensitive (false); ckbBlendCMSMatrix->set_sensitive (false); @@ -232,11 +236,11 @@ void ICMPanel::read (const ProcParams* pp, const ParamsEdited* pedited) { ckbBlendCMSMatrix->set_sensitive (true); } else if ((pp->icm.input == "(cameraICC)") && icameraICC->get_state()==Gtk::STATE_INSENSITIVE) { - // this is the case when (cameraICC) is instructed by packaged profiles, but ICC file is not found - // therefore falling back UI to explicitly reflect the (camera) option - icamera->set_active (true); + // this is the case when (cameraICC) is instructed by packaged profiles, but ICC file is not found + // therefore falling back UI to explicitly reflect the (camera) option + icamera->set_active (true); prefprof->set_sensitive (false); ckbToneCurve->set_sensitive (false); // RT's own are always single-illuminant and tone curve disabled - ckbBlendCMSMatrix->set_sensitive (false); + ckbBlendCMSMatrix->set_sensitive (false); } else if ((pp->icm.input == "(camera)" || pp->icm.input=="") && icamera->get_state()!=Gtk::STATE_INSENSITIVE) { icamera->set_active (true); @@ -249,9 +253,9 @@ void ICMPanel::read (const ProcParams* pp, const ParamsEdited* pedited) { ckbBlendCMSMatrix->set_sensitive (true); prefprof->set_sensitive (true); ckbToneCurve->set_sensitive (true); } - wnames->set_active_text (pp->icm.working); - wgamma->set_active_text (pp->icm.gamma); - + wnames->set_active_text (pp->icm.working); + wgamma->set_active_text (pp->icm.gamma); + if (pp->icm.output==ColorManagementParams::NoICMString) onames->set_active_text (M("TP_ICM_NOICM")); else @@ -263,14 +267,24 @@ void ICMPanel::read (const ProcParams* pp, const ParamsEdited* pedited) { prefprof->set_active(pp->icm.preferredProfile-1); ckbToneCurve->set_active (pp->icm.toneCurve); + lastToneCurve = pp->icm.toneCurve; + ckbBlendCMSMatrix->set_active (pp->icm.blendCMSMatrix); - onames->set_sensitive(wgamma->get_active_row_number()==0 || freegamma->get_active()); //"default" - wgamma->set_sensitive(!freegamma->get_active()); - + lastBlendCMSMatrix = pp->icm.blendCMSMatrix; + + onames->set_sensitive(wgamma->get_active_row_number()==0 || freegamma->get_active()); //"default" + wgamma->set_sensitive(!freegamma->get_active()); + + freegamma->set_active (pp->icm.freegamma); + lastgamfree = pp->icm.freegamma; + + gampos->setValue (pp->icm.gampos); + slpos->setValue (pp->icm.slpos); + if (pedited) { iunchanged->set_active (!pedited->icm.input); - ckbToneCurve->set_sensitive (false); - ckbBlendCMSMatrix->set_sensitive (false); + ckbToneCurve->set_inconsistent(!pedited->icm.toneCurve); + ckbBlendCMSMatrix->set_inconsistent(!pedited->icm.blendCMSMatrix); if (!pedited->icm.working) wnames->set_active_text(M("GENERAL_UNCHANGED")); if (!pedited->icm.output) @@ -280,21 +294,15 @@ void ICMPanel::read (const ProcParams* pp, const ParamsEdited* pedited) { if (!pedited->icm.gamma){ wgamma->set_active_text(M("GENERAL_UNCHANGED")); wgamma->set_active_text(M("GENERAL_UNCHANGED")); - } - gampos->setEditedState (pedited->icm.gampos ? Edited : UnEdited); - slpos->setEditedState (pedited->icm.slpos ? Edited : UnEdited); - - } - - gamcsconn.block (true); - freegamma->set_active (pp->icm.freegamma); - gamcsconn.block (false); + } + gampos->setEditedState (pedited->icm.gampos ? Edited : UnEdited); + slpos->setEditedState (pedited->icm.slpos ? Edited : UnEdited); - lastgamfree = pp->icm.freegamma; - gampos->setValue (pp->icm.gampos); - slpos->setValue (pp->icm.slpos); - - + } + + blendcmsconn.block(false); + tcurveconn.block(false); + gamcsconn.block (false); ipc.block (false); enableListener (); @@ -322,17 +330,17 @@ void ICMPanel::write (ProcParams* pp, ParamsEdited* pedited) { pp->icm.working = wnames->get_active_text (); pp->icm.gamma = wgamma->get_active_text (); pp->icm.preferredProfile = prefprof->get_active_row_number()+1; - + if (onames->get_active_text()==M("TP_ICM_NOICM")) pp->icm.output = ColorManagementParams::NoICMString; else pp->icm.output = onames->get_active_text(); - pp->icm.freegamma = freegamma->get_active(); + pp->icm.freegamma = freegamma->get_active(); pp->icm.toneCurve = ckbToneCurve->get_active (); pp->icm.blendCMSMatrix = ckbBlendCMSMatrix->get_active (); - pp->icm.gampos =(double) gampos->getValue(); - pp->icm.slpos =(double) slpos->getValue(); - + pp->icm.gampos =(double) gampos->getValue(); + pp->icm.slpos =(double) slpos->getValue(); + if (pedited) { pedited->icm.input = !iunchanged->get_active (); pedited->icm.working = wnames->get_active_text()!=M("GENERAL_UNCHANGED"); @@ -341,45 +349,41 @@ void ICMPanel::write (ProcParams* pp, ParamsEdited* pedited) { pedited->icm.toneCurve = !ckbToneCurve->get_inconsistent (); pedited->icm.blendCMSMatrix = !ckbBlendCMSMatrix->get_inconsistent (); pedited->icm.gamma = wgamma->get_active_text()!=M("GENERAL_UNCHANGED"); - pedited->icm.freegamma =!freegamma->get_inconsistent(); - pedited->icm.gampos = gampos->getEditedState (); - pedited->icm.slpos = slpos->getEditedState (); - + pedited->icm.freegamma =!freegamma->get_inconsistent(); + pedited->icm.gampos = gampos->getEditedState (); + pedited->icm.slpos = slpos->getEditedState (); } } -void ICMPanel::setDefaults (const ProcParams* defParams, const ParamsEdited* pedited) { - gampos->setDefault (defParams->icm.gampos); - slpos->setDefault (defParams->icm.slpos); - - if (pedited) { - gampos->setDefaultEditedState (pedited->icm.gampos ? Edited : UnEdited); - slpos->setDefaultEditedState (pedited->icm.slpos ? Edited : UnEdited); - } - else { +void ICMPanel::setDefaults (const ProcParams* defParams, const ParamsEdited* pedited) { + gampos->setDefault (defParams->icm.gampos); + slpos->setDefault (defParams->icm.slpos); + + if (pedited) { + gampos->setDefaultEditedState (pedited->icm.gampos ? Edited : UnEdited); + slpos->setDefaultEditedState (pedited->icm.slpos ? Edited : UnEdited); + } + else { gampos->setDefaultEditedState (Irrelevant); slpos->setDefaultEditedState (Irrelevant); - - } - } - - void ICMPanel::setAdjusterBehavior (bool gammaadd, bool slopeadd) { - gampos->setAddMode (gammaadd); - slpos->setAddMode (slopeadd); + } } - - + +void ICMPanel::setAdjusterBehavior (bool gammaadd, bool slopeadd) { + gampos->setAddMode (gammaadd); + slpos->setAddMode (slopeadd); +} + void ICMPanel::adjusterChanged (Adjuster* a, double newval) { if (listener && freegamma->get_active()) { Glib::ustring costr = Glib::ustring::format (std::setw(3), std::fixed, std::setprecision(2), newval); - if (a==gampos) + if (a==gampos) listener->panelChanged (EvGAMPOS, costr); - else if (a==slpos) + else if (a==slpos) listener->panelChanged (EvSLPOS, costr); - } } @@ -391,10 +395,10 @@ void ICMPanel::wpChanged () { void ICMPanel::gpChanged () { - if (listener) - {listener->panelChanged (EvGAMMA, wgamma->get_active_text ()); - onames->set_sensitive(wgamma->get_active_row_number()==0); //"default" - } + if (listener) { + listener->panelChanged (EvGAMMA, wgamma->get_active_text ()); + onames->set_sensitive(wgamma->get_active_row_number()==0); //"default" + } } void ICMPanel::prefProfChanged() { @@ -403,8 +407,21 @@ void ICMPanel::prefProfChanged() { } void ICMPanel::toneCurveChanged() { + if (batchMode) { + if (ckbToneCurve->get_inconsistent()) { + ckbToneCurve->set_inconsistent (false); + tcurveconn.block (true); + ckbToneCurve->set_active (false); + tcurveconn.block (false); + } + else if (lastToneCurve) + ckbToneCurve->set_inconsistent (true); + + lastToneCurve = ckbToneCurve->get_active (); + } + if (listener) - listener->panelChanged (EvDCPToneCurve, ""); + listener->panelChanged (EvDCPToneCurve, ckbToneCurve->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); } void ICMPanel::ipChanged () { @@ -430,15 +447,28 @@ void ICMPanel::ipChanged () { profname = ipDialog->get_filename (); ckbBlendCMSMatrix->set_sensitive(true); prefprof->set_sensitive (true); ckbToneCurve->set_sensitive (true); } - + if (listener && profname!=oldip) listener->panelChanged (EvIProfile, profname); oldip = profname; } -void ICMPanel::iccTogglesChanged() { - if (listener) listener->panelChanged (EvIProfile, ""); +void ICMPanel::blendCMSMatrixChanged() { + if (batchMode) { + if (ckbBlendCMSMatrix->get_inconsistent()) { + ckbBlendCMSMatrix->set_inconsistent (false); + blendcmsconn.block (true); + ckbBlendCMSMatrix->set_active (false); + blendcmsconn.block (false); + } + else if (lastBlendCMSMatrix) + ckbBlendCMSMatrix->set_inconsistent (true); + + lastBlendCMSMatrix = ckbBlendCMSMatrix->get_active (); + } + + if (listener) listener->panelChanged (EvBlendCMSMatrix, ckbBlendCMSMatrix->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); } void ICMPanel::GamChanged() { @@ -454,18 +484,18 @@ void ICMPanel::GamChanged() { lastgamfree = freegamma->get_active (); } - + if (listener) { if (freegamma->get_active()){ listener->panelChanged (EvGAMFREE, M("GENERAL_ENABLED")); - onames->set_sensitive(!freegamma->get_active());//disabled choice - wgamma->set_sensitive(!freegamma->get_active()); - } + onames->set_sensitive(!freegamma->get_active());//disabled choice + wgamma->set_sensitive(!freegamma->get_active()); + } else { listener->panelChanged (EvGAMFREE, M("GENERAL_DISABLED")); - onames->set_sensitive(!freegamma->get_active() && wgamma->get_active_row_number()==0); - wgamma->set_sensitive(!freegamma->get_active()); - } + onames->set_sensitive(!freegamma->get_active() && wgamma->get_active_row_number()==0); + wgamma->set_sensitive(!freegamma->get_active()); + } } } @@ -490,10 +520,10 @@ void ICMPanel::setRawMeta (bool raw, const rtengine::ImageData* pMeta) { void ICMPanel::ipSelectionChanged() { - if (ipDialog->get_filename() == "") - return; + if (ipDialog->get_filename() == "") + return; - ipChanged(); + ipChanged(); } void ICMPanel::saveReferencePressed () { @@ -550,11 +580,10 @@ void ICMPanel::setBatchMode (bool batchMode) { removeIfThere (this, saveRef); onames->append_text (M("GENERAL_UNCHANGED")); wnames->append_text (M("GENERAL_UNCHANGED")); - wgamma->append_text (M("GENERAL_UNCHANGED")); + wgamma->append_text (M("GENERAL_UNCHANGED")); prefprof->append_text (M("GENERAL_UNCHANGED")); - gampos->showEditedCB (); - slpos->showEditedCB (); - + gampos->showEditedCB (); + slpos->showEditedCB (); } diff --git a/rtgui/icmpanel.h b/rtgui/icmpanel.h index d4255a45d..4c3838dac 100644 --- a/rtgui/icmpanel.h +++ b/rtgui/icmpanel.h @@ -36,16 +36,21 @@ class ICMPanelListener { class ICMPanel : public Gtk::VBox, public AdjusterListener, public FoldableToolPanel { - protected: - Adjuster* gampos; - Adjuster* slpos; - bool lastgamfree; - sigc::connection gamcsconn; - //bool freegamma; + protected: + Adjuster* gampos; + Adjuster* slpos; + bool lastgamfree; + sigc::connection gamcsconn; + //bool freegamma; + bool lastToneCurve; + sigc::connection tcurveconn; + bool lastBlendCMSMatrix; + sigc::connection blendcmsconn; + private: Gtk::CheckButton* freegamma; - Gtk::RadioButton* inone; - + Gtk::RadioButton* inone; + Gtk::RadioButton* iembedded; Gtk::RadioButton* icamera; Gtk::RadioButton* icameraICC; @@ -55,7 +60,7 @@ class ICMPanel : public Gtk::VBox, public AdjusterListener, public FoldableToolP Gtk::CheckButton* ckbBlendCMSMatrix; MyComboBoxText* wnames; MyComboBoxText* wgamma; - + MyComboBoxText* onames; Gtk::RadioButton* ofromdir; Gtk::RadioButton* ofromfile; @@ -67,27 +72,27 @@ class ICMPanel : public Gtk::VBox, public AdjusterListener, public FoldableToolP sigc::connection ipc; Glib::ustring oldip; ICMPanelListener* icmplistener; - + bool enableLastICCWorkDirChange; Glib::ustring lastRefFilename; public: ICMPanel (); - + void read (const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited=NULL); void write (rtengine::procparams::ProcParams* pp, ParamsEdited* pedited=NULL); void setBatchMode (bool batchMode); - void setDefaults (const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited=NULL); + void setDefaults (const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited=NULL); void adjusterChanged (Adjuster* a, double newval); - void setAdjusterBehavior (bool gammaadd, bool slopeadd); + void setAdjusterBehavior (bool gammaadd, bool slopeadd); void wpChanged (); void opChanged (); void ipChanged (); void gpChanged (); - void GamChanged (); + void GamChanged (); void ipSelectionChanged (); - void iccTogglesChanged(); + void blendCMSMatrixChanged(); void prefProfChanged(); void toneCurveChanged(); diff --git a/rtgui/mydiagonalcurve.cc b/rtgui/mydiagonalcurve.cc index df7658b47..9456142e7 100644 --- a/rtgui/mydiagonalcurve.cc +++ b/rtgui/mydiagonalcurve.cc @@ -187,12 +187,12 @@ void MyDiagonalCurve::draw (int handle) { c = style->get_dark (state); cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p()); cr->set_antialias (Cairo::ANTIALIAS_NONE); - for (int i = 0; i < 5; i++) { + for (int i = 0; i <= 10; i++) { // horizontal lines - cr->move_to (double(graphX)+0.5 , double(graphY) - max(0.5, double(graphH*i/4) - 0.5)); + cr->move_to (double(graphX)+0.5 , double(graphY) - max(0.5, double(graphH*i/10) - 0.5)); cr->rel_line_to (double(graphW-1) , 0.); // vertical lines - cr->move_to (double(graphX) + max(0.5, double(graphW*i/4) - 0.5), double(graphY)); + cr->move_to (double(graphX) + max(0.5, double(graphW*i/10) - 0.5), double(graphY)); cr->rel_line_to (0. , double(-graphH+1)); } cr->stroke (); diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index b1c3a2662..288f32ddc 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -33,6 +33,7 @@ void ParamsEdited::set (bool v) { general.intrash = v; toneCurve.curve = v; + toneCurve.curveMode = v; toneCurve.brightness = v; toneCurve.black = v; toneCurve.contrast = v; @@ -231,6 +232,7 @@ void ParamsEdited::initFrom (const std::vector for (size_t i=1; ipack_start (*Gtk::manage (new Gtk::Label ( M("TP_EXPOSURE_TCMODE_LABEL") +": ")),Gtk::PACK_SHRINK, 4); + toneCurveMode = Gtk::manage (new MyComboBoxText ()); + toneCurveMode->append_text (M("TP_EXPOSURE_TCMODE_STANDARD")); + toneCurveMode->append_text (M("TP_EXPOSURE_TCMODE_FILMLIKE")); + toneCurveMode->append_text (M("TP_EXPOSURE_TCMODE_VALBLENDING")); + toneCurveMode->set_active (0); + hb1->pack_end (*toneCurveMode, Gtk::PACK_EXPAND_WIDGET, 4); + + pack_start( *hb1, Gtk::PACK_SHRINK, 2); + + tcmodeconn = toneCurveMode->signal_changed().connect( sigc::mem_fun(*this, &ToneCurve::curveModeChanged), true ); //curveEditorG->show(); // --------- Set Up Listeners ------------- @@ -119,23 +131,10 @@ void ToneCurve::read (const ProcParams* pp, const ParamsEdited* pedited) { disableListener (); - if (pedited) { - expcomp->setEditedState (pedited->toneCurve.expcomp ? Edited : UnEdited); - black->setEditedState (pedited->toneCurve.black ? Edited : UnEdited); - hlcompr->setEditedState (pedited->toneCurve.hlcompr ? Edited : UnEdited); - hlcomprthresh->setEditedState (pedited->toneCurve.hlcomprthresh ? Edited : UnEdited); - shcompr->setEditedState (pedited->toneCurve.shcompr ? Edited : UnEdited); - brightness->setEditedState (pedited->toneCurve.brightness ? Edited : UnEdited); - contrast->setEditedState (pedited->toneCurve.contrast ? Edited : UnEdited); - saturation->setEditedState (pedited->toneCurve.saturation ? Edited : UnEdited); - autolevels->set_inconsistent (!pedited->toneCurve.autoexp); - clipDirty = pedited->toneCurve.clip; - shape->setUnChanged (!pedited->toneCurve.curve); - } - + tcmodeconn.block(true); autoconn.block (true); + autolevels->set_active (pp->toneCurve.autoexp); - autoconn.block (false); lastAuto = pp->toneCurve.autoexp; sclip->set_value (pp->toneCurve.clip); @@ -150,6 +149,28 @@ void ToneCurve::read (const ProcParams* pp, const ParamsEdited* pedited) { saturation->setValue (pp->toneCurve.saturation); shape->setCurve (pp->toneCurve.curve); + toneCurveMode->set_active(pp->toneCurve.curveMode); + + if (pedited) { + expcomp->setEditedState (pedited->toneCurve.expcomp ? Edited : UnEdited); + black->setEditedState (pedited->toneCurve.black ? Edited : UnEdited); + hlcompr->setEditedState (pedited->toneCurve.hlcompr ? Edited : UnEdited); + hlcomprthresh->setEditedState (pedited->toneCurve.hlcomprthresh ? Edited : UnEdited); + shcompr->setEditedState (pedited->toneCurve.shcompr ? Edited : UnEdited); + brightness->setEditedState (pedited->toneCurve.brightness ? Edited : UnEdited); + contrast->setEditedState (pedited->toneCurve.contrast ? Edited : UnEdited); + saturation->setEditedState (pedited->toneCurve.saturation ? Edited : UnEdited); + autolevels->set_inconsistent (!pedited->toneCurve.autoexp); + clipDirty = pedited->toneCurve.clip; + shape->setUnChanged (!pedited->toneCurve.curve); + if (!pedited->toneCurve.curveMode) { + toneCurveMode->set_active(3); + } + } + + autoconn.block (false); + tcmodeconn.block(false); + enableListener (); } @@ -171,6 +192,12 @@ void ToneCurve::write (ProcParams* pp, ParamsEdited* pedited) { pp->toneCurve.saturation = (int)saturation->getValue (); pp->toneCurve.curve = shape->getCurve (); + int tcMode = toneCurveMode->get_active_row_number(); + if (tcMode == 0) pp->toneCurve.curveMode = ToneCurveParams::TC_MODE_STD; + if (tcMode == 1) pp->toneCurve.curveMode = ToneCurveParams::TC_MODE_FILMLIKE; + else if (tcMode == 2) pp->toneCurve.curveMode = ToneCurveParams::TC_MODE_VALBLENDING; + + if (pedited) { pedited->toneCurve.expcomp = expcomp->getEditedState (); pedited->toneCurve.black = black->getEditedState (); @@ -183,6 +210,7 @@ void ToneCurve::write (ProcParams* pp, ParamsEdited* pedited) { pedited->toneCurve.autoexp = !autolevels->get_inconsistent(); pedited->toneCurve.clip = clipDirty; pedited->toneCurve.curve = !shape->isUnChanged (); + pedited->toneCurve.curveMode = toneCurveMode->get_active_row_number() != 3; } } @@ -224,6 +252,16 @@ void ToneCurve::curveChanged () { if (listener) listener->panelChanged (EvToneCurve, M("HISTORY_CUSTOMCURVE")); } +void ToneCurve::curveModeChanged () { + //if (listener) listener->panelChanged (EvToneCurveMode, toneCurveMode->get_active_text()); + if (listener) Glib::signal_idle().connect (sigc::mem_fun(*this, &ToneCurve::curveModeChanged_)); +} + +bool ToneCurve::curveModeChanged_ () { + if (listener) listener->panelChanged (EvToneCurveMode, toneCurveMode->get_active_text()); + return false; +} + void ToneCurve::adjusterChanged (Adjuster* a, double newval) { // Switch off auto exposure if user changes sliders manually @@ -376,6 +414,7 @@ void ToneCurve::waitForAutoExp () { contrast->setEnabled (false); saturation->setEnabled (false); curveEditorG->set_sensitive (false); + toneCurveMode->set_sensitive (false); } int autoExpChangedUI (void* data) { @@ -408,6 +447,7 @@ void ToneCurve::enableAll () { contrast->setEnabled (true); saturation->setEnabled (true); curveEditorG->set_sensitive (true); + toneCurveMode->set_sensitive (true); } bool ToneCurve::autoExpComputed_ () { @@ -444,6 +484,8 @@ void ToneCurve::setBatchMode (bool batchMode) { contrast->showEditedCB (); saturation->showEditedCB (); + toneCurveMode->append_text (M("GENERAL_UNCHANGED")); + curveEditorG->setBatchMode (batchMode); } diff --git a/rtgui/tonecurve.h b/rtgui/tonecurve.h index f1e3cd0e4..3a35b68c9 100644 --- a/rtgui/tonecurve.h +++ b/rtgui/tonecurve.h @@ -42,9 +42,11 @@ class ToneCurve : public Gtk::VBox, public AdjusterListener, public FoldableTool Adjuster* shcompr; Adjuster* contrast; Adjuster* saturation; + MyComboBoxText* toneCurveMode; + bool clipDirty, lastAuto; - sigc::connection autoconn, neutralconn; + sigc::connection autoconn, neutralconn, tcmodeconn; CurveEditorGroup* curveEditorG; DiagonalCurveEditor* shape; @@ -79,6 +81,8 @@ class ToneCurve : public Gtk::VBox, public AdjusterListener, public FoldableTool bool autoExpComputed_ (); void enableAll (); void curveChanged (); + void curveModeChanged (); + bool curveModeChanged_ (); void expandCurve (bool isExpanded); bool isCurveExpanded (); void updateCurveBackgroundHistogram (LUTu & histToneCurve, LUTu & histLCurve, LUTu & histRed, LUTu & histGreen, LUTu & histBlue, LUTu & histLuma);