diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index c9a77fc5e..7e64f6a58 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -9,10 +9,15 @@ ADJUSTER_RESET_TO_DEFAULT;Réglages par défaut BATCHQUEUE_AUTOSTART;Démarrage auto BATCHQUEUE_DESTFILENAME;Chemin et nom de fichier BATCH_PROCESSING;Traitement par lot +CURVEEDITOR_AXIS_LEFT_TAN;TG: +CURVEEDITOR_AXIS_RIGHT_TAN;TD: +CURVEEDITOR_AXIS_IN;E: +CURVEEDITOR_AXIS_OUT;S: CURVEEDITOR_CURVES;Courbes CURVEEDITOR_CURVE;Courbe CURVEEDITOR_CUSTOM;Personnalisé CURVEEDITOR_DARKS;Zones sombres +CURVEEDITOR_EDITPOINT_HINT;Active l'édition des valeurs d'entrée/sortie.\n\nUn clic droit sur un point le sélectionne.\nUn clic droit dans un espace vide desélectionne le point. CURVEEDITOR_HIGHLIGHTS;Hautes lumières CURVEEDITOR_LIGHTS;Zones claires CURVEEDITOR_LINEAR;Linéaire @@ -1171,6 +1176,7 @@ TP_EXPOSURE_SATURATION;Saturation TP_EXPOSURE_TCMODE_FILMLIKE;Similaire Film TP_EXPOSURE_TCMODE_LABEL1;Mode courbe 1 TP_EXPOSURE_TCMODE_LABEL2;Mode courbe 2 +TP_EXPOSURE_TCMODE_LUMINANCE;Luminance TP_EXPOSURE_TCMODE_SATANDVALBLENDING;Mixage Saturation et Valeur TP_EXPOSURE_TCMODE_STANDARD;Standard TP_EXPOSURE_TCMODE_WEIGHTEDSTD;Standard Pondéré diff --git a/rtdata/languages/default b/rtdata/languages/default index a2f78b21c..3e2bff9e9 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -11,10 +11,15 @@ ADJUSTER_RESET_TO_DEFAULT;Reset to default BATCHQUEUE_AUTOSTART;Auto-start BATCHQUEUE_DESTFILENAME;Path and file name BATCH_PROCESSING;Batch Processing +CURVEEDITOR_AXIS_LEFT_TAN;LT: +CURVEEDITOR_AXIS_RIGHT_TAN;RT: +CURVEEDITOR_AXIS_IN;I: +CURVEEDITOR_AXIS_OUT;O: CURVEEDITOR_CURVES;Curves CURVEEDITOR_CURVE;Curve CURVEEDITOR_CUSTOM;Custom CURVEEDITOR_DARKS;Darks +CURVEEDITOR_EDITPOINT_HINT;Enable edition of node in/out values.\n\nRight-click on a node to select it.\nRight-click on empty space to de-select the node. CURVEEDITOR_HIGHLIGHTS;Highlights CURVEEDITOR_LIGHTS;Lights CURVEEDITOR_LINEAR;Linear @@ -1358,6 +1363,7 @@ TP_EXPOSURE_SATURATION;Saturation TP_EXPOSURE_TCMODE_FILMLIKE;Film-like TP_EXPOSURE_TCMODE_LABEL1;Curve mode 1 TP_EXPOSURE_TCMODE_LABEL2;Curve mode 2 +TP_EXPOSURE_TCMODE_LUMINANCE;Luminance TP_EXPOSURE_TCMODE_SATANDVALBLENDING;Saturation and Value Blending TP_EXPOSURE_TCMODE_STANDARD;Standard TP_EXPOSURE_TCMODE_WEIGHTEDSTD;Weighted Standard diff --git a/rtdata/themes/09-Gray-Orange.gtkrc b/rtdata/themes/09-Gray-Orange.gtkrc index 43f4f8f8a..080185d92 100644 --- a/rtdata/themes/09-Gray-Orange.gtkrc +++ b/rtdata/themes/09-Gray-Orange.gtkrc @@ -153,6 +153,13 @@ style "clearlooks-spinbutton" { } } +style "clearlooks-axisadjuster" = "clearlooks-spinbutton" { + # background + base[INSENSITIVE] = @rt_tool_bg + # text + text[INSENSITIVE] = @rt_fg_color +} + style "clearlooks-framelessspinbutton" { # IMPORTANT! @@ -535,6 +542,7 @@ widget "*.MyExpanderTitle" style "clearlooks-expander" widget "*.ExpanderBox" style "clearlooks-toolFrame" widget "*.ExpanderBox.*.MyExpanderTitle" style "clearlooks-subexpander" widget "*.ExpanderBox.*.MyExpanderTitle.*" style "clearlooks-subexpander" +widget "*.AxisAdjuster" style "clearlooks-axisadjuster" # The window of the tooltip is called "gtk-tooltip" ################################################################## diff --git a/rtdata/themes/17-Gray-Red.gtkrc b/rtdata/themes/17-Gray-Red.gtkrc index ce12862c0..03fd95b96 100644 --- a/rtdata/themes/17-Gray-Red.gtkrc +++ b/rtdata/themes/17-Gray-Red.gtkrc @@ -153,6 +153,13 @@ style "clearlooks-spinbutton" { } } +style "clearlooks-axisadjuster" = "clearlooks-spinbutton" { + # background + base[INSENSITIVE] = @rt_tool_bg + # text + text[INSENSITIVE] = @rt_fg_color +} + style "clearlooks-framelessspinbutton" { # IMPORTANT! @@ -535,6 +542,7 @@ widget "*.MyExpanderTitle" style "clearlooks-expander" widget "*.ExpanderBox" style "clearlooks-toolFrame" widget "*.ExpanderBox.*.MyExpanderTitle" style "clearlooks-subexpander" widget "*.ExpanderBox.*.MyExpanderTitle.*" style "clearlooks-subexpander" +widget "*.AxisAdjuster" style "clearlooks-axisadjuster" # The window of the tooltip is called "gtk-tooltip" ################################################################## diff --git a/rtdata/themes/21-Gray-Gray.gtkrc b/rtdata/themes/21-Gray-Gray.gtkrc index c1f236553..65bf68698 100644 --- a/rtdata/themes/21-Gray-Gray.gtkrc +++ b/rtdata/themes/21-Gray-Gray.gtkrc @@ -153,6 +153,13 @@ style "clearlooks-spinbutton" { } } +style "clearlooks-axisadjuster" = "clearlooks-spinbutton" { + # background + base[INSENSITIVE] = @rt_tool_bg + # text + text[INSENSITIVE] = @rt_fg_color +} + style "clearlooks-framelessspinbutton" { # IMPORTANT! @@ -535,6 +542,7 @@ widget "*.MyExpanderTitle" style "clearlooks-expander" widget "*.ExpanderBox" style "clearlooks-toolFrame" widget "*.ExpanderBox.*.MyExpanderTitle" style "clearlooks-subexpander" widget "*.ExpanderBox.*.MyExpanderTitle.*" style "clearlooks-subexpander" +widget "*.AxisAdjuster" style "clearlooks-axisadjuster" # The window of the tooltip is called "gtk-tooltip" ################################################################## diff --git a/rtdata/themes/21-Gray-Orange.gtkrc b/rtdata/themes/21-Gray-Orange.gtkrc index dcf4762a0..44eaaee34 100644 --- a/rtdata/themes/21-Gray-Orange.gtkrc +++ b/rtdata/themes/21-Gray-Orange.gtkrc @@ -153,6 +153,13 @@ style "clearlooks-spinbutton" { } } +style "clearlooks-axisadjuster" = "clearlooks-spinbutton" { + # background + base[INSENSITIVE] = @rt_tool_bg + # text + text[INSENSITIVE] = @rt_fg_color +} + style "clearlooks-framelessspinbutton" { # IMPORTANT! @@ -535,6 +542,7 @@ widget "*.MyExpanderTitle" style "clearlooks-expander" widget "*.ExpanderBox" style "clearlooks-toolFrame" widget "*.ExpanderBox.*.MyExpanderTitle" style "clearlooks-subexpander" widget "*.ExpanderBox.*.MyExpanderTitle.*" style "clearlooks-subexpander" +widget "*.AxisAdjuster" style "clearlooks-axisadjuster" # The window of the tooltip is called "gtk-tooltip" ################################################################## diff --git a/rtdata/themes/21-Gray-Purple.gtkrc b/rtdata/themes/21-Gray-Purple.gtkrc index 7b94adf2d..95f485640 100644 --- a/rtdata/themes/21-Gray-Purple.gtkrc +++ b/rtdata/themes/21-Gray-Purple.gtkrc @@ -153,6 +153,13 @@ style "clearlooks-spinbutton" { } } +style "clearlooks-axisadjuster" = "clearlooks-spinbutton" { + # background + base[INSENSITIVE] = @rt_tool_bg + # text + text[INSENSITIVE] = @rt_fg_color +} + style "clearlooks-framelessspinbutton" { # IMPORTANT! @@ -535,6 +542,7 @@ widget "*.MyExpanderTitle" style "clearlooks-expander" widget "*.ExpanderBox" style "clearlooks-toolFrame" widget "*.ExpanderBox.*.MyExpanderTitle" style "clearlooks-subexpander" widget "*.ExpanderBox.*.MyExpanderTitle.*" style "clearlooks-subexpander" +widget "*.AxisAdjuster" style "clearlooks-axisadjuster" # The window of the tooltip is called "gtk-tooltip" ################################################################## diff --git a/rtdata/themes/21-Gray-Red.gtkrc b/rtdata/themes/21-Gray-Red.gtkrc index 4c27916d3..ca5378f40 100644 --- a/rtdata/themes/21-Gray-Red.gtkrc +++ b/rtdata/themes/21-Gray-Red.gtkrc @@ -153,6 +153,13 @@ style "clearlooks-spinbutton" { } } +style "clearlooks-axisadjuster" = "clearlooks-spinbutton" { + # background + base[INSENSITIVE] = @rt_tool_bg + # text + text[INSENSITIVE] = @rt_fg_color +} + style "clearlooks-framelessspinbutton" { # IMPORTANT! @@ -535,6 +542,7 @@ widget "*.MyExpanderTitle" style "clearlooks-expander" widget "*.ExpanderBox" style "clearlooks-toolFrame" widget "*.ExpanderBox.*.MyExpanderTitle" style "clearlooks-subexpander" widget "*.ExpanderBox.*.MyExpanderTitle.*" style "clearlooks-subexpander" +widget "*.AxisAdjuster" style "clearlooks-axisadjuster" # The window of the tooltip is called "gtk-tooltip" ################################################################## diff --git a/rtdata/themes/25-Gray-Gray.gtkrc b/rtdata/themes/25-Gray-Gray.gtkrc index 6dd8e7df9..13372eb2a 100644 --- a/rtdata/themes/25-Gray-Gray.gtkrc +++ b/rtdata/themes/25-Gray-Gray.gtkrc @@ -153,6 +153,13 @@ style "clearlooks-spinbutton" { } } +style "clearlooks-axisadjuster" = "clearlooks-spinbutton" { + # background + base[INSENSITIVE] = @rt_tool_bg + # text + text[INSENSITIVE] = @rt_fg_color +} + style "clearlooks-framelessspinbutton" { # IMPORTANT! @@ -535,6 +542,7 @@ widget "*.MyExpanderTitle" style "clearlooks-expander" widget "*.ExpanderBox" style "clearlooks-toolFrame" widget "*.ExpanderBox.*.MyExpanderTitle" style "clearlooks-subexpander" widget "*.ExpanderBox.*.MyExpanderTitle.*" style "clearlooks-subexpander" +widget "*.AxisAdjuster" style "clearlooks-axisadjuster" # The window of the tooltip is called "gtk-tooltip" ################################################################## diff --git a/rtdata/themes/25-Gray-Purple.gtkrc b/rtdata/themes/25-Gray-Purple.gtkrc index d1a97c335..79ae82175 100644 --- a/rtdata/themes/25-Gray-Purple.gtkrc +++ b/rtdata/themes/25-Gray-Purple.gtkrc @@ -153,6 +153,13 @@ style "clearlooks-spinbutton" { } } +style "clearlooks-axisadjuster" = "clearlooks-spinbutton" { + # background + base[INSENSITIVE] = @rt_tool_bg + # text + text[INSENSITIVE] = @rt_fg_color +} + style "clearlooks-framelessspinbutton" { # IMPORTANT! @@ -535,6 +542,7 @@ widget "*.MyExpanderTitle" style "clearlooks-expander" widget "*.ExpanderBox" style "clearlooks-toolFrame" widget "*.ExpanderBox.*.MyExpanderTitle" style "clearlooks-subexpander" widget "*.ExpanderBox.*.MyExpanderTitle.*" style "clearlooks-subexpander" +widget "*.AxisAdjuster" style "clearlooks-axisadjuster" # The window of the tooltip is called "gtk-tooltip" ################################################################## diff --git a/rtdata/themes/25-Gray-Red.gtkrc b/rtdata/themes/25-Gray-Red.gtkrc index 649876ca5..fcb6d34ea 100644 --- a/rtdata/themes/25-Gray-Red.gtkrc +++ b/rtdata/themes/25-Gray-Red.gtkrc @@ -153,6 +153,13 @@ style "clearlooks-spinbutton" { } } +style "clearlooks-axisadjuster" = "clearlooks-spinbutton" { + # background + base[INSENSITIVE] = @rt_tool_bg + # text + text[INSENSITIVE] = @rt_fg_color +} + style "clearlooks-framelessspinbutton" { # IMPORTANT! @@ -535,6 +542,7 @@ widget "*.MyExpanderTitle" style "clearlooks-expander" widget "*.ExpanderBox" style "clearlooks-toolFrame" widget "*.ExpanderBox.*.MyExpanderTitle" style "clearlooks-subexpander" widget "*.ExpanderBox.*.MyExpanderTitle.*" style "clearlooks-subexpander" +widget "*.AxisAdjuster" style "clearlooks-axisadjuster" # The window of the tooltip is called "gtk-tooltip" ################################################################## diff --git a/rtdata/themes/37-Gray-Red-Textured.gtkrc b/rtdata/themes/37-Gray-Red-Textured.gtkrc index 0318f972a..34aa2bb78 100644 --- a/rtdata/themes/37-Gray-Red-Textured.gtkrc +++ b/rtdata/themes/37-Gray-Red-Textured.gtkrc @@ -153,6 +153,13 @@ style "clearlooks-spinbutton" { } } +style "clearlooks-axisadjuster" = "clearlooks-spinbutton" { + # background + base[INSENSITIVE] = @rt_tool_bg + # text + text[INSENSITIVE] = @rt_fg_color +} + style "clearlooks-framelessspinbutton" { # IMPORTANT! @@ -880,6 +887,7 @@ widget "*.MyExpanderTitle" style "clearlooks-expander" widget "*.ExpanderBox" style "clearlooks-toolFrame" widget "*.ExpanderBox.*.MyExpanderTitle" style "clearlooks-subexpander" widget "*.ExpanderBox.*.MyExpanderTitle.*" style "clearlooks-subexpander" +widget "*.AxisAdjuster" style "clearlooks-axisadjuster" # The window of the tooltip is called "gtk-tooltip" ################################################################## diff --git a/rtdata/themes/37-Gray-Red.gtkrc b/rtdata/themes/37-Gray-Red.gtkrc index 8830f0703..481c8623d 100644 --- a/rtdata/themes/37-Gray-Red.gtkrc +++ b/rtdata/themes/37-Gray-Red.gtkrc @@ -153,6 +153,13 @@ style "clearlooks-spinbutton" { } } +style "clearlooks-axisadjuster" = "clearlooks-spinbutton" { + # background + base[INSENSITIVE] = @rt_tool_bg + # text + text[INSENSITIVE] = @rt_fg_color +} + style "clearlooks-framelessspinbutton" { # IMPORTANT! @@ -537,6 +544,7 @@ widget "*.MyExpanderTitle" style "clearlooks-expander" widget "*.ExpanderBox" style "clearlooks-toolFrame" widget "*.ExpanderBox.*.MyExpanderTitle" style "clearlooks-subexpander" widget "*.ExpanderBox.*.MyExpanderTitle.*" style "clearlooks-subexpander" +widget "*.AxisAdjuster" style "clearlooks-axisadjuster" # The window of the tooltip is called "gtk-tooltip" ################################################################## diff --git a/rtdata/themes/63-Gray-Cyan.gtkrc b/rtdata/themes/63-Gray-Cyan.gtkrc index f7066607b..3c56771ef 100644 --- a/rtdata/themes/63-Gray-Cyan.gtkrc +++ b/rtdata/themes/63-Gray-Cyan.gtkrc @@ -153,6 +153,13 @@ style "clearlooks-spinbutton" { } } +style "clearlooks-axisadjuster" = "clearlooks-spinbutton" { + # background + base[INSENSITIVE] = @rt_tool_bg + # text + text[INSENSITIVE] = @rt_fg_color +} + style "clearlooks-framelessspinbutton" { # IMPORTANT! @@ -520,6 +527,7 @@ widget "*.MyExpanderTitle" style "clearlooks-expander" widget "*.ExpanderBox" style "clearlooks-toolFrame" widget "*.ExpanderBox.*.MyExpanderTitle" style "clearlooks-subexpander" widget "*.ExpanderBox.*.MyExpanderTitle.*" style "clearlooks-subexpander" +widget "*.AxisAdjuster" style "clearlooks-axisadjuster" # The window of the tooltip is called "gtk-tooltip" ################################################################## diff --git a/rtdata/themes/92-Beige-DarkCyan.gtkrc b/rtdata/themes/92-Beige-DarkCyan.gtkrc index 59108c6b0..f6489c4fc 100644 --- a/rtdata/themes/92-Beige-DarkCyan.gtkrc +++ b/rtdata/themes/92-Beige-DarkCyan.gtkrc @@ -153,6 +153,13 @@ style "clearlooks-spinbutton" { } } +style "clearlooks-axisadjuster" = "clearlooks-spinbutton" { + # background + base[INSENSITIVE] = @rt_tool_bg + # text + text[INSENSITIVE] = @rt_fg_color +} + style "clearlooks-framelessspinbutton" { # IMPORTANT! @@ -515,6 +522,7 @@ widget "*.MyExpanderTitle" style "clearlooks-expander" widget "*.ExpanderBox" style "clearlooks-toolFrame" widget "*.ExpanderBox.*.MyExpanderTitle" style "clearlooks-subexpander" widget "*.ExpanderBox.*.MyExpanderTitle.*" style "clearlooks-subexpander" +widget "*.AxisAdjuster" style "clearlooks-axisadjuster" # The window of the tooltip is called "gtk-tooltip" ################################################################## diff --git a/rtengine/LUT.h b/rtengine/LUT.h index a52deb047..464c588fb 100644 --- a/rtengine/LUT.h +++ b/rtengine/LUT.h @@ -84,7 +84,9 @@ protected: unsigned int maxs; float maxsf; T * data; - unsigned int clip, size; + unsigned int clip; + unsigned int size; + unsigned int upperBound; // always equals size-1, parameter created for performance reason private: unsigned int owner; #if defined( __SSE2__ ) && defined( __x86_64__ ) @@ -109,6 +111,7 @@ public: data = new T[s]; owner = 1; size = s; + upperBound = size-1; maxs=size-2; maxsf = (float)maxs; #if defined( __SSE2__ ) && defined( __x86_64__ ) @@ -131,6 +134,7 @@ public: data = new T[s]; owner = 1; size = s; + upperBound = size-1; maxs=size-2; maxsf = (float)maxs; #if defined( __SSE2__ ) && defined( __x86_64__ ) @@ -155,6 +159,7 @@ public: data = new T[s]; owner = 1; size = s; + upperBound = size-1; maxs=size-2; maxsf = (float)maxs; #if defined( __SSE2__ ) && defined( __x86_64__ ) @@ -199,7 +204,7 @@ public: * @return number of element in the array */ int getUpperBound() { - return size>0 ? size-1 : 0; + return size>0 ? upperBound : 0; } LUT & operator=(LUT &rhs) { @@ -214,6 +219,7 @@ public: this->owner=1; memcpy(this->data,rhs.data,rhs.size*sizeof(T)); this->size=rhs.size; + this->upperBound=rhs.upperBound; this->maxs=this->size-2; this->maxsf = (float)this->maxs; #if defined( __SSE2__ ) && defined( __x86_64__ ) @@ -228,7 +234,7 @@ public: } // use with integer indices T& operator[](int index) const { - return data[ rtengine::LIM(index, 0, size-1) ]; + return data[ rtengine::LIM(index, 0, upperBound) ]; } #if defined( __SSE2__ ) && defined( __x86_64__ ) @@ -350,7 +356,7 @@ public: else if (index > maxsf) { if (clip & LUT_CLIP_ABOVE) - return data[size - 1]; + return data[upperBound]; idx =maxs; } float diff = index - (float) idx; @@ -359,6 +365,27 @@ public: return (p1 + p2*diff); } + // Return the value for "index" that is in the [0-1] range. + T getVal01 (float index) const { + index *= float(upperBound); + int idx = (int)index; // don't use floor! The difference in negative space is no problems here + if (index<0.f) + { + if (clip & LUT_CLIP_BELOW) + return data[0]; + idx=0; + } + else if (index > maxsf) + { + if (clip & LUT_CLIP_ABOVE) + return data[upperBound]; + idx =maxs; + } + float diff = index - (float) idx; + T p1 = data[idx]; + T p2 = data[idx + 1]-p1; + return (p1 + p2*diff); + } #ifndef NDEBUG // Debug facility ; dump the content of the LUT in a file. No control of the filename is done @@ -393,10 +420,16 @@ public: data = NULL; owner = 1; size = 0; + upperBound=0; maxs=0; } }; + + +// TODO: HOMBRE: HueLUT is actually unused, could we delete this class now that LUT::getVal01 has been created? + + /** @brief LUT subclass handling hue values specifically. The array has a fixed size of float values and have to be in the [0.; 1.] range in both axis (no error checking implemented) */ class HueLUT : public LUTf { @@ -413,7 +446,7 @@ class HueLUT : public LUTf { // use with integer indices float& operator[](int index) const { - return data[ rtengine::LIM(index, 0, size-1) ]; + return data[ rtengine::LIM(index, 0, upperBound) ]; } // use with float indices in the [0.;1.] range @@ -422,7 +455,7 @@ class HueLUT : public LUTf { if (index<0.f) return data[0]; else if (index > 1.f) - return data[size - 1]; + return data[upperBound]; float balance = index - float(idx/500.f); float h1 = data[idx]; diff --git a/rtengine/color.h b/rtengine/color.h index 7e5d8688f..42b6e9cfc 100644 --- a/rtengine/color.h +++ b/rtengine/color.h @@ -154,8 +154,14 @@ public: * @return luminance value */ // xyz_sRGBD65 : conversion matrix from XYZ to sRGB for D65 illuminant: we use diagonal values - 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 float rgbLuminance(float r, float g, float b) { + // WArning: The sum of xyz_sRGBd65[1][] is > 1.0 (i.e. 1.0000001), so we use our own adapted values) + // 0.2126729, 0.7151521, 0.0721750 + return r*0.2126729f + g*0.7151521f + b*0.0721750f; + } + static double rgbLuminance(double r, double g, double b) { + return r*0.2126729 + g*0.7151521 + b*0.0721750; + } /** diff --git a/rtengine/curves.h b/rtengine/curves.h index 0a7a9e606..637224c7f 100644 --- a/rtengine/curves.h +++ b/rtengine/curves.h @@ -585,6 +585,11 @@ class WeightedStdToneCurve : public ToneCurve { void Apply(float& r, float& g, float& b) const; }; +class LuminanceToneCurve : public ToneCurve { + public: + void Apply(float& r, float& g, float& b) const; +}; + class WeightedStdToneCurvebw : public ToneCurve { private: float Triangle(float refX, float refY, float X2) const; @@ -670,6 +675,18 @@ inline void AdobeToneCurvebw::RGBTone (float& r, float& g, float& b) const { g = b + ((r - b) * (gold - bold) / (rold - bold)); } +// Modifying the Luminance channel only +inline void LuminanceToneCurve::Apply(float &r, float &g, float &b) const { + assert (lutToneCurve); + + float currLuminance = r*0.2126729f + g*0.7151521f + b*0.0721750f; + float newLuminance = lutToneCurve[currLuminance]; + float coef = newLuminance/currLuminance; + r = LIM(r*coef, 0.f, 65535.f); + g = LIM(g*coef, 0.f, 65535.f); + b = LIM(b*coef, 0.f, 65535.f); +} + inline float WeightedStdToneCurve::Triangle(float a, float a1, float b) const { if (a != b) { float b1; diff --git a/rtengine/iccmatrices.h b/rtengine/iccmatrices.h index eb4367b48..e5a91c4e0 100644 --- a/rtengine/iccmatrices.h +++ b/rtengine/iccmatrices.h @@ -30,7 +30,7 @@ const double d50_d65[3][3] = {{ 1.0478112, 0.0228866, -0.0501270}, // Color space conversion to/from XYZ; color spaces adapted to D65 const double xyz_sRGBd65[3][3] = {{0.4124564, 0.3575761, 0.1804375}, - {0.2126729, 0.7151522, 0.0721750}, + {0.2126729, 0.7151522, 0.0721750}, // WARNING: the summ of this line is > 1.0 {0.0193339, 0.1191920, 0.9503041}}; const double sRGBd65_xyz[3][3] = {{ 3.2404542, -1.5371385, -0.4985314}, diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index aa01ac203..865fb822b 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -2658,6 +2658,17 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, EditBuffer *e } } } + else if (curveMode==ToneCurveParams::TC_MODE_LUMINANCE){ // apply the curve to the luminance channel + const LuminanceToneCurve& userToneCurve = static_cast(customToneCurve1); + for (int i=istart,ti=0; i(rtemp[ti*TS+tj]); + gtemp[ti*TS+tj] = CLIP(gtemp[ti*TS+tj]); + btemp[ti*TS+tj] = CLIP(btemp[ti*TS+tj]); + userToneCurve.Apply(rtemp[ti*TS+tj], gtemp[ti*TS+tj], btemp[ti*TS+tj]); + } + } + } } if (editID == EUID_ToneCurve2) { // filling the pipette buffer @@ -2703,6 +2714,14 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, EditBuffer *e } } } + else if (curveMode2==ToneCurveParams::TC_MODE_LUMINANCE){ // apply the curve to the luminance channel + const LuminanceToneCurve& userToneCurve = static_cast(customToneCurve2); + for (int i=istart,ti=0; itoneCurve.curveMode = true; } if (keyFile.has_key ("Exposure", "CurveMode2")) { @@ -1916,7 +1923,7 @@ if (keyFile.has_group ("Exposure")) { if (sMode == "Standard") toneCurve.curveMode2 = ToneCurveParams::TC_MODE_STD; else if (sMode == "FilmLike") toneCurve.curveMode2 = ToneCurveParams::TC_MODE_FILMLIKE; else if (sMode == "SatAndValueBlending") toneCurve.curveMode2 = ToneCurveParams::TC_MODE_SATANDVALBLENDING; - else if (sMode == "WeightedStd") toneCurve.curveMode2 = ToneCurveParams::TC_MODE_WEIGHTEDSTD; + else if (sMode == "Luminance") toneCurve.curveMode2 = ToneCurveParams::TC_MODE_LUMINANCE; if (pedited) pedited->toneCurve.curveMode2 = true; } if (ppVersion>200) { diff --git a/rtengine/procparams.h b/rtengine/procparams.h index b8cc80931..6e0901e0a 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -193,7 +193,8 @@ class ToneCurveParams { TC_MODE_STD, // Standard modes, the curve is applied on all component individually TC_MODE_WEIGHTEDSTD, // Weighted standard mode TC_MODE_FILMLIKE, // Film-like mode, as defined in Adobe's reference code - TC_MODE_SATANDVALBLENDING // Modify the Saturation and Value channel + TC_MODE_SATANDVALBLENDING, // Modify the Saturation and Value channel + TC_MODE_LUMINANCE // Modify the Luminance channel with coefficients from Rec 709's }; bool autoexp; diff --git a/rtgui/CMakeLists.txt b/rtgui/CMakeLists.txt index 1880dd5b5..69c0d2872 100644 --- a/rtgui/CMakeLists.txt +++ b/rtgui/CMakeLists.txt @@ -4,7 +4,7 @@ set (BASESOURCEFILES exportpanel.cc cursormanager.cc rtwindow.cc renamedlg.cc recentbrowser.cc placesbrowser.cc filepanel.cc editorpanel.cc batchqueuepanel.cc ilabel.cc thumbbrowserbase.cc adjuster.cc filebrowserentry.cc filebrowser.cc filethumbnailbuttonset.cc cachemanager.cc cacheimagedata.cc shcselector.cc perspective.cc thresholdselector.cc thresholdadjuster.cc - clipboard.cc thumbimageupdater.cc bqentryupdater.cc lensgeom.cc coloredbar.cc edit.cc + clipboard.cc thumbimageupdater.cc bqentryupdater.cc lensgeom.cc coloredbar.cc edit.cc coordinateadjuster.cc coarsepanel.cc cacorrection.cc chmixer.cc blackwhite.cc resize.cc icmpanel.cc crop.cc shadowshighlights.cc impulsedenoise.cc dirpyrdenoise.cc epd.cc diff --git a/rtgui/coordinateadjuster.cc b/rtgui/coordinateadjuster.cc new file mode 100644 index 000000000..35bbe51e0 --- /dev/null +++ b/rtgui/coordinateadjuster.cc @@ -0,0 +1,198 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2004-2010 Gabor Horvath + * + * RawTherapee is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RawTherapee is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RawTherapee. If not, see . + */ + +#include "coordinateadjuster.h" +#include "multilangmgr.h" +#include +#include "curveeditorgroup.h" + +Axis::Axis() + : label(""), decimal(5), increment(0.001), pageIncrement(0.01), rangeLowerBound(0.), rangeUpperBound(1.) +{} + +Axis::Axis(Glib::ustring label, unsigned int decimal, double increment, double pageIncrement, double valMin=0.0, double valMax=1.0) + : label(label), decimal(decimal), increment(increment), pageIncrement(pageIncrement), rangeLowerBound(valMin), rangeUpperBound(valMax) +{} + +void Axis::setValues(Glib::ustring label, unsigned int decimal, double increment, double pageIncrement, double valMin, double valMax) { + this->label = label; + this->decimal = decimal; + this->increment = increment; + this->pageIncrement = pageIncrement; + this->rangeLowerBound = valMin; + this->rangeUpperBound = valMax; +} + +CoordinateAdjuster::AxisAdjuster::AxisAdjuster(CoordinateAdjuster *parent, const Axis *axis, char index) : idx(index), parent(parent) { + label = Gtk::manage( new Gtk::Label(axis->label) ); + spinButton = Gtk::manage( new Gtk::SpinButton() ); + + label = Gtk::manage (new Gtk::Label(axis->label)); + //label->set_alignment(Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER); + + spinButton = Gtk::manage (new Gtk::SpinButton()); + spinButton->set_name("AxisAdjuster"); + spinButton->set_digits(axis->decimal); + spinButton->set_increments(axis->increment, axis->pageIncrement); + spinButton->set_range(axis->rangeLowerBound, axis->rangeUpperBound); + spinButton->set_sensitive(false); + spinButtonConn = spinButton->signal_value_changed().connect( sigc::mem_fun(*this, &CoordinateAdjuster::AxisAdjuster::valueChanged) ); + //spinButton->signal_key_press_event().connect( sigc::mem_fun(*this, &CoordinateAdjuster::AxisAdjuster::keyPressed) ); +} + +void CoordinateAdjuster::AxisAdjuster::updateGUI(const Axis &axis) { + label->set_text(axis.label); + spinButton->set_digits(axis.decimal); + spinButton->set_increments(axis.increment, axis.pageIncrement); + spinButton->set_range(axis.rangeLowerBound, axis.rangeUpperBound); + spinButton->set_sensitive(false); + rangeLowerBound = axis.rangeLowerBound; + rangeUpperBound = axis.rangeUpperBound; +} + +void CoordinateAdjuster::AxisAdjuster::setValue(double newValue) { + float range = rangeUpperBound-rangeLowerBound; + spinButtonConn.block(true); + spinButton->set_value(newValue*range+rangeLowerBound); + spinButtonConn.block(false); +} + +void CoordinateAdjuster::AxisAdjuster::valueChanged() { + float range = rangeUpperBound-rangeLowerBound; + parent->updatePos(idx, (spinButton->get_value()-rangeLowerBound)/range); +} + +CoordinateAdjuster::CoordinateAdjuster(CoordinateProvider *provider, CurveEditorSubGroup *parent, const std::vector &axis) + : status(CA_STATUS_IDLE), parent(parent), coordinateProvider(provider) +{ + provider->setListener(this); + createWidgets(axis); +} + +CoordinateAdjuster::CoordinateAdjuster(CoordinateProvider *provider, CurveEditorSubGroup *parent) + : status(CA_STATUS_IDLE), parent(parent), coordinateProvider(provider) +{ + std::vector defaultAxis; + Axis X(M("CURVEEDITOR_AXIS_IN"), 3, 0.1, 1., 0., 100.); + Axis Y(M("CURVEEDITOR_AXIS_OUT"), 3, 0.1, 1., 0., 100.); + defaultAxis.push_back(X); + defaultAxis.push_back(Y); + + provider->setListener(this); + createWidgets(defaultAxis); +} + +void CoordinateAdjuster::createWidgets(const std::vector &axis) { + unsigned int count = axis.size(); + if (!count) { + printf("CoordinateAdjuster - Error: the Axis list is empty!\n"); + return; + } + assert (count <= 4); + + axisAdjusters.resize(axis.size()); + + set_spacing(3); + + AxisAdjuster *currAdjuster = NULL; + + for (unsigned int i=0; irangeLowerBound = currAxis->rangeLowerBound; + currAdjuster->rangeUpperBound = currAxis->rangeUpperBound; + + pack_start(*(currAdjuster->label), Gtk::PACK_SHRINK, 0); + pack_start(*(currAdjuster->spinButton), Gtk::PACK_SHRINK, 0); + } +} + +void CoordinateAdjuster::updatePos(char index, double value) { + coordinateProvider->setPos(value, index); +} + +void CoordinateAdjuster::setAxis(const std::vector &axis) { + assert (axis.size() == axisAdjusters.size()); + for (size_t i = 0; iupdateGUI(axis.at(i)); + } +} + +void CoordinateAdjuster::setPos(std::vector &pos) { + if (is_visible()) { + for (size_t i=0; isetValue(pos.at(i)); + } + } +} + +void CoordinateAdjuster::startNumericalAdjustment(const std::vector &newBoundaries) { + for (size_t i=0; ispinButton; + currSpinButton->set_sensitive(true); + float range = axisAdjusters.at(i)->rangeUpperBound-axisAdjusters.at(i)->rangeLowerBound; + currSpinButton->set_range(newBoundaries.at(i).minVal*range+axisAdjusters.at(i)->rangeLowerBound, newBoundaries.at(i).maxVal*range+axisAdjusters.at(i)->rangeUpperBound); + } + axisAdjusters.at(0)->spinButton->grab_focus(); + status = CA_STATUS_EDITING; +} + +void CoordinateAdjuster::switchAdjustedPoint(std::vector &pos, const std::vector &newBoundaries) { + if (status != CA_STATUS_EDITING) + return; + + for (size_t i=0; ispinButtonConn.block(true); + + // To avoid trimmed values, we have to... + + // ...enlarge range to the maximum + currAxis->spinButton->set_range(axisAdjusters.at(i)->rangeLowerBound, axisAdjusters.at(i)->rangeUpperBound); + + // ...set the new value + currAxis->setValue(pos.at(i)); + + // ...narrow the range to the new interval + float range = axisAdjusters.at(i)->rangeUpperBound-axisAdjusters.at(i)->rangeLowerBound; + currAxis->spinButton->set_range(newBoundaries.at(i).minVal*range+axisAdjusters.at(i)->rangeLowerBound, newBoundaries.at(i).maxVal*range+axisAdjusters.at(i)->rangeUpperBound); + + // enable events + currAxis->spinButtonConn.block(false); + } + axisAdjusters.at(0)->spinButton->grab_focus(); + status = CA_STATUS_EDITING; +} + +void CoordinateAdjuster::showMe(CoordinateProvider *provider) { + parent->showCoordinateAdjuster(provider); +} + +void CoordinateAdjuster::stopNumericalAdjustment() { + for (size_t i=0; ispinButtonConn.block(true); + axisAdjusters.at(i)->spinButton->set_sensitive(false); + axisAdjusters.at(i)->spinButton->set_range(axisAdjusters.at(i)->rangeLowerBound, axisAdjusters.at(i)->rangeUpperBound); + axisAdjusters.at(i)->spinButtonConn.block(false); + } + status = CA_STATUS_IDLE; +} diff --git a/rtgui/coordinateadjuster.h b/rtgui/coordinateadjuster.h new file mode 100644 index 000000000..3c4dbc38f --- /dev/null +++ b/rtgui/coordinateadjuster.h @@ -0,0 +1,157 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2004-2010 Gabor Horvath + * + * RawTherapee is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RawTherapee is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RawTherapee. If not, see . + */ +#ifndef _COORDINATEADJUSTER_ +#define _COORDINATEADJUSTER_ + +#include + +class CurveEditorSubGroup; + +class Axis { +public: + Glib::ustring label; + unsigned int decimal; + double increment; + double pageIncrement; + double rangeLowerBound; + double rangeUpperBound; + + Axis(); + Axis(Glib::ustring label, unsigned int decimal, double increment, double pageIncrement, double valMin, double valMax); + void setValues(Glib::ustring label, unsigned int decimal, double increment, double pageIncrement, double valMin, double valMax); +}; + +class CoordinateAdjuster; +/** + * @brief Object that will emit NewCoordinates events + */ +class CoordinateProvider { +protected: + CoordinateAdjuster *coordinateAdjuster; +public: + CoordinateProvider() : coordinateAdjuster(NULL) {} + virtual ~CoordinateProvider() {} + void setListener(CoordinateAdjuster *adjuster) { coordinateAdjuster = adjuster; } + + /** @brief Update the position of the edited point ; will trigger events + * + * @param pos New position + * @param chanIdx Chanel index as given in the std::vector upon instantiation + */ + virtual void setPos(double pos, int chanIdx)=0; + virtual void stopNumericalAdjustment()=0; +}; + +/** + * @brief Widget that displays spin buttons to adjust coordinates + * + * You can set up to 4 axis that will be displayed on a single line, so keep the labels short! + * + * The position of the Axis in the vector will be used in the communication between the Adjuster and the Provider to identify the Axis + */ +class CoordinateAdjuster : public Gtk::HBox { + +public: + //-------------------------------- AxisAdjuster ------------------- + class AxisAdjuster { + private: + char idx; + public: + CoordinateAdjuster *parent; + Gtk::Label *label; + Gtk::SpinButton *spinButton; + sigc::connection spinButtonConn; + float rangeLowerBound; + float rangeUpperBound; + + AxisAdjuster(CoordinateAdjuster *parent, const Axis *axis, char index); + + // used to update the AxisAdjuster's parameters + void updateGUI(const Axis &axis); + // useed to update the displayed value + void setValue(double newValue); + //bool keyPressed(GdkEventKey* event); + void valueChanged(); + }; + //---------------------------------------------------------------- + + //-------------------------------- Boundaries ------------------- + class Boundaries { + public: + double minVal; + double maxVal; + }; + //--------------------------------------------------------------- + +private: + typedef enum { + CA_STATUS_IDLE, + CA_STATUS_EDITING, + CA_STATUS_END_EDITING + } Status; + + std::vector axisAdjusters; + Status status; + CurveEditorSubGroup *parent; + + void createWidgets(const std::vector &axis); + +protected: + + friend class AxisAdjuster; + + CoordinateProvider *coordinateProvider; + + void updatePos(char index, double value); + + +public: + + /// Basic X/Y adjuster, in the [0-1] range + CoordinateAdjuster(CoordinateProvider *provider, CurveEditorSubGroup *parent); + /// For more complex adjuster + CoordinateAdjuster(CoordinateProvider *provider, CurveEditorSubGroup *parent, const std::vector &axis); + + virtual ~CoordinateAdjuster() {} + + // Update the Axis list, e.g. on Curve change, but MUST have the same axis count + void setAxis(const std::vector &axis); + + /** @brief Update the numbers in the spin buttons ; doesn't trigger any event + * + * @param pos Vector that gives the values of each channels + */ + void setPos(std::vector &pos); + + /// Start the adjustment session (enable the widget) + void startNumericalAdjustment(const std::vector &newBoundaries); + + /// Edit another point + void switchAdjustedPoint(std::vector &pos, const std::vector &newBoundaries); + + /// Trigger the event to show the CoordinateAdjuster + void showMe(CoordinateProvider *provider); + + /// Stop the adjustment session (disable the widget, i.e. you won't be able to edit the values) + void stopNumericalAdjustment(); + +}; + + +#endif diff --git a/rtgui/curveeditorgroup.cc b/rtgui/curveeditorgroup.cc index f3878e22e..ae3c7d141 100644 --- a/rtgui/curveeditorgroup.cc +++ b/rtgui/curveeditorgroup.cc @@ -46,6 +46,10 @@ CurveEditorGroup::~CurveEditorGroup() { void CurveEditorGroup::hideCurrentCurve() { // Setting the curve type to 'Unchanged' hide the CurveEditor + if (diagonalSubGroup) + diagonalSubGroup->stopNumericalAdjustment(); + if (flatSubGroup) + flatSubGroup->stopNumericalAdjustment(); if (displayedCurve) displayedCurve->curveType->set_active(false); } @@ -280,10 +284,10 @@ void CurveEditorGroup::curveChanged () { /* * Listener called when the user has modified the curve */ -float CurveEditorGroup::blendPipetteValues (float chan1, float chan2, float chan3) { +float CurveEditorGroup::blendPipetteValues (CurveEditor* ce, float chan1, float chan2, float chan3) { if (cl) - return cl->blendPipetteValues(chan1, chan2, chan3); + return cl->blendPipetteValues(ce, chan1, chan2, chan3); return -1.f; } diff --git a/rtgui/curveeditorgroup.h b/rtgui/curveeditorgroup.h index 2650f5770..8367d3b32 100644 --- a/rtgui/curveeditorgroup.h +++ b/rtgui/curveeditorgroup.h @@ -89,10 +89,12 @@ protected: void updateGUI (CurveEditor* ce); void curveResetPressed (); void curveChanged (); - float blendPipetteValues(float chan1, float chan2, float chan3); + float blendPipetteValues(CurveEditor* ce, float chan1, float chan2, float chan3); void setUnChanged (bool uc, CurveEditor* ce); }; +class CoordinateProvider; + class CurveEditorSubGroup { friend class CurveEditorGroup; @@ -105,6 +107,7 @@ protected: int valLinear; int valUnchanged; CurveEditorGroup *parent; + int curveBBoxPos; // 0=above, 1=right, 2=below, 3=left ColoredBar* leftBar; ColoredBar* bottomBar; @@ -120,6 +123,9 @@ public: virtual void refresh(CurveEditor *curveToRefresh) = 0; virtual void editModeSwitchedOff() = 0; + virtual void showCoordinateAdjuster(CoordinateProvider *provider) = 0; + virtual void stopNumericalAdjustment() = 0; + virtual void pipetteMouseOver(EditDataProvider *provider, int modifierKey) =0; virtual void pipetteButton1Pressed(EditDataProvider *provider, int modifierKey) =0; virtual void pipetteButton1Released(EditDataProvider *provider) =0; diff --git a/rtgui/curvelistener.h b/rtgui/curvelistener.h index e9c265821..682c98b29 100644 --- a/rtgui/curvelistener.h +++ b/rtgui/curvelistener.h @@ -19,6 +19,8 @@ #ifndef _CURVELISTENER_ #define _CURVELISTENER_ +#include + class CurveEditor; class CurveListener { @@ -37,7 +39,7 @@ class CurveListener { * @param ce CurveEditor that we want to reset * @param curve Actual curve for the return value. The actual curve type (given by the first value of the vector) * should be kept the same. Change the curve type if REALLY necessary! */ - virtual bool getResetCurve(CurveEditor *ce, std::vector &curve) { return false; }; + virtual bool getResetCurve(CurveEditor *ce, std::vector &curve) { return false; } /** @brief Blend pipette values from its different channels into a single value If the buffer has more than one channel and one channel, this method will blend them together. @@ -45,7 +47,7 @@ class CurveListener { @param chan2 second channel's value @param chan3 third channel's value @return the blended value */ - virtual float blendPipetteValues(float chan1, float chan2, float chan3) { + virtual float blendPipetteValues(CurveEditor *ce, float chan1, float chan2, float chan3) { float retVal = 0.f; int n = 0; if (chan1 != -1.f) { diff --git a/rtgui/diagonalcurveeditorsubgroup.cc b/rtgui/diagonalcurveeditorsubgroup.cc index f4a63cd57..2091e5a9d 100644 --- a/rtgui/diagonalcurveeditorsubgroup.cc +++ b/rtgui/diagonalcurveeditorsubgroup.cc @@ -27,6 +27,7 @@ #include "shcselector.h" #include "adjuster.h" #include "mycurve.h" +#include "mydiagonalcurve.h" #include "curveeditor.h" #include "diagonalcurveeditorsubgroup.h" @@ -35,6 +36,8 @@ DiagonalCurveEditorSubGroup::DiagonalCurveEditorSubGroup (CurveEditorGroup* prt, editedAdjuster = NULL; editedAdjusterValue = 0; + curveBBoxPos = options.curvebboxpos; + valLinear = (int)DCT_Linear; valUnchanged = (int)DCT_Unchanged; parent = prt; @@ -66,6 +69,9 @@ DiagonalCurveEditorSubGroup::DiagonalCurveEditorSubGroup (CurveEditorGroup* prt, saveCustom->add (*Gtk::manage (new RTImage ("gtk-save-large.png"))); loadCustom = Gtk::manage (new Gtk::Button ()); loadCustom->add (*Gtk::manage (new RTImage ("gtk-open.png"))); + editPointCustom = Gtk::manage (new Gtk::ToggleButton ()); + editPointCustom->add (*Gtk::manage (new RTImage ("gtk-edit.png"))); + editPointCustom->set_tooltip_text(M("CURVEEDITOR_EDITPOINT_HINT")); editCustom = Gtk::manage (new Gtk::ToggleButton()); editCustom->add (*Gtk::manage (new RTImage ("editmodehand.png"))); editCustom->set_tooltip_text(M("EDIT_PIPETTE_TOOLTIP")); @@ -75,6 +81,7 @@ DiagonalCurveEditorSubGroup::DiagonalCurveEditorSubGroup (CurveEditorGroup* prt, custombbox->pack_end (*copyCustom, Gtk::PACK_SHRINK, 0); custombbox->pack_end (*saveCustom, Gtk::PACK_SHRINK, 0); custombbox->pack_end (*loadCustom, Gtk::PACK_SHRINK, 0); + custombbox->pack_start(*editPointCustom, Gtk::PACK_SHRINK, 0); custombbox->pack_start(*editCustom, Gtk::PACK_SHRINK, 0); customCurveAndButtons->pack_start (*customCurve, Gtk::PACK_EXPAND_WIDGET, 0); @@ -90,12 +97,21 @@ DiagonalCurveEditorSubGroup::DiagonalCurveEditorSubGroup (CurveEditorGroup* prt, } else if (options.curvebboxpos==3) { customCurveAndButtons->reorder_child(*custombbox, 0); } + + customCoordAdjuster = Gtk::manage (new CoordinateAdjuster(customCurve, this)); + customCurveBox->pack_start(*customCoordAdjuster, Gtk::PACK_SHRINK, 0); + if (options.curvebboxpos == 2) + customCurveBox->reorder_child(*customCoordAdjuster, 2); + customCoordAdjuster->show_all(); + customCurveBox->show_all (); + customCoordAdjuster->hide(); saveCustom->signal_clicked().connect( sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::savePressed) ); loadCustom->signal_clicked().connect( sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::loadPressed) ); copyCustom->signal_clicked().connect( sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::copyPressed) ); pasteCustom->signal_clicked().connect( sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::pastePressed) ); + editPointCustomConn = editPointCustom->signal_toggled().connect( sigc::bind(sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::editPointToggled), editPointCustom) ); editCustomConn = editCustom->signal_toggled().connect( sigc::bind(sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::editToggled), editCustom) ); saveCustom->set_tooltip_text (M("CURVEEDITOR_TOOLTIPSAVE")); @@ -130,6 +146,9 @@ DiagonalCurveEditorSubGroup::DiagonalCurveEditorSubGroup (CurveEditorGroup* prt, saveNURBS->add (*Gtk::manage (new RTImage ("gtk-save-large.png"))); loadNURBS = Gtk::manage (new Gtk::Button ()); loadNURBS->add (*Gtk::manage (new RTImage ("gtk-open.png"))); + editPointNURBS = Gtk::manage (new Gtk::ToggleButton ()); + editPointNURBS->add (*Gtk::manage (new RTImage ("gtk-edit.png"))); + editPointNURBS->set_tooltip_text(M("CURVEEDITOR_EDITPOINT_HINT")); editNURBS = Gtk::manage (new Gtk::ToggleButton()); editNURBS->add (*Gtk::manage (new RTImage ("editmodehand.png"))); editNURBS->set_tooltip_text(M("EDIT_PIPETTE_TOOLTIP")); @@ -138,6 +157,7 @@ DiagonalCurveEditorSubGroup::DiagonalCurveEditorSubGroup (CurveEditorGroup* prt, NURBSbbox->pack_end (*copyNURBS, Gtk::PACK_SHRINK, 0); NURBSbbox->pack_end (*saveNURBS, Gtk::PACK_SHRINK, 0); NURBSbbox->pack_end (*loadNURBS, Gtk::PACK_SHRINK, 0); + NURBSbbox->pack_start(*editPointNURBS, Gtk::PACK_SHRINK, 0); NURBSbbox->pack_start(*editNURBS, Gtk::PACK_SHRINK, 0); NURBSCurveAndButtons->pack_start (*NURBSCurve, Gtk::PACK_EXPAND_WIDGET, 0); @@ -153,12 +173,21 @@ DiagonalCurveEditorSubGroup::DiagonalCurveEditorSubGroup (CurveEditorGroup* prt, } else if (options.curvebboxpos==3) { NURBSCurveAndButtons->reorder_child(*NURBSbbox, 0); } + + NURBSCoordAdjuster = Gtk::manage (new CoordinateAdjuster(NURBSCurve, this)); + NURBSCurveBox->pack_start(*NURBSCoordAdjuster, Gtk::PACK_SHRINK, 0); + if (options.curvebboxpos == 2) + NURBSCurveBox->reorder_child(*NURBSCoordAdjuster, 2); + NURBSCoordAdjuster->show_all(); + NURBSCurveBox->show_all (); + NURBSCoordAdjuster->hide(); saveNURBS->signal_clicked().connect( sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::savePressed) ); loadNURBS->signal_clicked().connect( sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::loadPressed) ); pasteNURBS->signal_clicked().connect( sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::pastePressed) ); copyNURBS->signal_clicked().connect( sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::copyPressed) ); + editPointNURBSConn = editPointNURBS->signal_toggled().connect( sigc::bind(sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::editPointToggled), editPointNURBS) ); editNURBSConn = editNURBS->signal_toggled().connect( sigc::bind(sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::editToggled), editNURBS) ); saveNURBS->set_tooltip_text (M("CURVEEDITOR_TOOLTIPSAVE")); @@ -320,17 +349,17 @@ void DiagonalCurveEditorSubGroup::editModeSwitchedOff () { bool prevState; prevState = editCustomConn.block(true); editCustom->set_active(false); - customCurve->pipetteMouseOver(NULL, 0); + customCurve->pipetteMouseOver(NULL, NULL, 0); customCurve->setDirty(true); if (!prevState) editCustomConn.block(false); prevState = editNURBSConn.block(true); editNURBS->set_active(false); - NURBSCurve->pipetteMouseOver(NULL, 0); + NURBSCurve->pipetteMouseOver(NULL, NULL, 0); NURBSCurve->setDirty(true); if (!prevState) editNURBSConn.block(false); prevState = editParamConn.block(true); editParam->set_active(false); - paramCurve->pipetteMouseOver(NULL, 0); + paramCurve->pipetteMouseOver(NULL, NULL, 0); paramCurve->setDirty(true); if (!prevState) editParamConn.block(false); } @@ -339,12 +368,12 @@ void DiagonalCurveEditorSubGroup::pipetteMouseOver(EditDataProvider *provider, i CurveEditor *curveEditor = static_cast(parent->displayedCurve); switch((DiagonalCurveType)(curveEditor->curveType->getSelected())) { case (DCT_Spline): - customCurve->pipetteMouseOver(provider, modifierKey); + customCurve->pipetteMouseOver(curveEditor, provider, modifierKey); customCurve->setDirty(true); break; case (DCT_Parametric): { - paramCurve->pipetteMouseOver(provider, modifierKey); + paramCurve->pipetteMouseOver(curveEditor, provider, modifierKey); paramCurve->setDirty(true); float pipetteVal = 0.f; editedAdjuster = NULL; @@ -392,7 +421,7 @@ void DiagonalCurveEditorSubGroup::pipetteMouseOver(EditDataProvider *provider, i } break; case (DCT_NURBS): - NURBSCurve->pipetteMouseOver(provider, modifierKey); + NURBSCurve->pipetteMouseOver(curveEditor, provider, modifierKey); NURBSCurve->setDirty(true); break; default: // (DCT_Linear, DCT_Unchanged) @@ -462,6 +491,20 @@ void DiagonalCurveEditorSubGroup::pipetteDrag(EditDataProvider *provider, int mo } } +void DiagonalCurveEditorSubGroup::showCoordinateAdjuster(CoordinateProvider *provider) { + if (provider == customCurve) { + if (!editPointCustom->get_active()) editPointCustom->set_active(true); + } + else if (provider == NURBSCurve) { + if (!editPointNURBS->get_active()) editPointNURBS->set_active(true); + } +} + +void DiagonalCurveEditorSubGroup::stopNumericalAdjustment() { + customCurve->stopNumericalAdjustment(); + NURBSCurve->stopNumericalAdjustment(); +} + /* * Force the resize of the curve editor, if the displayed one is the requested one */ @@ -773,6 +816,31 @@ void DiagonalCurveEditorSubGroup::pastePressed () { return; } +void DiagonalCurveEditorSubGroup::editPointToggled(Gtk::ToggleButton *button) { + if (button->get_active()) { + customCoordAdjuster->show(); + NURBSCoordAdjuster->show(); + } + else { + if (customCoordAdjuster) { + customCurve->stopNumericalAdjustment(); + customCoordAdjuster->hide(); + NURBSCurve->stopNumericalAdjustment(); + NURBSCoordAdjuster->hide(); + } + } + if (button == editPointCustom) { + editPointNURBSConn.block(true); + editPointNURBS->set_active(!editPointNURBS->get_active()); + editPointNURBSConn.block(false); + } + else { + editPointCustomConn.block(true); + editPointCustom->set_active(!editPointCustom->get_active()); + editPointCustomConn.block(false); + } +} + void DiagonalCurveEditorSubGroup::editToggled (Gtk::ToggleButton *button) { DiagonalCurveEditor* dCurve = static_cast(parent->displayedCurve); if (!dCurve) diff --git a/rtgui/diagonalcurveeditorsubgroup.h b/rtgui/diagonalcurveeditorsubgroup.h index a0361485c..e10daf3ed 100644 --- a/rtgui/diagonalcurveeditorsubgroup.h +++ b/rtgui/diagonalcurveeditorsubgroup.h @@ -46,18 +46,23 @@ protected: Adjuster *editedAdjuster; int editedAdjusterValue; + CoordinateAdjuster *customCoordAdjuster; + CoordinateAdjuster *NURBSCoordAdjuster; + Gtk::Button* saveCustom; Gtk::Button* loadCustom; Gtk::Button* copyCustom; Gtk::Button* pasteCustom; + Gtk::ToggleButton* editPointCustom; Gtk::ToggleButton* editCustom; - sigc::connection editCustomConn; + sigc::connection editCustomConn, editPointCustomConn; Gtk::Button* saveNURBS; Gtk::Button* loadNURBS; Gtk::Button* copyNURBS; Gtk::Button* pasteNURBS; + Gtk::ToggleButton* editPointNURBS; Gtk::ToggleButton* editNURBS; - sigc::connection editNURBSConn; + sigc::connection editNURBSConn, editPointNURBSConn; Gtk::Button* saveParam; Gtk::Button* loadParam; Gtk::Button* copyParam; @@ -80,6 +85,8 @@ public: void pipetteButton1Pressed(EditDataProvider *provider, int modifierKey); void pipetteButton1Released(EditDataProvider *provider); void pipetteDrag(EditDataProvider *provider, int modifierKey); + void showCoordinateAdjuster(CoordinateProvider *provider); + void stopNumericalAdjustment(); bool curveReset (CurveEditor *ce); @@ -91,6 +98,7 @@ protected: void loadPressed (); void copyPressed (); void pastePressed (); + void editPointToggled(Gtk::ToggleButton *button); void editToggled (Gtk::ToggleButton *button); void removeEditor (); const std::vector getCurveFromGUI (int type); diff --git a/rtgui/flatcurveeditorsubgroup.cc b/rtgui/flatcurveeditorsubgroup.cc index 1cca98fc0..50ec1d1f3 100644 --- a/rtgui/flatcurveeditorsubgroup.cc +++ b/rtgui/flatcurveeditorsubgroup.cc @@ -28,6 +28,7 @@ #include "shcselector.h" #include "adjuster.h" #include "mycurve.h" +#include "myflatcurve.h" #include "curveeditor.h" #include "flatcurveeditorsubgroup.h" @@ -37,6 +38,8 @@ FlatCurveEditorSubGroup::FlatCurveEditorSubGroup (CurveEditorGroup* prt, Glib::u valUnchanged = (int)FCT_Unchanged; parent = prt; + curveBBoxPos = options.curvebboxpos; + // ControlPoints curve CPointsCurveBox = new Gtk::VBox (); CPointsCurveBox->set_spacing(4); @@ -63,6 +66,9 @@ FlatCurveEditorSubGroup::FlatCurveEditorSubGroup (CurveEditorGroup* prt, Glib::u loadCPoints = Gtk::manage (new Gtk::Button ()); loadCPoints->add (*Gtk::manage (new RTImage ("gtk-open.png"))); editCPoints = Gtk::manage (new Gtk::ToggleButton()); + editPointCPoints = Gtk::manage (new Gtk::ToggleButton ()); + editPointCPoints->add (*Gtk::manage (new RTImage ("gtk-edit.png"))); + editPointCPoints->set_tooltip_text(M("CURVEEDITOR_EDITPOINT_HINT")); editCPoints->add (*Gtk::manage (new RTImage ("editmodehand.png"))); editCPoints->set_tooltip_text(M("EDIT_PIPETTE_TOOLTIP")); editCPoints->hide(); @@ -71,6 +77,7 @@ FlatCurveEditorSubGroup::FlatCurveEditorSubGroup (CurveEditorGroup* prt, Glib::u CPointsbbox->pack_end (*copyCPoints, Gtk::PACK_SHRINK, 0); CPointsbbox->pack_end (*saveCPoints, Gtk::PACK_SHRINK, 0); CPointsbbox->pack_end (*loadCPoints, Gtk::PACK_SHRINK, 0); + CPointsbbox->pack_start(*editPointCPoints, Gtk::PACK_SHRINK, 0); CPointsbbox->pack_start(*editCPoints, Gtk::PACK_SHRINK, 0); CPointsCurveAndButtons->pack_start (*CPointsCurve, Gtk::PACK_EXPAND_WIDGET, 0); @@ -86,12 +93,29 @@ FlatCurveEditorSubGroup::FlatCurveEditorSubGroup (CurveEditorGroup* prt, Glib::u } else if (options.curvebboxpos==3) { CPointsCurveAndButtons->reorder_child(*CPointsbbox, 0); } + + { + std::vector axis; + axis.resize(4); + axis.at(0).setValues(M("CURVEEDITOR_AXIS_IN"), 5, 0.001, 0.01, 0., 1.); + axis.at(1).setValues(M("CURVEEDITOR_AXIS_OUT"), 5, 0.001, 0.01, 0., 1.); + axis.at(2).setValues(M("CURVEEDITOR_AXIS_LEFT_TAN"), 5, 0.01, 0.1, 0., 1.); + axis.at(3).setValues(M("CURVEEDITOR_AXIS_RIGHT_TAN"), 5, 0.01, 0.1, 0., 1.); + CPointsCoordAdjuster = Gtk::manage (new CoordinateAdjuster(CPointsCurve, this, axis)); + CPointsCurveBox->pack_start(*CPointsCoordAdjuster, Gtk::PACK_SHRINK, 0); + if (options.curvebboxpos == 2) + CPointsCurveBox->reorder_child(*CPointsCoordAdjuster, 2); + CPointsCoordAdjuster->show_all(); + } + CPointsCurveBox->show_all (); + CPointsCoordAdjuster->hide(); saveCPoints->signal_clicked().connect( sigc::mem_fun(*this, &FlatCurveEditorSubGroup::savePressed) ); loadCPoints->signal_clicked().connect( sigc::mem_fun(*this, &FlatCurveEditorSubGroup::loadPressed) ); copyCPoints->signal_clicked().connect( sigc::mem_fun(*this, &FlatCurveEditorSubGroup::copyPressed) ); pasteCPoints->signal_clicked().connect( sigc::mem_fun(*this, &FlatCurveEditorSubGroup::pastePressed) ); + editPointCPointsConn = editPointCPoints->signal_toggled().connect( sigc::bind(sigc::mem_fun(*this, &FlatCurveEditorSubGroup::editPointToggled), editPointCPoints) ); editCPointsConn = editCPoints->signal_toggled().connect( sigc::bind(sigc::mem_fun(*this, &FlatCurveEditorSubGroup::editToggled), editCPoints) ); saveCPoints->set_tooltip_text (M("CURVEEDITOR_TOOLTIPSAVE")); @@ -117,6 +141,17 @@ FlatCurveEditor* FlatCurveEditorSubGroup::addCurve(Glib::ustring curveLabel, boo return newCE; } +void FlatCurveEditorSubGroup::showCoordinateAdjuster(CoordinateProvider *provider) { + if (provider == CPointsCurve) { + if (!editPointCPoints->get_active()) editPointCPoints->set_active(true); + } +} + +void FlatCurveEditorSubGroup::stopNumericalAdjustment() { + CPointsCurve->stopNumericalAdjustment(); +} + + /* * Force the resize of the curve editor, if the displayed one is the requested one */ @@ -140,7 +175,7 @@ void FlatCurveEditorSubGroup::editModeSwitchedOff () { // toggling off all edit buttons, even if only one is toggle on bool prevState = editCPointsConn.block(true); editCPoints->set_active(false); - CPointsCurve->pipetteMouseOver(NULL, 0); + CPointsCurve->pipetteMouseOver(NULL, NULL, 0); CPointsCurve->setDirty(true); if (!prevState) editCPointsConn.block(false); } @@ -149,7 +184,7 @@ void FlatCurveEditorSubGroup::pipetteMouseOver(EditDataProvider *provider, int m CurveEditor *curveEditor = static_cast(parent->displayedCurve); switch((FlatCurveType)(curveEditor->curveType->getSelected())) { case (FCT_MinMaxCPoints): - CPointsCurve->pipetteMouseOver(provider, modifierKey); + CPointsCurve->pipetteMouseOver(curveEditor, provider, modifierKey); CPointsCurve->setDirty(true); break; default: // (DCT_Linear, DCT_Unchanged) @@ -380,6 +415,15 @@ void FlatCurveEditorSubGroup::pastePressed () { return; } +void FlatCurveEditorSubGroup::editPointToggled(Gtk::ToggleButton *button) { + if (button->get_active()) + CPointsCoordAdjuster->show(); + else { + CPointsCurve->stopNumericalAdjustment(); + CPointsCoordAdjuster->hide(); + } +} + void FlatCurveEditorSubGroup::editToggled (Gtk::ToggleButton *button) { FlatCurveEditor* dCurve = static_cast(parent->displayedCurve); if (!dCurve) diff --git a/rtgui/flatcurveeditorsubgroup.h b/rtgui/flatcurveeditorsubgroup.h index 4903aa309..e937dd362 100644 --- a/rtgui/flatcurveeditorsubgroup.h +++ b/rtgui/flatcurveeditorsubgroup.h @@ -33,12 +33,15 @@ protected: MyFlatCurve* CPointsCurve; + CoordinateAdjuster *CPointsCoordAdjuster; + Gtk::Button* saveCPoints; Gtk::Button* loadCPoints; Gtk::Button* copyCPoints; Gtk::Button* pasteCPoints; + Gtk::ToggleButton* editPointCPoints; Gtk::ToggleButton* editCPoints; - sigc::connection editCPointsConn; + sigc::connection editCPointsConn, editPointCPointsConn; public: FlatCurveEditorSubGroup(CurveEditorGroup* prt, Glib::ustring& curveDir); @@ -53,6 +56,8 @@ public: void pipetteButton1Pressed(EditDataProvider *provider, int modifierKey); void pipetteButton1Released(EditDataProvider *provider); void pipetteDrag(EditDataProvider *provider, int modifierKey); + void showCoordinateAdjuster(CoordinateProvider *provider); + void stopNumericalAdjustment(); bool curveReset (CurveEditor *ce); @@ -66,6 +71,7 @@ protected: void pastePressed (); void removeEditor (); const std::vector getCurveFromGUI (int type); + void editPointToggled(Gtk::ToggleButton *button); void editToggled (Gtk::ToggleButton *button); }; diff --git a/rtgui/mycurve.cc b/rtgui/mycurve.cc index ec31124d1..5301dda62 100644 --- a/rtgui/mycurve.cc +++ b/rtgui/mycurve.cc @@ -36,6 +36,7 @@ MyCurve::MyCurve () : pipetteR(-1.f), pipetteG(-1.f), pipetteB(-1.f), pipetteVal sized = RS_Pending; snapToElmt = -100; curveIsDirty = true; + edited_point = -1; set_extension_events(Gdk::EXTENSION_EVENTS_ALL); #if defined (__APPLE__) @@ -114,6 +115,15 @@ bool MyCurve::snapCoordinateY(double testedVal, double realVal) { return false; } +float MyCurve::getVal(LUTf &curve, int x) { + if ((graphW-2) == curve.getSize()) { + return curve[x]; + } + else { + return curve.getVal01(float(x)/(graphW-3)); + } +} + void MyCurve::on_style_changed (const Glib::RefPtr& style) { setDirty(true); queue_draw (); diff --git a/rtgui/mycurve.h b/rtgui/mycurve.h index 602ce5257..5a20ad190 100644 --- a/rtgui/mycurve.h +++ b/rtgui/mycurve.h @@ -24,6 +24,7 @@ #include "curvelistener.h" #include "cursormanager.h" #include "coloredbar.h" +#include "coordinateadjuster.h" #include "../rtengine/LUT.h" #include "guiutils.h" #include "options.h" @@ -59,8 +60,9 @@ enum ResizeState { }; class MyCurveIdleHelper; +class CurveEditor; -class MyCurve : public Gtk::DrawingArea, public BackBuffer, public ColorCaller { +class MyCurve : public Gtk::DrawingArea, public BackBuffer, public ColorCaller, public CoordinateProvider { friend class MyCurveIdleHelper; @@ -72,14 +74,14 @@ class MyCurve : public Gtk::DrawingArea, public BackBuffer, public ColorCaller { ColoredBar *leftBar; ColoredBar *bottomBar; CursorShape cursor_type; - int graphX, graphY, graphW, graphH; /// dimensions of the graphic area, excluding surrounding space for the points of for the colored bar + int graphX, graphY, graphW, graphH; /// position and dimensions of the graphic area, excluding surrounding space for the points of for the colored bar int prevGraphW, prevGraphH; /// previous inner width and height of the editor Gdk::ModifierType mod_type; int cursorX; /// X coordinate in the graph of the cursor int cursorY; /// Y coordinate in the graph of the cursor - std::vector< Point > point; - std::vector< Point > upoint; - std::vector< Point > lpoint; + LUTf point; + LUTf upoint; + LUTf lpoint; bool buttonPressed; /** * snapToElmt, which will be used for the Y axis only, must be interpreted like this: @@ -98,10 +100,14 @@ class MyCurve : public Gtk::DrawingArea, public BackBuffer, public ColorCaller { enum ResizeState sized; bool curveIsDirty; + int edited_point; // > -1 when a point is being numerically edited + std::vector editedPos; + virtual std::vector get_vector (int veclen) = 0; int getGraphMinSize() { return GRAPH_SIZE + RADIUS + 1; } bool snapCoordinateX(double testedVal, double realVal); bool snapCoordinateY(double testedVal, double realVal); + float getVal(LUTf &curve, int x); // return value = new requested height int calcDimensions (); @@ -123,7 +129,7 @@ class MyCurve : public Gtk::DrawingArea, public BackBuffer, public ColorCaller { virtual bool handleEvents (GdkEvent* event) = 0; virtual void reset (const std::vector &resetCurve, double identityValue=0.5) = 0; - virtual void pipetteMouseOver (EditDataProvider *provider, int modifierKey) =0; + virtual void pipetteMouseOver (CurveEditor *ce, EditDataProvider *provider, int modifierKey) =0; virtual void pipetteButton1Pressed(EditDataProvider *provider, int modifierKey) =0; virtual void pipetteButton1Released(EditDataProvider *provider) =0; virtual void pipetteDrag(EditDataProvider *provider, int modifierKey) =0; diff --git a/rtgui/mydiagonalcurve.cc b/rtgui/mydiagonalcurve.cc index 916fb380e..070459e7b 100644 --- a/rtgui/mydiagonalcurve.cc +++ b/rtgui/mydiagonalcurve.cc @@ -33,6 +33,9 @@ MyDiagonalCurve::MyDiagonalCurve () : activeParam(-1), bghistvalid(false) { bghist = new unsigned int[256]; + editedPos.resize(2); + editedPos.at(0) = editedPos.at(1) = 0.0; + signal_event().connect( sigc::mem_fun(*this, &MyDiagonalCurve::handleEvents) ); curve.type = DCT_Spline; @@ -82,47 +85,77 @@ std::vector MyDiagonalCurve::get_vector (int veclen) { // calculate remaining points std::vector curveDescr = getPoints (); - rtengine::DiagonalCurve* rtcurve = new rtengine::DiagonalCurve (curveDescr, veclen*1.2); + rtengine::DiagonalCurve rtcurve(curveDescr, veclen*1.2); std::vector t; t.resize (veclen); for (int i = 0; i < veclen; i++) t[i] = (double) i / (veclen - 1.0); - rtcurve->getVal (t, vector); - delete rtcurve; + rtcurve.getVal (t, vector); return vector; } +void MyDiagonalCurve::get_LUT (LUTf &lut) { + + int size = lut.getSize(); + + if (curve.type != DCT_Parametric) { + // count active points: + double prev =- 1.0; + int active = 0; + int firstact = -1; + for (int i = 0; i < (int)curve.x.size(); ++i) + if (curve.x.at(i) > prev) { + if (firstact < 0) + firstact = i; + prev = curve.x.at(i); + ++active; + } + // handle degenerate case: + if (active < 2) { + double ry; + if (active > 0) + ry = curve.y.at(firstact); + else + ry = 0.0; + if (ry < 0.0) ry = 0.0; + if (ry > 1.0) ry = 1.0; + for (int x = 0; x < size; ++x) + lut[x] = ry; + return; + } + } + + // calculate remaining points + std::vector curveDescr = getPoints (); + rtengine::DiagonalCurve rtcurve(curveDescr, lut.getUpperBound()*1.2); + double t; + double maxVal = double(lut.getUpperBound()); + for (int i = 0; i < size; i++) { + t = double(i) / maxVal; + lut[i] = rtcurve.getVal (t); + } + return; +} + void MyDiagonalCurve::interpolate () { prevGraphW = graphW; prevGraphH = graphH; - int nbPoints = graphW-2; - point.resize (nbPoints); - std::vector vector = get_vector (nbPoints); - for (int i = 0; i < nbPoints; ++i) { - float currX = float(i)/float(nbPoints-1); - point.at(i).setCoords(float(graphX)+1.5f+float(graphW-3)*currX, float(graphY)-1.5f-float(graphH-3)*float(vector.at(i))); - } - upoint.clear (); - lpoint.clear (); + int nbPoints = rtengine::max(graphW-2,201); + point(nbPoints); + get_LUT (point); + upoint.reset(); + lpoint.reset (); if (curve.type==DCT_Parametric && activeParam>0) { double tmp = curve.x.at(activeParam-1); if (activeParam>=4) { - upoint.resize(nbPoints); - lpoint.resize(nbPoints); + upoint(nbPoints); + lpoint(nbPoints); curve.x.at(activeParam-1) = 100; - vector = get_vector (nbPoints); - for (int i = 0; i < nbPoints; ++i) { - float currX = float(i)/float(nbPoints-1); - upoint.at(i).setCoords(float(graphX)+1.5f+float(graphW-3)*currX, float(graphY)-1.5f-float(graphH-3)*float(vector.at(i))); - } + get_LUT(upoint); curve.x.at(activeParam-1) = -100; - vector = get_vector (nbPoints); - for (int i = 0; i < nbPoints; ++i) { - float currX = float(i)/float(nbPoints-1); - lpoint.at(i).setCoords(float(graphX)+1.5f+float(graphW-3)*currX, float(graphY)-1.5f-float(graphH-3)*float(vector.at(i))); - } + get_LUT (lpoint); curve.x.at(activeParam-1) = tmp; } } @@ -140,8 +173,10 @@ void MyDiagonalCurve::draw (int handle) { return; // re-calculate curve if dimensions changed - if (curveIsDirty || prevGraphW != graphW || prevGraphH != graphH || int(point.size()) != graphW-2) + int currPointSize = point.getUpperBound(); + if (curveIsDirty || /*prevGraphW != graphW || prevGraphH != graphH ||*/ (currPointSize==200 && (graphW-3>200)) || (currPointSize>200 && (graphW-2<=200 || graphW-3!=currPointSize))) interpolate (); + currPointSize = point.getUpperBound(); Gtk::StateType state = !is_sensitive() ? Gtk::STATE_INSENSITIVE : Gtk::STATE_NORMAL; @@ -214,15 +249,16 @@ void MyDiagonalCurve::draw (int handle) { cr->set_line_width (1.0); // draw upper and lower bounds - if (curve.type==DCT_Parametric && activeParam>0 && lpoint.size()>1 && upoint.size()>1) { + float graphH_ = float(graphH-3); + float graphX_ = float(graphX)+1.5; + float graphY_ = float(graphY)-1.5; + if (curve.type==DCT_Parametric && activeParam>0 && lpoint.getUpperBound()>1 && upoint.getUpperBound()>1) { cr->set_source_rgba (0.0, 0.0, 0.0, 0.15); - cr->move_to (upoint[0].x, upoint[0].y); - for (int i=1; i<(int)upoint.size(); i++) - cr->line_to (upoint[i].x, upoint[i].y); - cr->line_to (lpoint[lpoint.size()-1].x, lpoint[lpoint.size()-1].y); - for (int i=(int)lpoint.size()-2; i>=0; i--) - cr->line_to (lpoint[i].x, lpoint[i].y); - cr->line_to (upoint[0].x, upoint[0].y); + cr->move_to (graphX_, getVal(upoint, 0)*-graphH_+graphY_); + for (int i=1; iline_to (float(i)+graphX_, getVal(upoint, i)*-graphH_+graphY_); + for (int i=graphW-3; i>=0; --i) + cr->line_to (float(i)+graphX_, getVal(lpoint, i)*-graphH_+graphY_); cr->fill (); } @@ -299,10 +335,9 @@ void MyDiagonalCurve::draw (int handle) { // draw curve cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p()); - cr->move_to (double(point.at(0).x), double(point.at(0).y)); - for (int i=1; i<(int)point.size(); i++) { - cr->line_to (double(point.at(i).x), double(point.at(i).y)); - } + cr->move_to (graphX_, getVal(point, 0)*-graphH_+graphY_); + for (int i=1; iline_to (float(i)+graphX_, getVal(point, i)*-graphH_+graphY_); cr->stroke (); // draw the left colored bar @@ -348,7 +383,7 @@ void MyDiagonalCurve::draw (int handle) { cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p()); } else { - if (i == handle || i == snapToElmt) + if (i == handle || i == snapToElmt || i == edited_point) cr->set_source_rgb (1.0, 0.0, 0.0); else cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p()); @@ -359,6 +394,14 @@ void MyDiagonalCurve::draw (int handle) { cr->arc (x, y, RADIUS+0.5, 0, 2*M_PI); cr->fill (); + + if (i == edited_point) { + cr->set_line_width(2.); + cr->arc (x, y, RADIUS+3.5, 0, 2*M_PI); + cr->stroke(); + cr->set_line_width(1.); + } + } } setDirty(false); @@ -408,7 +451,8 @@ bool MyDiagonalCurve::handleEvents (GdkEvent* event) { // setDrawRectangle will allocate the backbuffer Surface if (setDrawRectangle(win, 0, 0, get_allocation().get_width(), get_allocation().get_height())) { setDirty(true); - curveIsDirty = true; + if (prevGraphW > 200 || graphW > 200) + curveIsDirty = true; } draw (lit_point); GdkRectangle *rectangle = &(event->expose.area); @@ -421,50 +465,118 @@ bool MyDiagonalCurve::handleEvents (GdkEvent* event) { case Gdk::BUTTON_PRESS: snapToElmt = -100; if (curve.type!=DCT_Parametric) { - if (event->button.button == 1) { - std::vector::iterator itx, ity; - buttonPressed = true; - add_modal_grab (); + if (edited_point == -1) { + if (event->button.button == 1) { + std::vector::iterator itx, ity; + buttonPressed = true; + add_modal_grab (); - // get the pointer position - getCursorPosition(Gdk::EventType(event->type), event->motion.is_hint!=0, int(event->button.x), int(event->button.y), Gdk::ModifierType(event->button.state)); - findClosestPoint(); + // get the pointer position + getCursorPosition(Gdk::EventType(event->type), event->motion.is_hint!=0, int(event->button.x), int(event->button.y), Gdk::ModifierType(event->button.state)); + findClosestPoint(); - new_type = CSMove; - if (distanceX > minDistanceX) { - /* insert a new control point */ - if (num > 0) { - if (clampedX > curve.x.at(closest_point)) - ++closest_point; + new_type = CSMove; + if (distanceX > minDistanceX) { + if (mod_type & GDK_CONTROL_MASK) { + clampedY = point.getVal01(clampedX); + } + /* insert a new control point */ + if (num > 0) { + if (clampedX > curve.x.at(closest_point)) + ++closest_point; + } + itx = curve.x.begin(); + ity = curve.y.begin(); + for (int i=0; ibutton.button == 3) { + if (lit_point>-1 && grab_point==-1) { + if (!coordinateAdjuster->is_visible()) + coordinateAdjuster->showMe(this); + + edited_point = lit_point; + std::vector newBoundaries(2); + unsigned int size = curve.x.size(); + if (edited_point == 0) { newBoundaries.at(0).minVal = 0.; newBoundaries.at(0).maxVal = curve.x.at(1); } + else if (edited_point == size-1) { newBoundaries.at(0).minVal = curve.x.at(edited_point-1); newBoundaries.at(0).maxVal = 1.; } + else if (curve.x.size() > 2) { newBoundaries.at(0).minVal = curve.x.at(edited_point-1); newBoundaries.at(0).maxVal = curve.x.at(edited_point+1); } + newBoundaries.at(1).minVal = 0.; newBoundaries.at(1).maxVal = 1.; + editedPos.at(0) = curve.x.at(edited_point); + editedPos.at(1) = curve.y.at(edited_point); + coordinateAdjuster->setPos(editedPos); + coordinateAdjuster->startNumericalAdjustment(newBoundaries); + setDirty(true); + draw (lit_point); + new_type = CSArrow; + retval = true; + } + } + if (buttonPressed) retval = true; } - if (buttonPressed) retval = true; + else { // if (edited_point > -1) + if (event->button.button == 3) { + // do we edit another point? + if (edited_point>-1 && grab_point==-1) { + /* get the pointer position */ + getCursorPosition(Gdk::EventType(event->type), event->motion.is_hint!=0, int(event->button.x), int(event->button.y), Gdk::ModifierType(event->button.state)); + findClosestPoint(); + if (cursorX>=0 && cursorX<=graphW && cursorY>=0 && cursorY<=graphH) { + if (distanceX <= minDistanceX) { + // the cursor is close to an existing point + lit_point = closest_point; + if (lit_point != edited_point) { + edited_point = lit_point; + curveIsDirty = true; + setDirty(true); + draw (lit_point); + std::vector newBoundaries; + newBoundaries.resize(2); + unsigned int size = curve.x.size(); + if (edited_point == 0) { newBoundaries.at(0).minVal = 0.; newBoundaries.at(0).maxVal = curve.x.at(1); } + else if (edited_point == size-1) { newBoundaries.at(0).minVal = curve.x.at(edited_point-1); newBoundaries.at(0).maxVal = 1.; } + else if (curve.x.size() > 2) { newBoundaries.at(0).minVal = curve.x.at(edited_point-1); newBoundaries.at(0).maxVal = curve.x.at(edited_point+1); } + newBoundaries.at(1).minVal = 0.; newBoundaries.at(1).maxVal = 1.; + retval = true; + editedPos.at(0) = curve.x.at(edited_point); + editedPos.at(1) = curve.y.at(edited_point); + coordinateAdjuster->switchAdjustedPoint(editedPos, newBoundaries); + } + } + else { + // the cursor is inside the graph but away from existing points + new_type = CSPlus; + curveIsDirty = true; + stopNumericalAdjustment(); + } + } + retval = true; + } + } + } + retval = true; } break; case Gdk::BUTTON_RELEASE: snapToElmt = -100; - if (curve.type!=DCT_Parametric) { + if (curve.type!=DCT_Parametric && edited_point==-1) { if (buttonPressed && event->button.button == 1) { std::vector::iterator itx, ity; int src, dst; @@ -542,25 +654,56 @@ bool MyDiagonalCurve::handleEvents (GdkEvent* event) { getCursorPosition(Gdk::EventType(event->type), event->motion.is_hint!=0, int(event->button.x), int(event->button.y), Gdk::ModifierType(event->button.state)); if (grab_point == -1) { - // there's no point currently being moved - int previous_lit_point = lit_point; - findClosestPoint(); - if (cursorX<0 || cursorX>graphW || cursorY<0 || cursorY>graphH) { - // the cursor has left the graph area - new_type = CSArrow; - lit_point = -1; + if (edited_point==-1) { + // there's no point currently being moved + int previous_lit_point = lit_point; + findClosestPoint(); + if (cursorX<0 || cursorX>graphW || cursorY<0 || cursorY>graphH) { + // the cursor has left the graph area + new_type = CSArrow; + lit_point = -1; + } + else if (distanceX <= minDistanceX) { + // the cursor is close to an existing point + new_type = CSMove; + lit_point = closest_point; + } + else { + // the cursor is inside the graph but away from existing points + new_type = CSPlus; + lit_point = -1; + } + if (lit_point != previous_lit_point) { + setDirty(true); + draw (lit_point); + if (lit_point > -1) { + editedPos.at(0) = curve.x.at(lit_point); + editedPos.at(1) = curve.y.at(lit_point); + } + coordinateAdjuster->setPos(editedPos); + } + if (lit_point == -1 && new_type == CSPlus) { + editedPos.at(0) = clampedX; + editedPos.at(1) = clampedY; + coordinateAdjuster->setPos(editedPos); + } } - else if (distanceX <= minDistanceX) { - new_type = CSMove; - lit_point = closest_point; - } - else { - new_type = CSPlus; - lit_point = -1; - } - if (lit_point != previous_lit_point) { - setDirty(true); - draw (lit_point); + else { // if (edited_point > -1) + // there's no point currently being moved + int previous_lit_point = lit_point; + findClosestPoint(); + if (distanceX <= minDistanceX) { + // the cursor is close to an existing point + lit_point = closest_point; + } + else { + // the cursor is outside the graph or inside the graph but away from existing points + lit_point = -1; + } + if (lit_point != previous_lit_point) { + setDirty(true); + draw (lit_point); + } } } else { @@ -646,6 +789,12 @@ bool MyDiagonalCurve::handleEvents (GdkEvent* event) { setDirty(true); draw (lit_point); notifyListener (); + + if (coordinateAdjuster->is_visible()) { + editedPos.at(0) = curve.x.at(grab_point); + editedPos.at(1) = curve.y.at(grab_point); + coordinateAdjuster->setPos(editedPos); + } } } } @@ -669,7 +818,7 @@ CursorShape MyDiagonalCurve::motionNotify(CursorShape type, double minDistanceX, return new_type; } -void MyDiagonalCurve::pipetteMouseOver (EditDataProvider *provider, int modifierKey) { +void MyDiagonalCurve::pipetteMouseOver (CurveEditor *ce, EditDataProvider *provider, int modifierKey) { if (!provider) { // occurs when leaving the preview area -> cleanup the curve editor pipetteR = pipetteG = pipetteB = -1.f; @@ -681,8 +830,9 @@ void MyDiagonalCurve::pipetteMouseOver (EditDataProvider *provider, int modifier pipetteG = provider->pipetteVal[1]; pipetteB = provider->pipetteVal[2]; pipetteVal = 0.f; - if (listener) - pipetteVal = listener->blendPipetteValues(pipetteR, pipetteG, pipetteB); + if (listener) { + pipetteVal = listener->blendPipetteValues(ce, pipetteR, pipetteG, pipetteB); + } else { int n = 0; if (pipetteR != -1.f) { @@ -713,33 +863,45 @@ void MyDiagonalCurve::pipetteMouseOver (EditDataProvider *provider, int modifier if (curve.type == DCT_Linear || curve.type == DCT_Spline || curve.type == DCT_NURBS) { // get the pointer position - int px = graphX + int(float(graphW)*pipetteVal); // WARNING: converting pipetteVal from float to int, precision loss here! - getCursorPosition(Gdk::MOTION_NOTIFY, false, px, graphY, Gdk::ModifierType(modifierKey)); + getCursorPositionFromCurve(pipetteVal); - if (grab_point == -1) { - // there's no point currently being moved - int previous_lit_point = lit_point; - findClosestPoint(); - if (cursorX<0 || cursorX>graphW || cursorY<0 || cursorY>graphH) { - // the cursor has left the graph area - lit_point = -1; - } - else if (distanceX <= minDistanceX) { - lit_point = closest_point; - } - else { - lit_point = -1; - } - if (lit_point != previous_lit_point) { - setDirty(true); - draw (lit_point); + if (edited_point==-1) { + if (grab_point == -1) { + // there's no point currently being moved + int previous_lit_point = lit_point; + findClosestPoint(); + if (cursorX<0 || cursorX>graphW || cursorY<0 || cursorY>graphH) { + // the cursor has left the graph area + lit_point = -1; + } + else if (distanceX <= minDistanceX) { + lit_point = closest_point; + } + else { + lit_point = -1; + } + if (lit_point != previous_lit_point) { + setDirty(true); + draw (lit_point); + } } } + else + draw(lit_point); + + if (edited_point==-1) { + editedPos.at(0) = pipetteVal; + editedPos.at(1) = point.getVal01(pipetteVal); + coordinateAdjuster->setPos(editedPos); + } } } void MyDiagonalCurve::pipetteButton1Pressed(EditDataProvider *provider, int modifierKey) { + if (edited_point>1) + return; + int num = (int)curve.x.size(); /* graphW and graphH are the size of the graph */ @@ -756,13 +918,10 @@ void MyDiagonalCurve::pipetteButton1Pressed(EditDataProvider *provider, int modi buttonPressed = true; // get the pointer position - int px = graphX + int(float(graphW)*pipetteVal); // WARNING: converting pipetteVal from float to int, precision loss here! - getCursorPosition(Gdk::BUTTON_PRESS, false, px, graphY, Gdk::ModifierType(modifierKey)); + getCursorPositionFromCurve(pipetteVal); findClosestPoint(); if (distanceX > minDistanceX) { - rtengine::DiagonalCurve rtCurve(getPoints(), 200); - /* insert a new control point */ if (num > 0) { if (clampedX > curve.x.at(closest_point)) @@ -771,17 +930,25 @@ void MyDiagonalCurve::pipetteButton1Pressed(EditDataProvider *provider, int modi itx = curve.x.begin(); ity = curve.y.begin(); for (int i=0; i-1 && grab_point==-1 && coordinateAdjuster->is_visible()) { + std::vector position; + position.resize(2); + position.at(0) = clampedX; + position.at(1) = clampedY; + coordinateAdjuster->setPos(position); + } curveIsDirty = true; setDirty(true); - draw (closest_point); + draw (lit_point); notifyListener (); } grab_point = closest_point; @@ -792,6 +959,9 @@ void MyDiagonalCurve::pipetteButton1Pressed(EditDataProvider *provider, int modi } void MyDiagonalCurve::pipetteButton1Released(EditDataProvider *provider) { + if (edited_point>1) + return; + /* graphW and graphH are the size of the graph */ calcDimensions(); @@ -827,7 +997,7 @@ void MyDiagonalCurve::pipetteButton1Released(EditDataProvider *provider) { } void MyDiagonalCurve::pipetteDrag(EditDataProvider *provider, int modifierKey) { - if (curve.type==DCT_Parametric || graphW<0 || graphH<0) + if (edited_point>-1 || curve.type==DCT_Parametric || graphW<0 || graphH<0) return; snapToMinDistY = snapToMinDistX = 10.; @@ -837,7 +1007,7 @@ void MyDiagonalCurve::pipetteDrag(EditDataProvider *provider, int modifierKey) { /* graphW and graphH are the size of the graph */ calcDimensions(); - getCursorPosition(Gdk::MOTION_NOTIFY, false, cursorX+graphX, graphY+provider->deltaScreen.y, Gdk::ModifierType(modifierKey)); + getCursorPosition(Gdk::MOTION_NOTIFY, false, cursorX+graphX, graphY-cursorY+provider->deltaPrevScreen.y, Gdk::ModifierType(modifierKey)); // we memorize the previous position of the point, for optimization purpose double prevPosX = curve.x.at(grab_point); @@ -888,9 +1058,38 @@ void MyDiagonalCurve::pipetteDrag(EditDataProvider *provider, int modifierKey) { setDirty(true); draw (lit_point); notifyListener (); + + if (lit_point>-1 && coordinateAdjuster->is_visible()) { + std::vector position; + position.resize(2); + position.at(0) = curve.x.at(grab_point); + position.at(1) = curve.y.at(grab_point); + coordinateAdjuster->setPos(position); + } + } } +void MyDiagonalCurve::getCursorPositionFromCurve(float x) { + + // the graph is refreshed only if a new point is created (snaped to a pixel) + clampedX = x; + clampedY = point.getVal01(x); + + cursorX = int(clampedX*float(graphW-3)) + graphX+1.5; + cursorY = graphY - int(clampedY*float(graphH-3)); +} + +// x = cursor position found in the event +void MyDiagonalCurve::getCursorPositionFromCurve(int x) { + + // the graph is refreshed only if a new point is created (snaped to a pixel) + cursorX = x-graphX; + clampedX = (float(cursorX)-1.5) / float(graphW-3); + clampedY = point.getVal01(clampedX); + cursorY = graphY - int(float(1.-clampedY)*float(graphH-3)); +} + void MyDiagonalCurve::getCursorPosition(Gdk::EventType evType, bool isHint, int evX, int evY, Gdk::ModifierType modifierKey) { int tx, ty; int prevCursorX, prevCursorY; @@ -1006,6 +1205,7 @@ std::vector MyDiagonalCurve::getPoints () { void MyDiagonalCurve::setPoints (const std::vector& p) { int ix = 0; + stopNumericalAdjustment(); DiagonalCurveType t = (DiagonalCurveType)p[ix++]; curve.type = t; if (t==DCT_Parametric) { @@ -1028,6 +1228,29 @@ void MyDiagonalCurve::setPoints (const std::vector& p) { queue_draw (); } +void MyDiagonalCurve::setPos(double pos, int chanIdx) { + assert (edited_point > -1); + if (chanIdx == 0) { + curve.x.at(edited_point) = pos; + } + else if (chanIdx == 1) { + curve.y.at(edited_point) = pos; + } + curveIsDirty = true; + setDirty(true); + draw(lit_point); + notifyListener (); +} + +void MyDiagonalCurve::stopNumericalAdjustment() { + if (edited_point>-1) { + edited_point = grab_point = lit_point = -1; + coordinateAdjuster->stopNumericalAdjustment(); + setDirty(true); + draw(lit_point); + } +} + void MyDiagonalCurve::setType (DiagonalCurveType t) { curve.type = t; @@ -1081,6 +1304,8 @@ void MyDiagonalCurve::updateBackgroundHistogram (LUTu & hist) { void MyDiagonalCurve::reset(const std::vector &resetCurve, double identityValue) { + stopNumericalAdjustment(); + if (!resetCurve.empty()) { setPoints(resetCurve); return; diff --git a/rtgui/mydiagonalcurve.h b/rtgui/mydiagonalcurve.h index 5866b4ade..881a568d1 100644 --- a/rtgui/mydiagonalcurve.h +++ b/rtgui/mydiagonalcurve.h @@ -49,7 +49,7 @@ class MyDiagonalCurve : public MyCurve { protected: DiagonalCurveDescr curve; - int grab_point; // the point that the user is moving + int grab_point; // the point that the user is moving by mouse int closest_point; // the point that is the closest from the cursor int lit_point; // the point that is lit when the cursor is near it double clampedX; // clamped grabbed point X coordinates in the [0;1] range @@ -63,12 +63,18 @@ class MyDiagonalCurve : public MyCurve { int activeParam; unsigned int* bghist; // histogram values bool bghistvalid; + void draw (int handle); void interpolate (); - void getCursorPosition(Gdk::EventType evType, bool isHint, int evX, int evY, Gdk::ModifierType modifierKey); void findClosestPoint(); CursorShape motionNotify(CursorShape type, double minDistanceX, double minDistanceY, int num); std::vector get_vector (int veclen); + void get_LUT (LUTf &lut); + // Get the cursor position and unclamped position from the curve given an X value ; BEWARE: can be time consuming, use with care + void getCursorPositionFromCurve(float x); + void getCursorPositionFromCurve(int x); + // Get the cursor position and unclamped value depending on cursor's position in the graph + void getCursorPosition(Gdk::EventType evType, bool isHint, int evX, int evY, Gdk::ModifierType modifierKey); public: MyDiagonalCurve (); @@ -81,10 +87,13 @@ class MyDiagonalCurve : public MyCurve { void reset (const std::vector &resetCurve, double identityValue=0.5); void updateBackgroundHistogram (LUTu & hist); - void pipetteMouseOver (EditDataProvider *provider, int modifierKey); + void pipetteMouseOver (CurveEditor *ce, EditDataProvider *provider, int modifierKey); void pipetteButton1Pressed(EditDataProvider *provider, int modifierKey); void pipetteButton1Released(EditDataProvider *provider); void pipetteDrag(EditDataProvider *provider, int modifierKey); + + virtual void setPos(double pos, int chanIdx); + virtual void stopNumericalAdjustment(); }; #endif diff --git a/rtgui/myflatcurve.cc b/rtgui/myflatcurve.cc index 0bce7c073..77678eb03 100644 --- a/rtgui/myflatcurve.cc +++ b/rtgui/myflatcurve.cc @@ -37,6 +37,9 @@ MyFlatCurve::MyFlatCurve () { //bghist = new unsigned int[256]; + editedPos.resize(4); + editedPos.at(0) = editedPos.at(1) = editedPos.at(2) = editedPos.at(3) = 0.0; + signal_event().connect( sigc::mem_fun(*this, &MyFlatCurve::handleEvents) ); // By default, we create a curve with 8 control points @@ -55,7 +58,7 @@ std::vector MyFlatCurve::get_vector (int veclen) { // Get the curve control points std::vector curveDescr = getPoints (); - rtengine::FlatCurve* rtcurve = new rtengine::FlatCurve (curveDescr, periodic, veclen*1.2 > 5000 ? 5000 : veclen*1.2); + rtengine::FlatCurve rtcurve(curveDescr, periodic, veclen*1.2 > 5000 ? 5000 : veclen*1.2); // Create the sample values that will be converted std::vector samples; @@ -64,26 +67,38 @@ std::vector MyFlatCurve::get_vector (int veclen) { samples.at(i) = (double) i / (veclen - 1.0); // Converting the values - rtcurve->getVal (samples, convertedValues); + rtcurve.getVal (samples, convertedValues); // Cleanup and return - delete rtcurve; return convertedValues; } +void MyFlatCurve::get_LUT (LUTf &lut) { + + int size = lut.getSize(); + + // Get the curve control points + std::vector curveDescr = getPoints (); + rtengine::FlatCurve rtcurve(curveDescr, periodic, lut.getUpperBound()*1.2 > 5000 ? 5000 : lut.getUpperBound()*1.2); + + double t; + double maxVal = double(lut.getUpperBound()); + for (int i = 0; i < size; i++) { + t = double(i) / maxVal; + lut[i] = rtcurve.getVal (t); + } + return; +} + void MyFlatCurve::interpolate () { prevGraphW = graphW; prevGraphH = graphH; int nbPoints = graphW-2; - point.resize (nbPoints); - std::vector vector = get_vector (nbPoints); - for (int i = 0; i < nbPoints; ++i) { - float currX = float(i)/float(nbPoints-1); - point.at(i).setCoords(float(graphX)+1.5f+float(graphW-3)*currX, float(graphY)-1.5f-float(graphH-3)*float(vector.at(i))); - } - upoint.clear (); - lpoint.clear (); + point(nbPoints); + get_LUT (point); + upoint.reset (); + lpoint.reset (); curveIsDirty = false; } @@ -98,7 +113,8 @@ void MyFlatCurve::draw () { return; // re-calculate curve if dimensions changed - if (curveIsDirty || prevGraphW != graphW || prevGraphH != graphH || (int)point.size() != graphW-2) + int currPointSize = point.getUpperBound(); + if (curveIsDirty || /*prevGraphW != graphW || prevGraphH != graphH ||*/ (currPointSize==200 && (graphW-3>200)) || (currPointSize>200 && (graphW-2<=200 || graphW-3!=currPointSize))) interpolate (); double innerW = double(graphW-2); @@ -228,22 +244,30 @@ void MyFlatCurve::draw () { cr->set_line_width (coloredLineWidth); // draw the lit_point's horizontal line - if (i == lit_point) { - + bool drawHLine = false; + if (edited_point>-1) { + if (i == edited_point) { + cr->set_line_width (2*coloredLineWidth); + drawHLine = true; + } + } + else if (i == lit_point) { if ( (area&(FCT_Area_H|FCT_Area_V|FCT_Area_Point)) || editedHandle==FCT_EditedHandle_CPointUD) { - if (editedHandle&(FCT_EditedHandle_CPointUD|FCT_EditedHandle_CPoint|FCT_EditedHandle_CPointY)) { cr->set_line_width (2*coloredLineWidth); + drawHLine = true; } - - colorProvider->colorForValue(curve.x.at(i), curve.y.at(i), CCET_HORIZONTAL_BAR, colorCallerId, this); - cr->set_source_rgb (ccRed, ccGreen, ccBlue); - - cr->move_to (double(graphX+1) , double(graphY-1) - innerH*curve.y.at(lit_point)); - cr->rel_line_to (innerW, 0.); - cr->stroke (); } } + if (drawHLine) { + int point = edited_point>-1 ? edited_point : lit_point; + colorProvider->colorForValue(curve.x.at(i), curve.y.at(i), CCET_HORIZONTAL_BAR, colorCallerId, this); + cr->set_source_rgb (ccRed, ccGreen, ccBlue); + + cr->move_to (double(graphX+1) , double(graphY-1) - innerH*curve.y.at(point)); + cr->rel_line_to (innerW, 0.); + cr->stroke (); + } } } // endif @@ -252,12 +276,13 @@ void MyFlatCurve::draw () { else { cr->set_source_rgb (0.5, 0.0, 0.0); - if ((lit_point>-1) && ((area&(FCT_Area_H|FCT_Area_V|FCT_Area_Point)) || editedHandle==FCT_EditedHandle_CPointUD) ) { + if (edited_point>-1 || ((lit_point>-1) && ((area&(FCT_Area_H|FCT_Area_V|FCT_Area_Point)) || editedHandle==FCT_EditedHandle_CPointUD)) ) { // draw the lit_point's vertical line - if (editedHandle&(FCT_EditedHandle_CPointUD|FCT_EditedHandle_CPoint|FCT_EditedHandle_CPointY)) { + if (edited_point>-1 || (editedHandle&(FCT_EditedHandle_CPointUD|FCT_EditedHandle_CPoint|FCT_EditedHandle_CPointY))) { cr->set_line_width (2.0); } - cr->move_to (double(graphX)+1 + innerW*curve.x.at(lit_point), double(graphY-1)); + int point = edited_point>-1 ? edited_point : lit_point; + cr->move_to (double(graphX)+1 + innerW*curve.x.at(point), double(graphY-1)); cr->rel_line_to (0., -innerH); cr->stroke (); cr->set_line_width (1.0); @@ -266,7 +291,7 @@ void MyFlatCurve::draw () { if (editedHandle&(FCT_EditedHandle_CPointUD|FCT_EditedHandle_CPoint|FCT_EditedHandle_CPointY)) { cr->set_line_width (2.0); } - cr->move_to (double(graphX+1) , double(graphY-1) - innerH*curve.y.at(lit_point)); + cr->move_to (double(graphX+1) , double(graphY-1) - innerH*curve.y.at(point)); cr->rel_line_to (innerW, 0.); cr->stroke (); cr->set_line_width (1.0); @@ -282,7 +307,7 @@ void MyFlatCurve::draw () { cr->stroke (); double lineMinLength = 1. / graphW * SQUARE * 0.9; - if (lit_point!=-1 && getHandles(lit_point) && curve.x.at(lit_point)!=-1.) { + if (tanHandlesDisplayed && lit_point!=-1 && getHandles(lit_point) && curve.x.at(lit_point)!=-1.) { double x = double(graphX+1) + innerW*curve.x.at(lit_point); double y = double(graphY) - innerH*curve.y.at(lit_point); double x2; @@ -353,26 +378,30 @@ void MyFlatCurve::draw () { // draw curve c = style->get_fg (state); cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p()); - cr->move_to (point.at(0).x, point.at(0).y); - for (int i=1; i<(int)point.size(); i++) - cr->line_to (double(point.at(i).x), double(point.at(i).y)); + float graphH_ = float(graphH-3); + float graphX_ = float(graphX)+1.5; + float graphY_ = float(graphY)-1.5; + cr->move_to (graphX_, getVal(point, 0)*-graphH_+graphY_); + for (int i=1; iline_to (float(i)+graphX_, getVal(point, i)*-graphH_+graphY_); cr->stroke (); // draw bullets //if (curve.type!=FCT_Parametric) for (int i = 0; i < (int)curve.x.size(); ++i) { if (curve.x.at(i) != -1.) { - if (i == lit_point) { - if (colorProvider) { + if (i == edited_point) + cr->set_source_rgb (1.0, 0.0, 0.0); + else if (i == lit_point) { + if (colorProvider && edited_point==-1) { colorProvider->colorForValue(curve.x.at(i), curve.y.at(i), CCET_POINT, colorCallerId, this); cr->set_source_rgb (ccRed, ccGreen, ccBlue); } else cr->set_source_rgb (1.0, 0.0, 0.0); } - else if (i == snapToElmt) { + else if (i == snapToElmt || i == edited_point) cr->set_source_rgb (1.0, 0.0, 0.0); - } else if (curve.y.at(i) == 0.5) cr->set_source_rgb (0.0, 0.5, 0.0); else @@ -383,6 +412,15 @@ void MyFlatCurve::draw () { cr->arc (x, y, (double)RADIUS, 0, 2*M_PI); cr->fill (); + + if (i == edited_point) { + cr->set_source_rgb (1.0, 0.0, 0.0); + cr->set_line_width(2.); + cr->arc (x, y, RADIUS+3.5, 0, 2*M_PI); + cr->stroke(); + cr->set_line_width(1.); + } + } } // endif @@ -499,7 +537,8 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) { // setDrawRectangle will allocate the backbuffer Surface if (setDrawRectangle(win, 0, 0, get_allocation().get_width(), get_allocation().get_height())) { setDirty(true); - curveIsDirty = true; + if (prevGraphW > 200 || graphW > 200) + curveIsDirty = true; } draw (); GdkRectangle *rectangle = &(event->expose.area); @@ -510,7 +549,7 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) { } case Gdk::BUTTON_PRESS: - //if (curve.type!=FCT_Parametric) { + if (edited_point==-1) { //curve.type!=FCT_Parametric) { if (event->button.button == 1) { buttonPressed = true; add_modal_grab (); @@ -544,6 +583,10 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) { curve.rightTangent.insert (itrt, 0); num++; + if (mod_type & GDK_CONTROL_MASK) { + clampedY = point.getVal01(clampedX); + } + // the graph is refreshed only if a new point is created curve.x.at(closest_point) = clampedX; curve.y.at(closest_point) = clampedY; @@ -593,14 +636,85 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) { default: break; } + } + else if (event->button.button == 3) { + /* get the pointer position */ + getCursorPosition(Gdk::EventType(event->type), event->motion.is_hint!=0, int(event->button.x), int(event->button.y), Gdk::ModifierType(event->button.state)); + getMouseOverArea(); + if (lit_point>-1 && lit_point!=edited_point) { + if (editedHandle == FCT_EditedHandle_None) { + if (area == FCT_Area_Point || area == FCT_Area_V) { + // the cursor is close to an existing point + if (!coordinateAdjuster->is_visible()) + coordinateAdjuster->showMe(this); + new_type = CSArrow; + tanHandlesDisplayed = false; + edited_point = lit_point; + setDirty(true); + draw (); + std::vector newBoundaries(4); + unsigned int size = curve.x.size(); + if (edited_point == 0) { newBoundaries.at(0).minVal = 0.; newBoundaries.at(0).maxVal = curve.x.at(1); } + else if (edited_point == size-1) { newBoundaries.at(0).minVal = curve.x.at(edited_point-1); newBoundaries.at(0).maxVal = 1.; } + else if (curve.x.size() > 2) { newBoundaries.at(0).minVal = curve.x.at(edited_point-1); newBoundaries.at(0).maxVal = curve.x.at(edited_point+1); } + newBoundaries.at(1).minVal = 0.; newBoundaries.at(1).maxVal = 1.; + newBoundaries.at(2).minVal = 0.; newBoundaries.at(2).maxVal = 1.; + newBoundaries.at(3).minVal = 0.; newBoundaries.at(3).maxVal = 1.; + retval = true; + editedPos.at(0) = curve.x.at(edited_point); + editedPos.at(1) = curve.y.at(edited_point); + editedPos.at(2) = curve.leftTangent.at(edited_point); + editedPos.at(3) = curve.rightTangent.at(edited_point); + coordinateAdjuster->setPos(editedPos); + coordinateAdjuster->startNumericalAdjustment(newBoundaries); + } + } + } + retval = true; } if (buttonPressed) retval = true; - //} + } + else { // if (edited_point > -1) + if (event->button.button == 3) { + // do we edit another point? + /* get the pointer position */ + getCursorPosition(Gdk::EventType(event->type), event->motion.is_hint!=0, int(event->button.x), int(event->button.y), Gdk::ModifierType(event->button.state)); + getMouseOverArea(); + if (area == FCT_Area_Point || area == FCT_Area_V) { + // the cursor is close to an existing point + if (lit_point != edited_point) { + edited_point = lit_point; + setDirty(true); + draw (); + std::vector newBoundaries(4); + unsigned int size = curve.x.size(); + if (edited_point == 0) { newBoundaries.at(0).minVal = 0.; newBoundaries.at(0).maxVal = curve.x.at(1); } + else if (edited_point == size-1) { newBoundaries.at(0).minVal = curve.x.at(edited_point-1); newBoundaries.at(0).maxVal = 1.; } + else if (curve.x.size() > 2) { newBoundaries.at(0).minVal = curve.x.at(edited_point-1); newBoundaries.at(0).maxVal = curve.x.at(edited_point+1); } + newBoundaries.at(1).minVal = 0.; newBoundaries.at(1).maxVal = 1.; + newBoundaries.at(2).minVal = 0.; newBoundaries.at(2).maxVal = 1.; + newBoundaries.at(3).minVal = 0.; newBoundaries.at(3).maxVal = 1.; + editedPos.at(0) = curve.x.at(edited_point); + editedPos.at(1) = curve.y.at(edited_point); + editedPos.at(2) = curve.leftTangent.at(edited_point); + editedPos.at(3) = curve.rightTangent.at(edited_point); + coordinateAdjuster->switchAdjustedPoint(editedPos, newBoundaries); + retval = true; + } + } + else if (area == FCT_Area_Insertion) { + // the cursor is inside the graph but away from existing points + new_type = CSPlus; + curveIsDirty = true; + stopNumericalAdjustment(); + } + } + } break; case Gdk::BUTTON_RELEASE: - //if (curve.type!=FCT_Parametric) { + if (edited_point==-1) { //curve.type!=FCT_Parametric) { if (buttonPressed && event->button.button == 1) { int src, dst; buttonPressed = false; @@ -684,7 +798,7 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) { retval = true; //notifyListener (); } - //} + } break; case Gdk::MOTION_NOTIFY: @@ -722,7 +836,7 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) { case (FCT_EditedHandle_None): { - if ((lit_point != -1 && previous_lit_point != lit_point) && (area&(FCT_Area_V|FCT_Area_Point))) { + if ((lit_point != -1 && previous_lit_point != lit_point) && (area&(FCT_Area_V|FCT_Area_Point)) && edited_point==-1) { bool sameSide = false; @@ -771,54 +885,92 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) { } } } - else if (lit_point == -1) { + else if (lit_point == -1 || edited_point>-1) { tanHandlesDisplayed = false; } - switch (area) { + if (edited_point==-1) { + switch (area) { - case (FCT_Area_Insertion): - new_type = CSPlus; - break; - case (FCT_Area_Point): - //new_type = CSMove; - //break; - case (FCT_Area_V): - new_type = CSMove; - break; - case (FCT_Area_H): - new_type = CSResizeHeight; - break; - case (FCT_Area_LeftTan): - new_type = CSMoveLeft; - break; - case (FCT_Area_RightTan): - new_type = CSMoveRight; - break; - case (FCT_Area_None): - default: - new_type = CSArrow; - break; + case (FCT_Area_Insertion): + new_type = CSPlus; + break; + case (FCT_Area_Point): + //new_type = CSMove; + //break; + case (FCT_Area_V): + new_type = CSMove; + break; + case (FCT_Area_H): + new_type = CSResizeHeight; + break; + case (FCT_Area_LeftTan): + new_type = CSMoveLeft; + break; + case (FCT_Area_RightTan): + new_type = CSMoveRight; + break; + case (FCT_Area_None): + default: + new_type = CSArrow; + break; + } } if ((lit_point != previous_lit_point) || (prevArea != area)) { setDirty(true); draw (); } + + if (coordinateAdjuster->is_visible() && edited_point==-1) { + if (lit_point > -1) { + if (lit_point != previous_lit_point) { + editedPos.at(0) = curve.x.at(lit_point); + editedPos.at(1) = curve.y.at(lit_point); + editedPos.at(2) = curve.leftTangent.at(lit_point); + editedPos.at(3) = curve.rightTangent.at(lit_point); + } + coordinateAdjuster->setPos(editedPos); + } + else if (area == FCT_Area_Insertion) { + editedPos.at(0) = clampedX; + editedPos.at(1) = clampedY; + editedPos.at(2) = 0.; + editedPos.at(3) = 0.; + coordinateAdjuster->setPos(editedPos); + } + else { + editedPos.at(0) = editedPos.at(1) = editedPos.at(2) = editedPos.at(3) = 0; + coordinateAdjuster->setPos(editedPos); + } + } break; } case (FCT_EditedHandle_CPoint): movePoint(true, true); + if (coordinateAdjuster->is_visible()) { + editedPos.at(0) = curve.x.at(lit_point); + editedPos.at(1) = curve.y.at(lit_point); + coordinateAdjuster->setPos(editedPos); + } break; case (FCT_EditedHandle_CPointX): movePoint(true, false); + if (coordinateAdjuster->is_visible()) { + editedPos.at(0) = curve.x.at(lit_point); + coordinateAdjuster->setPos(editedPos); + } break; case (FCT_EditedHandle_CPointY): movePoint(false, true); + if (coordinateAdjuster->is_visible()) { + editedPos.at(1) = curve.y.at(lit_point); + coordinateAdjuster->setPos(editedPos); + } break; case (FCT_EditedHandle_LeftTan): { @@ -843,6 +995,10 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) { setDirty(true); draw (); notifyListener (); + if (coordinateAdjuster->is_visible()) { + editedPos.at(2) = curve.leftTangent.at(lit_point); + coordinateAdjuster->setPos(editedPos); + } } break; } @@ -869,6 +1025,8 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) { setDirty(true); draw (); notifyListener (); + editedPos.at(3) = curve.rightTangent.at(lit_point); + coordinateAdjuster->setPos(editedPos); } break; } @@ -879,6 +1037,23 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) { default: break; } + + if (edited_point==-1) { + if (lit_point == -1) { + editedPos.at(0) = editedPos.at(1) = editedPos.at(2) = editedPos.at(3) = 0; + } + else if (editedPos.at(0) != curve.x.at(lit_point) + || editedPos.at(1) != curve.y.at(lit_point) + || editedPos.at(2) != curve.leftTangent.at(lit_point) + || editedPos.at(3) != curve.rightTangent.at(lit_point)) + { + editedPos.at(0) = curve.x.at(lit_point); + editedPos.at(1) = curve.y.at(lit_point); + editedPos.at(2) = curve.leftTangent.at(lit_point); + editedPos.at(3) = curve.rightTangent.at(lit_point); + coordinateAdjuster->setPos(editedPos); + } + } } retval = true; @@ -908,7 +1083,7 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) { return retval; } -void MyFlatCurve::pipetteMouseOver (EditDataProvider *provider, int modifierKey) { +void MyFlatCurve::pipetteMouseOver (CurveEditor *ce, EditDataProvider *provider, int modifierKey) { if (!provider) { // occurs when leaving the preview area -> cleanup the curve editor pipetteR = pipetteG = pipetteB = -1.f; @@ -921,7 +1096,7 @@ void MyFlatCurve::pipetteMouseOver (EditDataProvider *provider, int modifierKey) pipetteB = provider->pipetteVal[2]; pipetteVal = 0.f; if (listener) - pipetteVal = listener->blendPipetteValues(pipetteR, pipetteG, pipetteB); + pipetteVal = listener->blendPipetteValues(ce, pipetteR, pipetteG, pipetteB); else { int n = 0; if (pipetteR != -1.f) { @@ -958,7 +1133,8 @@ void MyFlatCurve::pipetteMouseOver (EditDataProvider *provider, int modifierKey) // get the pointer position int px = graphX + int(float(graphW)*pipetteVal); // WARNING: converting pipetteVal from float to int, precision loss here! getCursorPosition(Gdk::EventType(Gdk::BUTTON_PRESS), false, px, graphY, Gdk::ModifierType(modifierKey)); - getMouseOverArea(); + if (edited_point==-1) + getMouseOverArea(); if (area==FCT_Area_Point) area = FCT_Area_V; @@ -966,13 +1142,26 @@ void MyFlatCurve::pipetteMouseOver (EditDataProvider *provider, int modifierKey) snapToMinDistY = snapToMinDistX = 10.; snapToValY = snapToValX = 0.; - if (editedHandle==FCT_EditedHandle_None && lit_point != previous_lit_point) { - setDirty(true); - draw (); + if (edited_point==-1) { + if (editedHandle==FCT_EditedHandle_None && lit_point != previous_lit_point) { + setDirty(true); + draw (); + } + } + else + draw(); + + if (edited_point==-1) { + editedPos.at(0) = pipetteVal; + editedPos.at(1) = point.getVal01(pipetteVal); + coordinateAdjuster->setPos(editedPos); } } void MyFlatCurve::pipetteButton1Pressed(EditDataProvider *provider, int modifierKey) { + if (edited_point>-1) + return; + buttonPressed = true; // get the pointer position @@ -1040,6 +1229,9 @@ void MyFlatCurve::pipetteButton1Pressed(EditDataProvider *provider, int modifier } void MyFlatCurve::pipetteButton1Released(EditDataProvider *provider) { + if (edited_point>-1) + return; + buttonPressed = false; remove_modal_grab (); @@ -1057,6 +1249,8 @@ void MyFlatCurve::pipetteButton1Released(EditDataProvider *provider) { } void MyFlatCurve::pipetteDrag(EditDataProvider *provider, int modifierKey) { + if (edited_point>-1) + return; snapToMinDistY = snapToMinDistX = 10.; snapToValY = snapToValX = 0.; @@ -1423,6 +1617,7 @@ std::vector MyFlatCurve::getPoints () { void MyFlatCurve::setPoints (const std::vector& p) { int ix = 0; + stopNumericalAdjustment(); FlatCurveType t = (FlatCurveType)p[ix++]; curve.type = t; if (t==FCT_MinMaxCPoints) { @@ -1442,6 +1637,38 @@ void MyFlatCurve::setPoints (const std::vector& p) { queue_draw (); } +void MyFlatCurve::setPos(double pos, int chanIdx) { + assert (edited_point > -1); + switch (chanIdx) { + case (0): + curve.x.at(edited_point) = pos; + break; + case (1): + curve.y.at(edited_point) = pos; + break; + case (2): + curve.leftTangent.at(edited_point) = pos; + break; + case (3): + curve.rightTangent.at(edited_point) = pos; + break; + } + curveIsDirty = true; + setDirty(true); + draw(); + notifyListener (); +} + +void MyFlatCurve::stopNumericalAdjustment() { + if (edited_point>-1) { + edited_point = lit_point = -1; + area = FCT_Area_None; + coordinateAdjuster->stopNumericalAdjustment(); + setDirty(true); + draw(); + } +} + void MyFlatCurve::setType (FlatCurveType t) { curve.type = t; @@ -1451,6 +1678,8 @@ void MyFlatCurve::setType (FlatCurveType t) { void MyFlatCurve::reset(const std::vector &resetCurve, double identityValue) { calcDimensions(); + stopNumericalAdjustment(); + // If a resetCurve exist (non empty) if (!resetCurve.empty()) { setPoints(resetCurve); diff --git a/rtgui/myflatcurve.h b/rtgui/myflatcurve.h index e57662351..6f18f56d1 100644 --- a/rtgui/myflatcurve.h +++ b/rtgui/myflatcurve.h @@ -110,6 +110,7 @@ class MyFlatCurve : public MyCurve { bool getHandles(int n); CursorShape motionNotify(CursorShape type, double minDistanceX, double minDistanceY, int num); std::vector get_vector (int veclen); + void get_LUT (LUTf &lut); public: MyFlatCurve (); @@ -122,10 +123,13 @@ class MyFlatCurve : public MyCurve { void reset (const std::vector &resetCurve, double identityValue=0.5); //void updateBackgroundHistogram (unsigned int* hist); - void pipetteMouseOver (EditDataProvider *provider, int modifierKey); + void pipetteMouseOver (CurveEditor *ce, EditDataProvider *provider, int modifierKey); void pipetteButton1Pressed(EditDataProvider *provider, int modifierKey); void pipetteButton1Released(EditDataProvider *provider); void pipetteDrag(EditDataProvider *provider, int modifierKey); + + void setPos(double pos, int chanIdx); + virtual void stopNumericalAdjustment(); }; #endif diff --git a/rtgui/ppversion.h b/rtgui/ppversion.h index c1ca3656a..4f4862067 100644 --- a/rtgui/ppversion.h +++ b/rtgui/ppversion.h @@ -2,13 +2,15 @@ #define _PPVERSION_ // This number has to be incremented whenever the PP3 file format is modified or the behaviour of a tool changes -#define PPVERSION 322 +#define PPVERSION 323 #define PPVERSION_AEXP 301 //value of PPVERSION when auto exposure algorithm was modified /* Log of version changes - 322 2015-01-31 - [Wavelet] new tool using wavelet levels + 323 2015-10-05 + [Exposure] Added 'Luminance' tone curve mode + 322 2015-01-31 + [Wavelet] new tool using wavelet levels 321 2014-08-17 [Film Simulation] new tool using HALDCLUT files 320 2014-07-02 (yes, same version number... this is an error due to a wrong version number set in comment of previous change) diff --git a/rtgui/tonecurve.cc b/rtgui/tonecurve.cc index aa0abdbe8..84079224f 100644 --- a/rtgui/tonecurve.cc +++ b/rtgui/tonecurve.cc @@ -123,6 +123,7 @@ ToneCurve::ToneCurve () : FoldableToolPanel(this, "tonecurve", M("TP_EXPOSURE_LA toneCurveMode->append_text (M("TP_EXPOSURE_TCMODE_WEIGHTEDSTD")); toneCurveMode->append_text (M("TP_EXPOSURE_TCMODE_FILMLIKE")); toneCurveMode->append_text (M("TP_EXPOSURE_TCMODE_SATANDVALBLENDING")); + toneCurveMode->append_text (M("TP_EXPOSURE_TCMODE_LUMINANCE")); toneCurveMode->set_active (0); toneCurveMode->set_tooltip_text(M("TP_EXPOSURE_TCMODE_LABEL1")); @@ -148,6 +149,7 @@ ToneCurve::ToneCurve () : FoldableToolPanel(this, "tonecurve", M("TP_EXPOSURE_LA toneCurveMode2->append_text (M("TP_EXPOSURE_TCMODE_WEIGHTEDSTD")); toneCurveMode2->append_text (M("TP_EXPOSURE_TCMODE_FILMLIKE")); toneCurveMode2->append_text (M("TP_EXPOSURE_TCMODE_SATANDVALBLENDING")); + toneCurveMode2->append_text (M("TP_EXPOSURE_TCMODE_LUMINANCE")); toneCurveMode2->set_active (0); toneCurveMode2->set_tooltip_text(M("TP_EXPOSURE_TCMODE_LABEL2")); @@ -224,10 +226,10 @@ void ToneCurve::read (const ProcParams* pp, const ParamsEdited* pedited) { shape->setUnChanged (!pedited->toneCurve.curve); shape2->setUnChanged (!pedited->toneCurve.curve2); if (!pedited->toneCurve.curveMode) { - toneCurveMode->set_active(4); + toneCurveMode->set_active(5); } if (!pedited->toneCurve.curveMode2) { - toneCurveMode2->set_active(4); + toneCurveMode2->set_active(5); } } if (pedited) @@ -292,12 +294,14 @@ void ToneCurve::write (ProcParams* pp, ParamsEdited* pedited) { else if (tcMode == 1) pp->toneCurve.curveMode = ToneCurveParams::TC_MODE_WEIGHTEDSTD; else if (tcMode == 2) pp->toneCurve.curveMode = ToneCurveParams::TC_MODE_FILMLIKE; else if (tcMode == 3) pp->toneCurve.curveMode = ToneCurveParams::TC_MODE_SATANDVALBLENDING; + else if (tcMode == 4) pp->toneCurve.curveMode = ToneCurveParams::TC_MODE_LUMINANCE; tcMode = toneCurveMode2->get_active_row_number(); if (tcMode == 0) pp->toneCurve.curveMode2 = ToneCurveParams::TC_MODE_STD; else if (tcMode == 1) pp->toneCurve.curveMode2 = ToneCurveParams::TC_MODE_WEIGHTEDSTD; else if (tcMode == 2) pp->toneCurve.curveMode2 = ToneCurveParams::TC_MODE_FILMLIKE; else if (tcMode == 3) pp->toneCurve.curveMode2 = ToneCurveParams::TC_MODE_SATANDVALBLENDING; + else if (tcMode == 4) pp->toneCurve.curveMode2 = ToneCurveParams::TC_MODE_LUMINANCE; if (pedited) { pedited->toneCurve.expcomp = expcomp->getEditedState (); @@ -312,8 +316,8 @@ void ToneCurve::write (ProcParams* pp, ParamsEdited* pedited) { pedited->toneCurve.clip = clipDirty; pedited->toneCurve.curve = !shape->isUnChanged (); pedited->toneCurve.curve2 = !shape2->isUnChanged (); - pedited->toneCurve.curveMode = toneCurveMode->get_active_row_number() != 4; - pedited->toneCurve.curveMode2 = toneCurveMode2->get_active_row_number() != 4; + pedited->toneCurve.curveMode = toneCurveMode->get_active_row_number() != 5; + pedited->toneCurve.curveMode2 = toneCurveMode2->get_active_row_number() != 5; } if (pedited) { pedited->toneCurve.method = method->get_active_row_number()!=4; @@ -446,6 +450,19 @@ bool ToneCurve::curveMode2Changed_ () { return false; } +float ToneCurve::blendPipetteValues(CurveEditor *ce, float chan1, float chan2, float chan3) { + // assuming that all the channels are used... + if (ce == shape) { + if (toneCurveMode->get_active_row_number() == 4) + return chan1*0.2126729f + chan2*0.7151521f + chan3*0.0721750f; + } + else if (ce == shape2) { + if (toneCurveMode2->get_active_row_number() == 4) + return chan1*0.2126729f + chan2*0.7151521f + chan3*0.0721750f; + } + return CurveListener::blendPipetteValues(ce, chan1, chan2, chan3); +} + void ToneCurve::adjusterChanged (Adjuster* a, double newval) { // Switch off auto exposure if user changes sliders manually diff --git a/rtgui/tonecurve.h b/rtgui/tonecurve.h index b30cd46ed..830ae6f7d 100644 --- a/rtgui/tonecurve.h +++ b/rtgui/tonecurve.h @@ -84,6 +84,7 @@ class ToneCurve : public ToolParamBlock, public AdjusterListener, public Foldabl void autoOpenCurve (); void setEditProvider (EditDataProvider *provider); + virtual float blendPipetteValues (CurveEditor *ce, float chan1, float chan2, float chan3); void adjusterChanged (Adjuster* a, double newval); void neutral_pressed ();