Solving issue 1444: " Better Vibrance tool's threshold handling"
This commit is contained in:
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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<float>(params->vibrance.psthreshold.value[ThresholdSelector::TS_TOPLEFT]) / 100.0f;
|
||||
float transitionweighting = static_cast<float>(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<float>(params->vibrance.psthreshold.value[ThresholdSelector::TS_BOTTOMLEFT]) / 100.0f;
|
||||
limitpastelsatur=static_cast<float>(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<height; i++)
|
||||
@@ -2214,21 +2225,21 @@ void ImProcFunctions::vibrance (LabImage* lab) {
|
||||
else { pa=(chs2- chs3)/(s2-s3);pb=chs2-pa*s2; chmodsat = pa*saturation + pb; }
|
||||
|
||||
if(chromaPastel != chromaSatur){
|
||||
//if sliders pastels and saturated differents: tansition with linear interpolation between p2 and s0
|
||||
float chromaPastel_a, chromaPastel_b, chromamean, chromaSatur_a, chromaSatur_b, newchromaPastel, newchromaSatur;
|
||||
//modify the "mean" point in function of double threshold => 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
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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 <typename T>
|
||||
rtengine::procparams::Threshold<T> getValue () { return tSelector.getPositions<T>(); }
|
||||
rtengine::procparams::Threshold<T> getValue () {
|
||||
return tSelector.getPositions<T>();
|
||||
}
|
||||
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 <class T>
|
||||
void setValue (const rtengine::procparams::Threshold<T> &tValues) {
|
||||
tSelector.setPositions<T>(tValues);
|
||||
}
|
||||
void setValue (const rtengine::procparams::Threshold<T> &tValues) { tSelector.setPositions<T>(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
|
||||
|
@@ -20,17 +20,45 @@
|
||||
#include "thresholdselector.h"
|
||||
#include "multilangmgr.h"
|
||||
#include <cassert>
|
||||
#include <iomanip>
|
||||
#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<GradientMilestone> &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<Gtk::Style> 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<double> pts = bgCurveProvider->getCurvePoints(this); // the values sent by the provider are not checked (assumed to be correct)
|
||||
if (pts.size() >= 4) {
|
||||
std::vector<double>::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 (; i<pts.end(); ) {
|
||||
x = *i; i++;
|
||||
y = *i; i++;
|
||||
cr->line_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("<b>%1:</b> %2 <b>%3:</b> %4\n<b>%5:</b> %6 <b>%7:</b> %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("<b>%1:</b> %2 <b>%3:</b> %4\n<b>%5:</b> %6 <b>%7:</b> %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("<b>%1:</b> %2\n<b>%3:</b> %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("<b>%1:</b> %2\n<b>%3:</b> %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("<b>%1:</b> %2\n<b>%3:</b> %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<void> 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();
|
||||
}
|
||||
|
@@ -21,6 +21,21 @@
|
||||
|
||||
#include "guiutils.h"
|
||||
#include "../rtengine/procparams.h"
|
||||
#include <iomanip>
|
||||
|
||||
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<double> 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<Gdk::GC> gc_;
|
||||
Glib::RefPtr<Gdk::Pixmap> backBuffer;
|
||||
std::vector<GradientMilestone> 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<void> 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 <typename T>
|
||||
void setDefaults (const rtengine::procparams::Threshold<T> &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 <typename T>
|
||||
void setPositions (const rtengine::procparams::Threshold<T> &tValues) {
|
||||
positions[TS_BOTTOMLEFT] = static_cast<double>(tValues.value[TS_BOTTOMLEFT]);
|
||||
@@ -122,28 +149,42 @@ class ThresholdSelector : public Gtk::DrawingArea {
|
||||
rtengine::procparams::Threshold<T> getPositions () {
|
||||
if (doubleThresh) {
|
||||
rtengine::procparams::Threshold<T> rThresh(
|
||||
static_cast<T>(shapeValue(positions[TS_BOTTOMLEFT])),
|
||||
static_cast<T>(shapeValue(positions[TS_TOPLEFT])),
|
||||
static_cast<T>(shapeValue(positions[TS_BOTTOMRIGHT])),
|
||||
static_cast<T>(shapeValue(positions[TS_TOPRIGHT])),
|
||||
static_cast<T>(shapePositionValue(TS_BOTTOMLEFT)),
|
||||
static_cast<T>(shapePositionValue(TS_TOPLEFT)),
|
||||
static_cast<T>(shapePositionValue(TS_BOTTOMRIGHT)),
|
||||
static_cast<T>(shapePositionValue(TS_TOPRIGHT)),
|
||||
initalEq1
|
||||
);
|
||||
return rThresh;
|
||||
}
|
||||
else {
|
||||
rtengine::procparams::Threshold<T> rThresh(
|
||||
static_cast<T>(shapeValue(positions[TS_BOTTOMLEFT])),
|
||||
static_cast<T>(shapeValue(positions[TS_TOPLEFT])),
|
||||
static_cast<T>(shapePositionValue(TS_BOTTOMLEFT)),
|
||||
static_cast<T>(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 <typename T>
|
||||
void getPositions (T &bottom, T &top) {
|
||||
bottom = static_cast<T>(shapePositionValue(TS_BOTTOMLEFT));
|
||||
top = static_cast<T>(shapePositionValue(TS_TOPLEFT));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void getPositions (T &bottomLeft, T &topLeft, T &bottomRight, T &topRight) {
|
||||
bottomLeft = static_cast<T>(shapePositionValue(TS_BOTTOMLEFT));
|
||||
topLeft = static_cast<T>(shapePositionValue(TS_TOPLEFT));
|
||||
bottomRight = static_cast<T>(shapePositionValue(TS_BOTTOMRIGHT));
|
||||
topRight = static_cast<T>(shapePositionValue(TS_TOPRIGHT));
|
||||
}
|
||||
|
||||
void setSeparatedSliders(bool separated);
|
||||
bool getSeparatedSliders();
|
||||
void setBgGradient (const std::vector<GradientMilestone> &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<Gtk::Style>& 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> (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> (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
|
||||
|
||||
|
@@ -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<double> Vibrance::getCurvePoints(ThresholdSelector* tAdjuster) const {
|
||||
std::vector<double> points;
|
||||
double threshold, transitionWeighting;
|
||||
tAdjuster->getPositions<double>(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;
|
||||
}
|
||||
|
@@ -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<double> getCurvePoints(ThresholdSelector* tAdjuster) const;
|
||||
};
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user