diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index 450bd3ac6..dfa62c3df 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -1064,7 +1064,10 @@ TP_VIBRANCE_LABEL;Vibrance TP_VIBRANCE_PASTELS;Tons pastels TP_VIBRANCE_PASTSATTOG;Lier Pastels et Saturés TP_VIBRANCE_PROTECTSKINS;Protéger les tons chairs -TP_VIBRANCE_PSTHRESHOLD;Seuil différentiel entre Pastels/Saturés +TP_VIBRANCE_PSTHRESHOLD;Seuil entre tons pastels/saturés +TP_VIBRANCE_PSTHRESHOLD_SATTHRESH;Seuil de saturation +TP_VIBRANCE_PSTHRESHOLD_WEIGTHING;Pondération de la transition pastels/saturés +TP_VIBRANCE_PSTHRESHOLD_TOOLTIP;L'axe vertical represente les tons pastel en bas et le tons saturés en haut.\nL'axe horizontal represente l'échelle de la saturation. TP_VIBRANCE_SATURATED;Tons saturés TP_VIGNETTING_AMOUNT;Quantité TP_VIGNETTING_CENTER;Centre diff --git a/rtdata/languages/default b/rtdata/languages/default index c809d4686..5f742a408 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1075,7 +1075,10 @@ TP_VIBRANCE_LABEL;Vibrance TP_VIBRANCE_PASTELS;Pastel tones TP_VIBRANCE_PASTSATTOG;Link pastel and saturated tones TP_VIBRANCE_PROTECTSKINS;Protect skin tones -TP_VIBRANCE_PSTHRESHOLD;Pastel/Saturated tone differential threshold +TP_VIBRANCE_PSTHRESHOLD;Pastel/saturated tones threshold +TP_VIBRANCE_PSTHRESHOLD_SATTHRESH;Saturation threshold +TP_VIBRANCE_PSTHRESHOLD_WEIGTHING;Pastel/saturated transition's weighting +TP_VIBRANCE_PSTHRESHOLD_TOOLTIP;The vertical axis represents pastel tones at the bottom and saturated tones at the top.\nThe horizontal axis represents the saturation range. TP_VIBRANCE_SATURATED;Saturated tones TP_VIGNETTING_AMOUNT;Amount TP_VIGNETTING_CENTER;Center diff --git a/rtengine/ipvibrance.cc b/rtengine/ipvibrance.cc index 3a27f6824..c3e773884 100644 --- a/rtengine/ipvibrance.cc +++ b/rtengine/ipvibrance.cc @@ -2005,10 +2005,17 @@ void ImProcFunctions::vibrance (LabImage* lab) { HH = new float[width*height]; */ + float chromaPastel = float(params->vibrance.pastels) / 100.0f; + float chromaSatur = float(params->vibrance.saturated) / 100.0f; + float limitpastelsatur = static_cast(params->vibrance.psthreshold.value[ThresholdSelector::TS_TOPLEFT]) / 100.0f; + float transitionweighting = static_cast(params->vibrance.psthreshold.value[ThresholdSelector::TS_BOTTOMLEFT]) / 100.0f; + + bool highlight = params->hlrecovery.enabled;//Get the value if "highlight reconstruction" is activated + #ifdef _DEBUG -#pragma omp parallel default(shared) reduction(+: negat, moreRGB, negsat ,moresat, Munspb, Munsry, Munsgy, Munsrp, depass) if (multiThread) +#pragma omp parallel default(shared) firstprivate(lab, width, chromaPastel, chromaSatur, highlight, limitpastelsatur, transitionweighting) reduction(+: negat, moreRGB, negsat ,moresat, Munspb, Munsry, Munsgy, Munsrp, depass) if (multiThread) #else -#pragma omp parallel default(shared) if (multiThread) +#pragma omp parallel default(shared) firstprivate(lab, width, chromaPastel, chromaSatur, highlight, limitpastelsatur, transitionweighting) if (multiThread) #endif { @@ -2017,9 +2024,6 @@ void ImProcFunctions::vibrance (LabImage* lab) { float fy,fx,fz,x_,y_,z_,Lprov,Lprov1,aprov1,bprov1,aprovn,bprovn,fxx,fyy,fzz,xx_,yy_,zz_; float saturation; TMatrix wiprof = iccStore->workingSpaceInverseMatrix (params->icm.working); - float chromaPastel= (float) params->vibrance.pastels / 100.0f;// - float chromaSatur = (float) params->vibrance.saturated / 100.0f;// - bool highlight = params->hlrecovery.enabled;//Get the value if "highlight reconstruction" is activated //inverse matrix user select double wip[3][3] = { {wiprof[0][0],wiprof[0][1],wiprof[0][2]}, @@ -2030,26 +2034,16 @@ void ImProcFunctions::vibrance (LabImage* lab) { float satredu;//reduct sat in function of skin float sathue[5],sathue2[4];// adjust sat in function of hue float correctionHue; // Munsell's correction - float limitpastelsatur; - float limitpastelsaturbottom;//TS_TOPRIGHT int zone=0; bool allwaysingamut=true; - // sur la ligne ci-dessous, on a acces aux valeurs du seuil via le champs 'value' - // psthreshold est un seuil simple commencant en bas, ce qui signifie que seuls TS_BOTTOMLEFT et TS_TOPLEFT - // sont exploitables - limitpastelsaturbottom=static_cast(params->vibrance.psthreshold.value[ThresholdSelector::TS_BOTTOMLEFT]) / 100.0f; - limitpastelsatur=static_cast(params->vibrance.psthreshold.value[ThresholdSelector::TS_TOPLEFT]) / 100.0f; - - if (limitpastelsatur < 0.07) limitpastelsatur=0.07; -// if (limitpastelsaturbottom < 0.07) limitpastelsaturbottom=0.07; + // Fitting limitpastelsatur into the real 0.07->1.0 range + limitpastelsatur = limitpastelsatur*0.93f + 0.07f; float p0,p1,p2;//adapt limit of pyramid to psThreshold float s0,s1,s2; float maxdp=(limitpastelsatur-0.07)/4.0; -// float maxdp=(limitpastelsaturbottom-0.07)/4.0; - float maxds=(1.0-limitpastelsatur)/4.0; p0=0.07+maxdp; p1=0.07+2.0*maxdp; @@ -2058,8 +2052,25 @@ void ImProcFunctions::vibrance (LabImage* lab) { s1=limitpastelsatur + 2.0*maxds; s2=limitpastelsatur + 3.0*maxds; + float chromamean=0.; + if(chromaPastel != chromaSatur){ + //if sliders pastels and saturated are different: transition with a double linear interpolation: between p2 and limitpastelsatur, and between limitpastelsatur and s0 + //modify the "mean" point in function of double threshold => differential transition + chromamean = maxdp * (chromaSatur-chromaPastel) / (s0-p2) + chromaPastel; + // move chromaMean up or down depending on transitionCtrl + if (transitionweighting > 0.0) { + float _chromamean = chromamean; + chromamean = (chromaSatur-chromamean) * transitionweighting + chromamean; + } + else if (transitionweighting < 0.0) { + float _chromamean = chromamean; + chromamean = (chromamean-chromaPastel) * transitionweighting + chromamean; + } + } + if (settings->verbose) printf("vibrance: p0=%1.2f p1=%1.2f p2=%1.2f s0=%1.2f s1=%1.2f s2=%1.2f\n", p0,p1,p2,s0,s1,s2); - if (settings->verbose) printf("vibrance: pastel=%f satur=%f limit= %1.2f\n",1.0+chromaPastel,1.0+chromaSatur, limitpastelsatur); + if (settings->verbose) printf("vibrance: pastel=%f satur=%f limit= %1.2f chromamean=%0.5f \n",1.0+chromaPastel,1.0+chromaSatur, limitpastelsatur, chromamean); + #pragma omp for schedule(dynamic, 10) for (int i=0; i differential transition - chromamean = (chromaSatur*limitpastelsatur + chromaPastel*limitpastelsaturbottom)/(limitpastelsaturbottom+limitpastelsatur); - chromaPastel_a = (chromaPastel-chromamean)/(p2-limitpastelsatur); - chromaPastel_b = chromaPastel-chromaPastel_a*p2; + if(saturation > p2 && saturation < limitpastelsatur) { - newchromaPastel = chromaPastel_a*saturation + chromaPastel_b; + float chromaPastel_a = (chromaPastel-chromamean)/(p2-limitpastelsatur); + float chromaPastel_b = chromaPastel-chromaPastel_a*p2; + float newchromaPastel = chromaPastel_a*saturation + chromaPastel_b; chmodpastel = newchromaPastel*satredu*sathue[3]; } - - chromaSatur_a=(chromaSatur-chromamean)/(s0-limitpastelsatur); - chromaSatur_b=chromaSatur-chromaSatur_a*s0; - if(saturation < s0 && saturation >=limitpastelsatur) {newchromaSatur=chromaSatur_a*saturation + chromaSatur_b; chmodsat = newchromaSatur*satredu*sathue2[0];} - }// end transition + + if(saturation < s0 && saturation >=limitpastelsatur) { + float chromaSatur_a=(chromaSatur-chromamean)/(s0-limitpastelsatur); + float chromaSatur_b=chromaSatur-chromaSatur_a*s0; + float newchromaSatur=chromaSatur_a*saturation + chromaSatur_b; + chmodsat = newchromaSatur*satredu*sathue2[0]; + } + }// end transition if (saturation <= limitpastelsatur) { if(chmodpastel > 2.0 ) chmodpastel = 2.0; //avoid too big values if(chmodpastel < -0.93) chmodpastel =-0.93; //avoid negative values diff --git a/rtgui/thresholdadjuster.cc b/rtgui/thresholdadjuster.cc index 6ec5ba569..087354671 100644 --- a/rtgui/thresholdadjuster.cc +++ b/rtgui/thresholdadjuster.cc @@ -27,6 +27,21 @@ #define MIN_RESET_BUTTON_HEIGHT 17 +ThresholdAdjuster::ThresholdAdjuster (Glib::ustring label, + double minValueBottom, double maxValueBottom, double defBottom, Glib::ustring labelBottom, unsigned int precisionBottom, + double minValueTop, double maxValueTop, double defTop, Glib::ustring labelTop, unsigned int precisionTop, + ThresholdCurveProvider* curveProvider, bool editedCheckBox) + : tSelector(minValueBottom, maxValueBottom, defBottom, labelBottom, precisionBottom, minValueTop, maxValueTop, defTop, labelTop, precisionTop, curveProvider) + +{ + initialDefaultVal[ThresholdSelector::TS_BOTTOMLEFT] = defBottom; + initialDefaultVal[ThresholdSelector::TS_TOPLEFT] = defTop; + initialDefaultVal[ThresholdSelector::TS_BOTTOMRIGHT] = 0.; // unused + initialDefaultVal[ThresholdSelector::TS_TOPRIGHT] = 0.; // unused + + initObject (label, editedCheckBox); +} + ThresholdAdjuster::ThresholdAdjuster (Glib::ustring label, double minValue, double maxValue, double defBottom, double defTop, unsigned int precision, bool startAtOne, bool editedCheckBox) : tSelector(minValue, maxValue, defBottom, defTop, precision, startAtOne) @@ -196,11 +211,11 @@ void ThresholdAdjuster::setValue (double bottomLeft, double topLeft, double bott afterReset = false; } -void ThresholdAdjuster::getValue (Glib::ustring& bottom, Glib::ustring& top) { +inline void ThresholdAdjuster::getValue (Glib::ustring& bottom, Glib::ustring& top) { tSelector.getPositions (bottom, top); } -void ThresholdAdjuster::getValue (Glib::ustring& bottomLeft, Glib::ustring& topLeft, Glib::ustring& bottomRight, Glib::ustring& topRight) { +inline void ThresholdAdjuster::getValue (Glib::ustring& bottomLeft, Glib::ustring& topLeft, Glib::ustring& bottomRight, Glib::ustring& topRight) { tSelector.getPositions (bottomLeft, topLeft, bottomRight, topRight); } @@ -213,6 +228,11 @@ bool ThresholdAdjuster::notifyListener () { return false; } +inline void ThresholdAdjuster::setBgCurveProvider (ThresholdCurveProvider* provider) { + tSelector.setBgCurveProvider(provider); +} + + void ThresholdAdjuster::setEnabled (bool enabled) { tSelector.set_sensitive (enabled); @@ -283,9 +303,19 @@ void ThresholdAdjuster::sendToListener () { } } +void ThresholdAdjuster::set_tooltip_markup(const Glib::ustring& markup) { + tSelector.set_tooltip_markup(markup); +} + +void ThresholdAdjuster::set_tooltip_text(const Glib::ustring& text) { + tSelector.set_tooltip_text(text); +} + /* For better readability, this method create the history string of the parameter column, * so that the parameters list can be read in a more logical way (i.e. corresponding * to the startAtOne field) + * + * If separatedMode==true, the top slider is assumed to be the primary slider, then the bottom slider as the second one */ Glib::ustring ThresholdAdjuster::getHistoryString () { if (tSelector.isDouble()) { @@ -296,6 +326,6 @@ Glib::ustring ThresholdAdjuster::getHistoryString () { else { Glib::ustring b, t; tSelector.getPositions(b, t); - return Glib::ustring::compose(tSelector.isStartAtOne()?"%2, %1":"%1, %2", b, t); + return Glib::ustring::compose(tSelector.isStartAtOne()||separatedMode?"%2, %1":"%1, %2", b, t); } } diff --git a/rtgui/thresholdadjuster.h b/rtgui/thresholdadjuster.h index e589b5f2f..8b8c965ae 100644 --- a/rtgui/thresholdadjuster.h +++ b/rtgui/thresholdadjuster.h @@ -64,6 +64,7 @@ class ThresholdAdjuster : public Gtk::VBox { bool afterReset; bool blocked; bool addMode; + bool separatedMode; int delay; double shapeValue (double a); @@ -73,8 +74,16 @@ class ThresholdAdjuster : public Gtk::VBox { public: + // Custom Threshold widget with 2 cursors (Top and Bottom) and a custom curve provided by the instanciator, + // each cursor having his own range, default value and precision + ThresholdAdjuster (Glib::ustring label, + double minValueBottom, double maxValueBottom, double defBottom, Glib::ustring labelBottom, unsigned int precisionBottom, + double minValueTop, double maxValueTop, double defTop, Glib::ustring labelTop, unsigned int precisionTop, + ThresholdCurveProvider* curveProvider, bool editedCheckBox=false); + // Simple Threshold widget with 2 linked sliders (0->1 or 1->0) ThresholdAdjuster (Glib::ustring label, double minValue, double maxValue, double defBottom, double defTop, unsigned int precision, bool startAtOne, bool editedCheckBox=false); + // Simple Threshold widget with 4 linked sliders by pairs (0->1->0 or 1->0->1) ThresholdAdjuster (Glib::ustring label, double minValue, double maxValue, double defBottomLeft, double defTopLeft, double defBottomRight, double defTopRight, unsigned int precision, bool startAtOne, bool editedCheckBox=false); @@ -82,8 +91,13 @@ class ThresholdAdjuster : public Gtk::VBox { virtual ~ThresholdAdjuster (); void setAdjusterListener (ThresholdAdjusterListener* alistener) { adjusterListener = alistener; } + void setBgCurveProvider (ThresholdCurveProvider* provider); + ThresholdSelector* getThresholdSelector() { return &tSelector; }; + template - rtengine::procparams::Threshold getValue () { return tSelector.getPositions(); } + rtengine::procparams::Threshold getValue () { + return tSelector.getPositions(); + } void getValue (double& bottom, double& top); void getValue (double& bottomLeft, double& topLeft, double& bottomRight, double& topRight); void getValue (int& bottom, int& top); @@ -91,9 +105,7 @@ class ThresholdAdjuster : public Gtk::VBox { void getValue (Glib::ustring& bottom, Glib::ustring& top); void getValue (Glib::ustring& bottomLeft, Glib::ustring& topLeft, Glib::ustring& bottomRight, Glib::ustring& topRight); template - void setValue (const rtengine::procparams::Threshold &tValues) { - tSelector.setPositions(tValues); - } + void setValue (const rtengine::procparams::Threshold &tValues) { tSelector.setPositions(tValues); } void setValue (double bottom, double top); void setValue (double bottomLeft, double topLeft, double bottomRight, double topRight); void setEnabled (bool enabled); @@ -114,6 +126,9 @@ class ThresholdAdjuster : public Gtk::VBox { void resetPressed (GdkEventButton* event); void editedToggled (); Glib::ustring getHistoryString (); + void set_tooltip_markup(const Glib::ustring& markup); + // this set_tooltip_text method is to set_tooltip_markup, and text can contain markups + void set_tooltip_text(const Glib::ustring& text); }; #endif diff --git a/rtgui/thresholdselector.cc b/rtgui/thresholdselector.cc index 5dcc39843..ce04e90ff 100644 --- a/rtgui/thresholdselector.cc +++ b/rtgui/thresholdselector.cc @@ -20,17 +20,45 @@ #include "thresholdselector.h" #include "multilangmgr.h" #include -#include #include "mycurve.h" +ThresholdSelector::ThresholdSelector(double minValueBottom, double maxValueBottom, double defBottom, Glib::ustring labelBottom, unsigned int precisionBottom, + double minValueTop, double maxValueTop, double defTop, Glib::ustring labelTop, unsigned int precisionTop, + ThresholdCurveProvider* curveProvider) { + positions[TS_BOTTOMLEFT] = defPos[TS_BOTTOMLEFT] = defBottom; + positions[TS_TOPLEFT] = defPos[TS_TOPLEFT] = defTop; + positions[TS_BOTTOMRIGHT] = defPos[TS_BOTTOMRIGHT] = 0; // unused + positions[TS_TOPRIGHT] = defPos[TS_TOPRIGHT] = 0; // unused + this->precisionTop = precisionTop; + this->precisionBottom = precisionBottom; + doubleThresh = false; + + separatedLabelBottom = labelBottom; + separatedLabelTop = labelTop; + + bgCurveProvider = curveProvider; + separatedSliders = true; + initalEq1 = false; // unused + minValBottom = minValueBottom; + maxValBottom = maxValueBottom; + minValTop = minValueTop; + maxValTop = maxValueTop; + + initValues (); +} + ThresholdSelector::ThresholdSelector(double minValue, double maxValue, double defBottom, double defTop, unsigned int precision, bool startAtOne) { positions[TS_BOTTOMLEFT] = defPos[TS_BOTTOMLEFT] = defBottom; positions[TS_TOPLEFT] = defPos[TS_TOPLEFT] = defTop; positions[TS_BOTTOMRIGHT] = defPos[TS_BOTTOMRIGHT] = maxValue; positions[TS_TOPRIGHT] = defPos[TS_TOPRIGHT] = maxValue; - this->precision = precision; + this->precisionTop = precision; + this->precisionBottom = precision; doubleThresh = false; + separatedLabelBottom = ""; + separatedLabelTop = ""; + #ifndef NDEBUG if (startAtOne) { assert (defBottom >= defTop); @@ -42,9 +70,17 @@ ThresholdSelector::ThresholdSelector(double minValue, double maxValue, double de assert (defBottom >= minValue); assert (defTop <= maxValue); } + assert(minValue < maxValue); #endif - initValues (minValue, maxValue, startAtOne); + bgCurveProvider = NULL; + separatedSliders = false; + initalEq1 = startAtOne; + minValTop = minValBottom = minValue; + maxValTop = maxValBottom = maxValue; + + initValues (); + } ThresholdSelector::ThresholdSelector(double minValue, double maxValue, double defBottomLeft, double defTopLeft, double defBottomRight, double defTopRight, unsigned int precision, bool startAtOne) { @@ -52,9 +88,13 @@ ThresholdSelector::ThresholdSelector(double minValue, double maxValue, double de positions[TS_TOPLEFT] = defPos[TS_TOPLEFT] = defTopLeft; positions[TS_BOTTOMRIGHT] = defPos[TS_BOTTOMRIGHT] = defBottomRight; positions[TS_TOPRIGHT] = defPos[TS_TOPRIGHT] = defTopRight; - this->precision = precision; + this->precisionTop = precision; + this->precisionBottom = precision; doubleThresh = true; + separatedLabelBottom = ""; + separatedLabelTop = ""; + #ifndef NDEBUG if (startAtOne) { assert (minValue <= defTopLeft); @@ -70,20 +110,25 @@ ThresholdSelector::ThresholdSelector(double minValue, double maxValue, double de assert (defTopRight <= defBottomRight); assert (defBottomRight <= maxValue); } + assert(minValue < maxValue); #endif - initValues (minValue, maxValue, startAtOne); + bgCurveProvider = NULL; + separatedSliders = false; + initalEq1 = startAtOne; + minValTop = minValBottom = minValue; + maxValTop = maxValBottom = maxValue; + + initValues (); } -void ThresholdSelector::initValues (double minValue, double maxValue, bool startAtOne) { - assert(minValue <= maxValue); - initalEq1 = startAtOne; - minVal = minValue; - maxVal = maxValue; +void ThresholdSelector::initValues () { + + additionalTTip = ""; oldLitCursor = litCursor = TS_UNDEFINED; movedCursor = TS_UNDEFINED; secondaryMovedCursor = TS_UNDEFINED; - set_size_request (-1, 20); + set_size_request (-1, 30); add_events(Gdk::LEAVE_NOTIFY_MASK); set_name("ThresholdSelector"); set_can_focus(false); @@ -96,7 +141,7 @@ void ThresholdSelector::initValues (double minValue, double maxValue, bool start */ void ThresholdSelector::setPositions (double bottom, double top) { - setPositions(bottom, top, maxVal, maxVal); + setPositions(bottom, top, maxValBottom, maxValTop); } /* @@ -120,7 +165,7 @@ void ThresholdSelector::setPositions (double bottomLeft, double topLeft, double void ThresholdSelector::setDefaults (double bottom, double top) { - setDefaults(bottom, top, maxVal, maxVal); + setDefaults(bottom, top, maxValBottom, maxValTop); } void ThresholdSelector::setDefaults (double bottomLeft, double topLeft, double bottomRight, double topRight) { @@ -133,21 +178,6 @@ void ThresholdSelector::setDefaults (double bottomLeft, double topLeft, double b } } -void ThresholdSelector::getPositions (Glib::ustring& bottom, Glib::ustring& top) { - - - bottom = Glib::ustring::format(std::fixed, std::setprecision(precision),positions[TS_BOTTOMLEFT]); - top = Glib::ustring::format(std::fixed, std::setprecision(precision),positions[TS_TOPLEFT]); -} - -void ThresholdSelector::getPositions (Glib::ustring& bottomLeft, Glib::ustring& topLeft, Glib::ustring& bottomRight, Glib::ustring& topRight) { - - bottomLeft = Glib::ustring::format(std::fixed, std::setprecision(precision),positions[TS_BOTTOMLEFT]); - topLeft = Glib::ustring::format(std::fixed, std::setprecision(precision),positions[TS_TOPLEFT]); - bottomRight = Glib::ustring::format(std::fixed, std::setprecision(precision),positions[TS_BOTTOMRIGHT]); - topRight = Glib::ustring::format(std::fixed, std::setprecision(precision),positions[TS_TOPRIGHT]); -} - void ThresholdSelector::setBgGradient (const std::vector &milestones) { bgGradient.clear(); bgGradient = milestones; @@ -175,9 +205,10 @@ bool ThresholdSelector::on_expose_event(GdkEventExpose* event) { int iw = w-wslider-2*hb; // inner width (excluding padding for tabs) - for (int i=0; i<4; i++) { - positions01[i] = to01(positions[i]); - } + positions01[TS_BOTTOMLEFT] = to01(TS_BOTTOMLEFT); + positions01[TS_TOPLEFT] = to01(TS_TOPLEFT); + positions01[TS_BOTTOMRIGHT] = to01(TS_BOTTOMRIGHT); + positions01[TS_TOPRIGHT] = to01(TS_TOPRIGHT); Gtk::StateType state = !is_sensitive() ? Gtk::STATE_INSENSITIVE : Gtk::STATE_NORMAL; Glib::RefPtr style = get_style(); @@ -208,24 +239,52 @@ bool ThresholdSelector::on_expose_event(GdkEventExpose* event) { } // draw curve - double yStart = initalEq1 ? double(int(float(h)*1.5f/7.f))+1.5 : double(int(float(h)*5.5f/7.f))-0.5; - double yEnd = initalEq1 ? double(int(float(h)*5.5f/7.f))-0.5 : double(int(float(h)*1.5f/7.f))+1.5; - ThreshCursorId p[4]; - if (initalEq1) { p[0] = TS_TOPLEFT; p[1] = TS_BOTTOMLEFT; p[2] = TS_BOTTOMRIGHT; p[3] = TS_TOPRIGHT; } - else { p[0] = TS_BOTTOMLEFT; p[1] = TS_TOPLEFT; p[2] = TS_TOPRIGHT; p[3] = TS_BOTTOMRIGHT; } - if (positions[p[1]] > minVal) - cr->move_to (hb+hwslider, yStart); - else - cr->move_to (hb+hwslider, yEnd); - if (positions[p[0]] > minVal) - cr->line_to (hb+hwslider+iw*positions01[p[0]]+0.5, yStart); - if (positions[p[1]] > minVal) - cr->line_to (hb+hwslider+iw*positions01[p[1]]+0.5, yEnd); - cr->line_to (hb+hwslider+iw*positions01[p[2]]+0.5, yEnd); - if (doubleThresh && positions[p[2]] < maxVal) { - cr->line_to (hb+hwslider+iw*positions01[p[3]]+0.5, yStart); - if (positions[p[3]] < maxVal) + + if (bgCurveProvider) { + double yStart = double(int(float(h)*5.5f/7.f))-0.5; + double yEnd = double(int(float(h)*1.5f/7.f))+1.5; + + std::vector pts = bgCurveProvider->getCurvePoints(this); // the values sent by the provider are not checked (assumed to be correct) + if (pts.size() >= 4) { + std::vector::iterator i=pts.begin(); + double x = *i; i++; + double y = *i; i++; + cr->move_to (hb+hwslider+iw*x+0.5, (yEnd-yStart)*y+yStart); + + for (; iline_to (hb+hwslider+iw*x+0.5, (yEnd-yStart)*y+yStart); + } + } + else { + // Draw a straight line because not enough points has been sent + double yStart = double(int(float(h)*5.5f/7.f))-0.5; + cr->move_to (hb+hwslider+0.5, yStart); cr->line_to (hb+hwslider+iw+0.5, yStart); + } + + } + else { + double yStart = initalEq1 ? double(int(float(h)*1.5f/7.f))+1.5 : double(int(float(h)*5.5f/7.f))-0.5; + double yEnd = initalEq1 ? double(int(float(h)*5.5f/7.f))-0.5 : double(int(float(h)*1.5f/7.f))+1.5; + ThreshCursorId p[4]; + if (initalEq1) { p[0] = TS_TOPLEFT; p[1] = TS_BOTTOMLEFT; p[2] = TS_BOTTOMRIGHT; p[3] = TS_TOPRIGHT; } + else { p[0] = TS_BOTTOMLEFT; p[1] = TS_TOPLEFT; p[2] = TS_TOPRIGHT; p[3] = TS_BOTTOMRIGHT; } + if (positions[p[1]] > minValTop) // we use minValTop since if this block is executed, it means that we are in a simple Threshold where both bottom and top range are the same + cr->move_to (hb+hwslider, yStart); + else + cr->move_to (hb+hwslider, yEnd); + if (positions[p[0]] > minValTop) + cr->line_to (hb+hwslider+iw*positions01[p[0]]+0.5, yStart); + if (positions[p[1]] > minValTop) + cr->line_to (hb+hwslider+iw*positions01[p[1]]+0.5, yEnd); + cr->line_to (hb+hwslider+iw*positions01[p[2]]+0.5, yEnd); + if (doubleThresh && positions[p[2]] < maxValTop) { + cr->line_to (hb+hwslider+iw*positions01[p[3]]+0.5, yStart); + if (positions[p[3]] < maxValTop) + cr->line_to (hb+hwslider+iw+0.5, yStart); + } } if (is_sensitive() && bgGradient.size()>1) { // draw surrounding curve @@ -371,14 +430,18 @@ bool ThresholdSelector::on_motion_notify_event (GdkEventMotion* event) { if (movedCursor != TS_UNDEFINED) { // user is moving a cursor or two - double minBound, maxBound; + double minBound, maxBound, dRange; findSecondaryMovedCursor(event->state); // computing the boundaries findBoundaries(minBound, maxBound); - double dX = ( (event->x-tmpX)*(maxVal-minVal) )/( w-2*hb ); + if (movedCursor==TS_BOTTOMLEFT || movedCursor==TS_BOTTOMRIGHT) + dRange = maxValBottom-minValBottom; + else + dRange = maxValTop-minValTop; + double dX = ( (event->x-tmpX)*dRange )/( w-2*hb ); // slow motion if CTRL is pressed if (event->state & Gdk::CONTROL_MASK) dX *= 0.05; @@ -429,7 +492,8 @@ void ThresholdSelector::findLitCursor(int posX, int posY) { litCursor = TS_TOPLEFT; if (doubleThresh) { - double cursorX = (posX-hb)*(maxVal-minVal)/(w-2*hb)+minVal; + // we use minValTop since if this block is executed, it means that we are in a simple Threshold where both bottom and top range are the same + double cursorX = (posX-hb)*(maxValTop-minValTop)/(w-2*hb)+minValTop; if (cursorX>positions[TS_TOPRIGHT] || abs(cursorX-positions[TS_TOPRIGHT]) < abs(cursorX-positions[TS_TOPLEFT])) litCursor = TS_TOPRIGHT; @@ -440,7 +504,8 @@ void ThresholdSelector::findLitCursor(int posX, int posY) { if (posX > 0 && posX < w) { litCursor = TS_BOTTOMLEFT; if (doubleThresh) { - double cursorX = (posX-hb)*(maxVal-minVal)/(w-2*hb)+minVal; + // we use minValTop since if this block is executed, it means that we are in a simple Threshold where both bottom and top range are the same + double cursorX = (posX-hb)*(maxValTop-minValTop)/(w-2*hb)+minValTop; if (cursorX>positions[TS_BOTTOMRIGHT] || abs(cursorX-positions[TS_BOTTOMRIGHT]) < abs(cursorX-positions[TS_BOTTOMLEFT])) litCursor = TS_BOTTOMRIGHT; @@ -453,55 +518,75 @@ void ThresholdSelector::findBoundaries(double &min, double &max) { switch (movedCursor) { case (TS_BOTTOMLEFT): - if (initalEq1) { - min = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_TOPLEFT] : minVal+(positions[TS_BOTTOMLEFT]-positions[TS_TOPLEFT]); + if (separatedSliders) { + if (movedCursor == TS_BOTTOMLEFT) { + min = minValBottom; + max = maxValBottom; + } + else if (movedCursor == TS_TOPLEFT) { + min = minValTop; + max = maxValTop; + } + } + else if (initalEq1) { + min = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_TOPLEFT] : minValTop+(positions[TS_BOTTOMLEFT]-positions[TS_TOPLEFT]); max = positions[TS_BOTTOMRIGHT]; } else { - min = minVal; + min = minValTop; max = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_TOPLEFT] : positions[TS_TOPRIGHT]-(positions[TS_TOPLEFT]-positions[TS_BOTTOMLEFT]); } break; case (TS_TOPLEFT): - if (initalEq1) { - min = minVal; + if (separatedSliders) { + if (movedCursor == TS_BOTTOMLEFT) { + min = minValBottom; + max = maxValBottom; + } + else if (movedCursor == TS_TOPLEFT) { + min = minValTop; + max = maxValTop; + } + } + else if (initalEq1) { + min = minValTop; max = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_BOTTOMLEFT] : positions[TS_BOTTOMRIGHT]-(positions[TS_BOTTOMLEFT]-positions[TS_TOPLEFT]); } else { - min = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_BOTTOMLEFT] : minVal+(positions[TS_TOPLEFT]-positions[TS_BOTTOMLEFT]); + min = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_BOTTOMLEFT] : minValTop+(positions[TS_TOPLEFT]-positions[TS_BOTTOMLEFT]); max = positions[TS_TOPRIGHT]; } break; case (TS_BOTTOMRIGHT): if (initalEq1) { min = positions[TS_BOTTOMLEFT]; - max = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_TOPRIGHT] : maxVal-(positions[TS_TOPRIGHT]-positions[TS_BOTTOMRIGHT]); + max = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_TOPRIGHT] : maxValTop-(positions[TS_TOPRIGHT]-positions[TS_BOTTOMRIGHT]); } else { min = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_TOPRIGHT] : positions[TS_TOPLEFT]+(positions[TS_BOTTOMRIGHT]-positions[TS_TOPRIGHT]); - max = maxVal; + max = maxValTop; } break; case (TS_TOPRIGHT): if (initalEq1) { min = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_BOTTOMRIGHT] : positions[TS_BOTTOMLEFT]+(positions[TS_TOPRIGHT]-positions[TS_BOTTOMRIGHT]); - max = maxVal; + max = maxValTop; } else { min = positions[TS_TOPLEFT]; - max = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_BOTTOMRIGHT] : maxVal-(positions[TS_BOTTOMRIGHT]-positions[TS_TOPRIGHT]); + max = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_BOTTOMRIGHT] : maxValTop-(positions[TS_BOTTOMRIGHT]-positions[TS_TOPRIGHT]); } break; default: - min = minVal; - max = maxVal; + min = minValTop; + max = maxValTop; break; } } void ThresholdSelector::findSecondaryMovedCursor(guint state) { secondaryMovedCursor = TS_UNDEFINED; - if (!(state & Gdk::SHIFT_MASK)) { + if (!separatedSliders && !(state & Gdk::SHIFT_MASK)) { switch (movedCursor) { case (TS_BOTTOMLEFT): secondaryMovedCursor = TS_TOPLEFT; @@ -537,34 +622,79 @@ void ThresholdSelector::reset () { queue_draw (); } -inline double ThresholdSelector::to01(double value) { +inline double ThresholdSelector::to01(ThreshCursorId cursorId) { - double rVal = (value-minVal)/(maxVal-minVal); + double rVal; + if (cursorId==TS_BOTTOMLEFT || cursorId==TS_BOTTOMRIGHT) + rVal = (positions[cursorId]-minValBottom)/(maxValBottom-minValBottom); + else + rVal = (positions[cursorId]-minValTop)/(maxValTop-minValTop); if (rVal < 0.) rVal = 0.; else if (rVal > 1.) rVal = 1.; return rVal; } +inline void ThresholdSelector::setBgCurveProvider (ThresholdCurveProvider* provider) { + bgCurveProvider = provider; +} + +inline void ThresholdSelector::setSeparatedSliders(bool separated) { + separatedSliders = separated; +} + +inline bool ThresholdSelector::getSeparatedSliders() { + return separatedSliders; +} + void ThresholdSelector::updateTooltip() { Glib::ustring tTip; - if (doubleThresh) - tTip = Glib::ustring::compose("%1: %2 %3: %4\n%5: %6 %7: %8\n%9", - M("THRESHOLDSELECTOR_TL"), Glib::ustring::format(std::fixed, std::setprecision(precision), positions[TS_TOPLEFT]), - M("THRESHOLDSELECTOR_TR"), Glib::ustring::format(std::fixed, std::setprecision(precision), positions[TS_TOPRIGHT]), - M("THRESHOLDSELECTOR_BL"), Glib::ustring::format(std::fixed, std::setprecision(precision), positions[TS_BOTTOMLEFT]), - M("THRESHOLDSELECTOR_BR"), Glib::ustring::format(std::fixed, std::setprecision(precision), positions[TS_BOTTOMRIGHT]), - M("THRESHOLDSELECTOR_HINT") + if (doubleThresh) { + tTip = Glib::ustring::compose("%1: %2 %3: %4\n%5: %6 %7: %8", + M("THRESHOLDSELECTOR_TL"), Glib::ustring::format(std::fixed, std::setprecision(precisionTop), positions[TS_TOPLEFT]), + M("THRESHOLDSELECTOR_TR"), Glib::ustring::format(std::fixed, std::setprecision(precisionTop), positions[TS_TOPRIGHT]), + M("THRESHOLDSELECTOR_BL"), Glib::ustring::format(std::fixed, std::setprecision(precisionBottom), positions[TS_BOTTOMLEFT]), + M("THRESHOLDSELECTOR_BR"), Glib::ustring::format(std::fixed, std::setprecision(precisionBottom), positions[TS_BOTTOMRIGHT]) ); - else - tTip = Glib::ustring::compose("%1: %2\n%3: %4\n%5", - M("THRESHOLDSELECTOR_T"), Glib::ustring::format(std::fixed, std::setprecision(precision), positions[TS_TOPLEFT]), - M("THRESHOLDSELECTOR_B"), Glib::ustring::format(std::fixed, std::setprecision(precision), positions[TS_BOTTOMLEFT]), - M("THRESHOLDSELECTOR_HINT") + if (!additionalTTip.empty()) + tTip += Glib::ustring::compose("\n\n%1", additionalTTip); + tTip += Glib::ustring::compose("\n\n%1", M("THRESHOLDSELECTOR_HINT")); + } + else if (separatedSliders) { + tTip = Glib::ustring::compose("%1: %2\n%3: %4", + separatedLabelTop, Glib::ustring::format(std::fixed, std::setprecision(precisionTop), positions[TS_TOPLEFT]), + separatedLabelBottom, Glib::ustring::format(std::fixed, std::setprecision(precisionBottom), positions[TS_BOTTOMLEFT]) ); - set_tooltip_markup(tTip); + if (!additionalTTip.empty()) + tTip += Glib::ustring::compose("\n\n%1", additionalTTip); + } + else { + tTip = Glib::ustring::compose("%1: %2\n%3: %4", + M("THRESHOLDSELECTOR_T"), Glib::ustring::format(std::fixed, std::setprecision(precisionTop), positions[TS_TOPLEFT]), + M("THRESHOLDSELECTOR_B"), Glib::ustring::format(std::fixed, std::setprecision(precisionBottom), positions[TS_BOTTOMLEFT]) + ); + if (!additionalTTip.empty()) + tTip += Glib::ustring::compose("\n\n%1", additionalTTip); + tTip += Glib::ustring::compose("\n\n%1", M("THRESHOLDSELECTOR_HINT")); + } + Gtk::Widget::set_tooltip_markup(tTip); } sigc::signal ThresholdSelector::signal_value_changed() { return sig_val_changed; } + +double ThresholdSelector::shapePositionValue (ThreshCursorId cursorId) { + unsigned int precision = (cursorId==TS_BOTTOMLEFT || cursorId==TS_BOTTOMRIGHT) ? precisionBottom : precisionTop; + return round(positions[cursorId]*pow(double(10), precision)) / pow(double(10), precision); +} + +void ThresholdSelector::set_tooltip_markup(const Glib::ustring& markup) { + additionalTTip = markup; + updateTooltip(); +} + +void ThresholdSelector::set_tooltip_text(const Glib::ustring& text) { + additionalTTip = text; + updateTooltip(); +} diff --git a/rtgui/thresholdselector.h b/rtgui/thresholdselector.h index 0308ad9d6..482213e94 100644 --- a/rtgui/thresholdselector.h +++ b/rtgui/thresholdselector.h @@ -21,6 +21,21 @@ #include "guiutils.h" #include "../rtengine/procparams.h" +#include + +class ThresholdSelector; + +/* + * This class let the instanciator to provide the background curve + */ +class ThresholdCurveProvider { + public: + /* + * The curve provider has to send back a list of point (at least 2 points) in the [0.0 ; 1.0] range + * for both X and Y axis; X and Y values are streamlined ( X1, Y1, X2, Y2, X3, Y3, ...) + */ + virtual std::vector getCurvePoints(ThresholdSelector* tAdjuster) const = 0; +}; /* * This widget will let you select a linearly variable threshold, creating a ramp up @@ -36,6 +51,11 @@ * * Please note that the values are related to the cursors, depending on their position * on the graph. E.g. the "bottomLeft" value is related to the bottom left cursor. + * + * It is also possible to have a threshold with 2 totally independent cursors, each one having his own range, + * man/max/default values and precision. This let developers create their own threshold curve, that they will + * have to provide through the + * */ class ThresholdSelector : public Gtk::DrawingArea { @@ -57,17 +77,24 @@ class ThresholdSelector : public Gtk::DrawingArea { Glib::RefPtr gc_; Glib::RefPtr backBuffer; std::vector bgGradient; + ThresholdCurveProvider* bgCurveProvider; + Glib::ustring additionalTTip; + Glib::ustring separatedLabelBottom; // Label for the bottom cursor, displayed if separatedSliders==true only + Glib::ustring separatedLabelTop; // Label for the top cursor, displayed if separatedSliders==true only + bool separatedSliders; // If true, the Top and Bottom sliders are totally separate and can be drag through the full range; for simple threshold only! bool doubleThresh; // If true: there curve is a double threshold (0 to 1 to 0, or 1 to 0 to 1). bool initalEq1; // If true: the curve start at 1 (top); if false: the curve start at 0 (bottom) - unsigned int precision; // Decimal number if this object has to handle "double" values + unsigned int precisionTop; // Decimal number if this object has to handle "double" values, for the Top slider + unsigned int precisionBottom; // Decimal number if this object has to handle "double" values, for the Bottom slider ThreshCursorId litCursor; ThreshCursorId oldLitCursor; double boundary1[2], boundary2[2]; double tmpX, tmpPos; ThreshCursorId movedCursor, secondaryMovedCursor; - double minVal, maxVal; + double minValTop, maxValTop; + double minValBottom, maxValBottom; double defPos[4]; double positions[4]; unsigned short wslider; @@ -75,22 +102,24 @@ class ThresholdSelector : public Gtk::DrawingArea { const static int hb = 3; // horizontal border const static int vb = 2; // vertical border - void initValues (double minValue, double maxValue, bool startAtOne); + void initValues (); void findLitCursor(int posX, int posY); void findSecondaryMovedCursor(guint state); void findBoundaries(double &min, double &max); - double to01(double value); + double to01(ThreshCursorId cursorId); void updateTooltip(); public: sigc::signal signal_value_changed(); + ThresholdSelector(double minValueBottom, double maxValueBottom, double defBottom, Glib::ustring labelBottom, unsigned int precisionBottom, + double minValueTop, double maxValueTop, double defTop, Glib::ustring labelTop, unsigned int precisionTop, + ThresholdCurveProvider* curveProvider); ThresholdSelector(double minValue, double maxValue, double defBottom, double defTop, unsigned int precision, bool startAtOne); ThresholdSelector(double minValue, double maxValue, double defBottomLeft, double defTopLeft, double defBottomRight, double defTopRight, unsigned int precision, bool startAtOne); - double shapeValue (double value) { return round(value*pow(double(10), precision)) / pow(double(10), precision); } - + double shapePositionValue (ThreshCursorId cursorId); template void setDefaults (const rtengine::procparams::Threshold &t) { defPos[TS_BOTTOMLEFT] = double(t.value[0]); // should we use shapeValue() ? @@ -100,10 +129,8 @@ class ThresholdSelector : public Gtk::DrawingArea { defPos[TS_TOPRIGHT] = double(t.value[3]); } } - void setDefaults (double bottom, double top); void setDefaults (double bottomLeft, double topLeft, double bottomRight, double topRight); - template void setPositions (const rtengine::procparams::Threshold &tValues) { positions[TS_BOTTOMLEFT] = static_cast(tValues.value[TS_BOTTOMLEFT]); @@ -122,28 +149,42 @@ class ThresholdSelector : public Gtk::DrawingArea { rtengine::procparams::Threshold getPositions () { if (doubleThresh) { rtengine::procparams::Threshold rThresh( - static_cast(shapeValue(positions[TS_BOTTOMLEFT])), - static_cast(shapeValue(positions[TS_TOPLEFT])), - static_cast(shapeValue(positions[TS_BOTTOMRIGHT])), - static_cast(shapeValue(positions[TS_TOPRIGHT])), + static_cast(shapePositionValue(TS_BOTTOMLEFT)), + static_cast(shapePositionValue(TS_TOPLEFT)), + static_cast(shapePositionValue(TS_BOTTOMRIGHT)), + static_cast(shapePositionValue(TS_TOPRIGHT)), initalEq1 ); return rThresh; } else { rtengine::procparams::Threshold rThresh( - static_cast(shapeValue(positions[TS_BOTTOMLEFT])), - static_cast(shapeValue(positions[TS_TOPLEFT])), + static_cast(shapePositionValue(TS_BOTTOMLEFT)), + static_cast(shapePositionValue(TS_TOPLEFT)), initalEq1 ); return rThresh; } } - void getPositions (Glib::ustring& bottom, Glib::ustring& top); - void getPositions (Glib::ustring& bottomLeft, Glib::ustring& topLeft, Glib::ustring& bottomRight, Glib::ustring& topRight); + template + void getPositions (T &bottom, T &top) { + bottom = static_cast(shapePositionValue(TS_BOTTOMLEFT)); + top = static_cast(shapePositionValue(TS_TOPLEFT)); + } + template + void getPositions (T &bottomLeft, T &topLeft, T &bottomRight, T &topRight) { + bottomLeft = static_cast(shapePositionValue(TS_BOTTOMLEFT)); + topLeft = static_cast(shapePositionValue(TS_TOPLEFT)); + bottomRight = static_cast(shapePositionValue(TS_BOTTOMRIGHT)); + topRight = static_cast(shapePositionValue(TS_TOPRIGHT)); + } + + void setSeparatedSliders(bool separated); + bool getSeparatedSliders(); void setBgGradient (const std::vector &milestones); + void setBgCurveProvider (ThresholdCurveProvider* provider); bool isStartAtOne() { return initalEq1; } bool isDouble() { return doubleThresh; } void on_realize (); @@ -153,9 +194,27 @@ class ThresholdSelector : public Gtk::DrawingArea { bool on_motion_notify_event (GdkEventMotion* event); bool on_leave_notify_event (GdkEventCrossing* event); void styleChanged (const Glib::RefPtr& style); - unsigned int getPrecision () { return precision; } + unsigned int getPrecision () { return precisionTop; } void reset (); + void set_tooltip_markup(const Glib::ustring& markup); + // this set_tooltip_text method is to set_tooltip_markup, and text can contain markups + void set_tooltip_text(const Glib::ustring& text); }; +template<> +inline void ThresholdSelector::getPositions (Glib::ustring& bottom, Glib::ustring& top) { + bottom = Glib::ustring::format(std::fixed, std::setprecision(precisionBottom), shapePositionValue(TS_BOTTOMLEFT)); + top = Glib::ustring::format(std::fixed, std::setprecision(precisionTop), shapePositionValue(TS_TOPLEFT)); +} + +template<> +inline void ThresholdSelector::getPositions (Glib::ustring& bottomLeft, Glib::ustring& topLeft, Glib::ustring& bottomRight, Glib::ustring& topRight) { + + bottomLeft = Glib::ustring::format(std::fixed, std::setprecision(precisionBottom), shapePositionValue(TS_BOTTOMLEFT)); + topLeft = Glib::ustring::format(std::fixed, std::setprecision(precisionTop), shapePositionValue(TS_TOPLEFT)); + bottomRight = Glib::ustring::format(std::fixed, std::setprecision(precisionBottom), shapePositionValue(TS_BOTTOMRIGHT)); + topRight = Glib::ustring::format(std::fixed, std::setprecision(precisionTop), shapePositionValue(TS_TOPRIGHT)); +} + #endif diff --git a/rtgui/vibrance.cc b/rtgui/vibrance.cc index 589009c08..0d3b05410 100644 --- a/rtgui/vibrance.cc +++ b/rtgui/vibrance.cc @@ -28,19 +28,20 @@ Vibrance::Vibrance () : Gtk::VBox(), FoldableToolPanel(this) { enabled->set_active (false); pack_start(*enabled, Gtk::PACK_SHRINK, 0); - pastels = Gtk::manage(new Adjuster (M("TP_VIBRANCE_PASTELS"),-100,100,5,50)); - pastels->setAdjusterListener (this); - //if (pastels->delay < 1000) pastels->delay = 1000; - pack_start( *pastels, Gtk::PACK_SHRINK, 0); - - saturated = Gtk::manage(new Adjuster (M("TP_VIBRANCE_SATURATED"),-100,100,5,50)); + saturated = Gtk::manage(new Adjuster (M("TP_VIBRANCE_SATURATED"),-100,100,1,50)); saturated->setAdjusterListener (this); saturated->set_sensitive(false); //if (saturated->delay < 1000) saturated->delay = 1000; pack_start( *saturated, Gtk::PACK_SHRINK, 0); - psThreshold = Gtk::manage (new ThresholdAdjuster (M("TP_VIBRANCE_PSTHRESHOLD"), 0., 100., 1., 75., 0, false)); + pastels = Gtk::manage(new Adjuster (M("TP_VIBRANCE_PASTELS"),-100,100,1,50)); + pastels->setAdjusterListener (this); + //if (pastels->delay < 1000) pastels->delay = 1000; + pack_start( *pastels, Gtk::PACK_SHRINK, 0); + + psThreshold = Gtk::manage (new ThresholdAdjuster (M("TP_VIBRANCE_PSTHRESHOLD"), -100., 100., 0., M("TP_VIBRANCE_PSTHRESHOLD_WEIGTHING"), 0, 0., 100., 75., M("TP_VIBRANCE_PSTHRESHOLD_SATTHRESH"), 0, this, false)); psThreshold->setAdjusterListener (this); + psThreshold->set_tooltip_markup(M("TP_VIBRANCE_PSTHRESHOLD_TOOLTIP")); psThreshold->set_sensitive(false); //if (psThreshold->delay < 1000) psThreshold->delay = 1000; pack_start( *psThreshold, Gtk::PACK_SHRINK, 0); @@ -251,12 +252,14 @@ void Vibrance::adjusterChanged (Adjuster* a, double newval) { else if (a == saturated && !pastSatTog->get_active()) listener->panelChanged (EvVibranceSaturated, value ); } + if (pastSatTog->get_active()) + psThreshold->queue_draw(); } void Vibrance::adjusterChanged (ThresholdAdjuster* a, int newBottom, int newTop) { - if (listener && enabled->get_active()) { - listener->panelChanged (EvVibrancePastSatThreshold, psThreshold->getHistoryString()); - } + if (listener && enabled->get_active()) { + listener->panelChanged (EvVibrancePastSatThreshold, psThreshold->getHistoryString()); + } } @@ -295,3 +298,46 @@ void Vibrance::trimValues (ProcParams* pp) { pastels->trimValue (pp->vibrance.pastels); saturated->trimValue (pp->vibrance.saturated); } + +std::vector Vibrance::getCurvePoints(ThresholdSelector* tAdjuster) const { + std::vector points; + double threshold, transitionWeighting; + tAdjuster->getPositions(transitionWeighting, threshold); // ( range -100;+100, range 0;+100 ) + transitionWeighting /= 100.; // range -1., +1. + threshold /= 100.; // range 0., +1. + + // Initial point + points.push_back(0.); points.push_back(0.); + + double p2 = 3.0*threshold/4.0; // same one than in ipvibrance.cc + double s0 = threshold + (1.0-threshold)/4.0; // same one than in ipvibrance.cc + + // point at the beginning of the first linear transition + points.push_back(p2); points.push_back(0.); + + // Y value of the chroma mean point, calculated to get a straight line between p2 and s0 + double chromaMean = (threshold/4.0) / (s0-p2); + // move chromaMean up or down depending on transitionWeighting + if (transitionWeighting > 0.0) { + // positive values -> give more weight to Saturated + chromaMean = (1.0-chromaMean) * transitionWeighting + chromaMean; + } + else if (transitionWeighting < 0.0) { + // negative values -> give more weight to Pastels + chromaMean = chromaMean * transitionWeighting + chromaMean; + } + + // point at the location of the Top cursor, at the end of the first linear transition and the beginning of the second one + points.push_back(threshold); points.push_back(chromaMean); + + if (threshold < 1.0) { + + // point at the end of the second linear transition + points.push_back(s0); points.push_back(1.0); + + // end point + points.push_back(1.0); points.push_back(1.0); + } + + return points; +} diff --git a/rtgui/vibrance.h b/rtgui/vibrance.h index 160024ba9..a53599f89 100644 --- a/rtgui/vibrance.h +++ b/rtgui/vibrance.h @@ -24,7 +24,7 @@ #include "thresholdadjuster.h" #include "toolpanel.h" -class Vibrance : public Gtk::VBox, public AdjusterListener, public ThresholdAdjusterListener, public FoldableToolPanel { +class Vibrance : public Gtk::VBox, public AdjusterListener, public ThresholdAdjusterListener, public FoldableToolPanel, public ThresholdCurveProvider { protected: Gtk::CheckButton* enabled; @@ -60,7 +60,8 @@ public: void enabled_toggled (); void protectskins_toggled (); void avoidcolorshift_toggled (); - void pastsattog_toggled (); + void pastsattog_toggled (); + std::vector getCurvePoints(ThresholdSelector* tAdjuster) const; };