Solving issue 2610: "Display curve node input/output values" + add a new tone curve mode (Luminance)

This commit is contained in:
Hombrenatureh.510 2015-05-21 00:07:22 +02:00
parent 06deac5da4
commit 7c0b8e6fe1
41 changed files with 1434 additions and 239 deletions

View File

@ -9,10 +9,15 @@ ADJUSTER_RESET_TO_DEFAULT;Réglages par défaut
BATCHQUEUE_AUTOSTART;Démarrage auto
BATCHQUEUE_DESTFILENAME;Chemin et nom de fichier
BATCH_PROCESSING;Traitement par lot
CURVEEDITOR_AXIS_LEFT_TAN;TG:
CURVEEDITOR_AXIS_RIGHT_TAN;TD:
CURVEEDITOR_AXIS_IN;E:
CURVEEDITOR_AXIS_OUT;S:
CURVEEDITOR_CURVES;Courbes
CURVEEDITOR_CURVE;Courbe
CURVEEDITOR_CUSTOM;Personnalisé
CURVEEDITOR_DARKS;Zones sombres
CURVEEDITOR_EDITPOINT_HINT;Active l'édition des valeurs d'entrée/sortie.\n\nUn clic droit sur un point le sélectionne.\nUn clic droit dans un espace vide desélectionne le point.
CURVEEDITOR_HIGHLIGHTS;Hautes lumières
CURVEEDITOR_LIGHTS;Zones claires
CURVEEDITOR_LINEAR;Linéaire
@ -1171,6 +1176,7 @@ TP_EXPOSURE_SATURATION;Saturation
TP_EXPOSURE_TCMODE_FILMLIKE;Similaire Film
TP_EXPOSURE_TCMODE_LABEL1;Mode courbe 1
TP_EXPOSURE_TCMODE_LABEL2;Mode courbe 2
TP_EXPOSURE_TCMODE_LUMINANCE;Luminance
TP_EXPOSURE_TCMODE_SATANDVALBLENDING;Mixage Saturation et Valeur
TP_EXPOSURE_TCMODE_STANDARD;Standard
TP_EXPOSURE_TCMODE_WEIGHTEDSTD;Standard Pondéré

View File

@ -11,10 +11,15 @@ ADJUSTER_RESET_TO_DEFAULT;Reset to default
BATCHQUEUE_AUTOSTART;Auto-start
BATCHQUEUE_DESTFILENAME;Path and file name
BATCH_PROCESSING;Batch Processing
CURVEEDITOR_AXIS_LEFT_TAN;LT:
CURVEEDITOR_AXIS_RIGHT_TAN;RT:
CURVEEDITOR_AXIS_IN;I:
CURVEEDITOR_AXIS_OUT;O:
CURVEEDITOR_CURVES;Curves
CURVEEDITOR_CURVE;Curve
CURVEEDITOR_CUSTOM;Custom
CURVEEDITOR_DARKS;Darks
CURVEEDITOR_EDITPOINT_HINT;Enable edition of node in/out values.\n\nRight-click on a node to select it.\nRight-click on empty space to de-select the node.
CURVEEDITOR_HIGHLIGHTS;Highlights
CURVEEDITOR_LIGHTS;Lights
CURVEEDITOR_LINEAR;Linear
@ -1358,6 +1363,7 @@ TP_EXPOSURE_SATURATION;Saturation
TP_EXPOSURE_TCMODE_FILMLIKE;Film-like
TP_EXPOSURE_TCMODE_LABEL1;Curve mode 1
TP_EXPOSURE_TCMODE_LABEL2;Curve mode 2
TP_EXPOSURE_TCMODE_LUMINANCE;Luminance
TP_EXPOSURE_TCMODE_SATANDVALBLENDING;Saturation and Value Blending
TP_EXPOSURE_TCMODE_STANDARD;Standard
TP_EXPOSURE_TCMODE_WEIGHTEDSTD;Weighted Standard

View File

@ -153,6 +153,13 @@ style "clearlooks-spinbutton" {
}
}
style "clearlooks-axisadjuster" = "clearlooks-spinbutton" {
# background
base[INSENSITIVE] = @rt_tool_bg
# text
text[INSENSITIVE] = @rt_fg_color
}
style "clearlooks-framelessspinbutton" {
# IMPORTANT!
@ -535,6 +542,7 @@ widget "*.MyExpanderTitle" style "clearlooks-expander"
widget "*.ExpanderBox" style "clearlooks-toolFrame"
widget "*.ExpanderBox.*.MyExpanderTitle" style "clearlooks-subexpander"
widget "*.ExpanderBox.*.MyExpanderTitle.*" style "clearlooks-subexpander"
widget "*.AxisAdjuster" style "clearlooks-axisadjuster"
# The window of the tooltip is called "gtk-tooltip"
##################################################################

View File

@ -153,6 +153,13 @@ style "clearlooks-spinbutton" {
}
}
style "clearlooks-axisadjuster" = "clearlooks-spinbutton" {
# background
base[INSENSITIVE] = @rt_tool_bg
# text
text[INSENSITIVE] = @rt_fg_color
}
style "clearlooks-framelessspinbutton" {
# IMPORTANT!
@ -535,6 +542,7 @@ widget "*.MyExpanderTitle" style "clearlooks-expander"
widget "*.ExpanderBox" style "clearlooks-toolFrame"
widget "*.ExpanderBox.*.MyExpanderTitle" style "clearlooks-subexpander"
widget "*.ExpanderBox.*.MyExpanderTitle.*" style "clearlooks-subexpander"
widget "*.AxisAdjuster" style "clearlooks-axisadjuster"
# The window of the tooltip is called "gtk-tooltip"
##################################################################

View File

@ -153,6 +153,13 @@ style "clearlooks-spinbutton" {
}
}
style "clearlooks-axisadjuster" = "clearlooks-spinbutton" {
# background
base[INSENSITIVE] = @rt_tool_bg
# text
text[INSENSITIVE] = @rt_fg_color
}
style "clearlooks-framelessspinbutton" {
# IMPORTANT!
@ -535,6 +542,7 @@ widget "*.MyExpanderTitle" style "clearlooks-expander"
widget "*.ExpanderBox" style "clearlooks-toolFrame"
widget "*.ExpanderBox.*.MyExpanderTitle" style "clearlooks-subexpander"
widget "*.ExpanderBox.*.MyExpanderTitle.*" style "clearlooks-subexpander"
widget "*.AxisAdjuster" style "clearlooks-axisadjuster"
# The window of the tooltip is called "gtk-tooltip"
##################################################################

View File

@ -153,6 +153,13 @@ style "clearlooks-spinbutton" {
}
}
style "clearlooks-axisadjuster" = "clearlooks-spinbutton" {
# background
base[INSENSITIVE] = @rt_tool_bg
# text
text[INSENSITIVE] = @rt_fg_color
}
style "clearlooks-framelessspinbutton" {
# IMPORTANT!
@ -535,6 +542,7 @@ widget "*.MyExpanderTitle" style "clearlooks-expander"
widget "*.ExpanderBox" style "clearlooks-toolFrame"
widget "*.ExpanderBox.*.MyExpanderTitle" style "clearlooks-subexpander"
widget "*.ExpanderBox.*.MyExpanderTitle.*" style "clearlooks-subexpander"
widget "*.AxisAdjuster" style "clearlooks-axisadjuster"
# The window of the tooltip is called "gtk-tooltip"
##################################################################

View File

@ -153,6 +153,13 @@ style "clearlooks-spinbutton" {
}
}
style "clearlooks-axisadjuster" = "clearlooks-spinbutton" {
# background
base[INSENSITIVE] = @rt_tool_bg
# text
text[INSENSITIVE] = @rt_fg_color
}
style "clearlooks-framelessspinbutton" {
# IMPORTANT!
@ -535,6 +542,7 @@ widget "*.MyExpanderTitle" style "clearlooks-expander"
widget "*.ExpanderBox" style "clearlooks-toolFrame"
widget "*.ExpanderBox.*.MyExpanderTitle" style "clearlooks-subexpander"
widget "*.ExpanderBox.*.MyExpanderTitle.*" style "clearlooks-subexpander"
widget "*.AxisAdjuster" style "clearlooks-axisadjuster"
# The window of the tooltip is called "gtk-tooltip"
##################################################################

View File

@ -153,6 +153,13 @@ style "clearlooks-spinbutton" {
}
}
style "clearlooks-axisadjuster" = "clearlooks-spinbutton" {
# background
base[INSENSITIVE] = @rt_tool_bg
# text
text[INSENSITIVE] = @rt_fg_color
}
style "clearlooks-framelessspinbutton" {
# IMPORTANT!
@ -535,6 +542,7 @@ widget "*.MyExpanderTitle" style "clearlooks-expander"
widget "*.ExpanderBox" style "clearlooks-toolFrame"
widget "*.ExpanderBox.*.MyExpanderTitle" style "clearlooks-subexpander"
widget "*.ExpanderBox.*.MyExpanderTitle.*" style "clearlooks-subexpander"
widget "*.AxisAdjuster" style "clearlooks-axisadjuster"
# The window of the tooltip is called "gtk-tooltip"
##################################################################

View File

@ -153,6 +153,13 @@ style "clearlooks-spinbutton" {
}
}
style "clearlooks-axisadjuster" = "clearlooks-spinbutton" {
# background
base[INSENSITIVE] = @rt_tool_bg
# text
text[INSENSITIVE] = @rt_fg_color
}
style "clearlooks-framelessspinbutton" {
# IMPORTANT!
@ -535,6 +542,7 @@ widget "*.MyExpanderTitle" style "clearlooks-expander"
widget "*.ExpanderBox" style "clearlooks-toolFrame"
widget "*.ExpanderBox.*.MyExpanderTitle" style "clearlooks-subexpander"
widget "*.ExpanderBox.*.MyExpanderTitle.*" style "clearlooks-subexpander"
widget "*.AxisAdjuster" style "clearlooks-axisadjuster"
# The window of the tooltip is called "gtk-tooltip"
##################################################################

View File

@ -153,6 +153,13 @@ style "clearlooks-spinbutton" {
}
}
style "clearlooks-axisadjuster" = "clearlooks-spinbutton" {
# background
base[INSENSITIVE] = @rt_tool_bg
# text
text[INSENSITIVE] = @rt_fg_color
}
style "clearlooks-framelessspinbutton" {
# IMPORTANT!
@ -535,6 +542,7 @@ widget "*.MyExpanderTitle" style "clearlooks-expander"
widget "*.ExpanderBox" style "clearlooks-toolFrame"
widget "*.ExpanderBox.*.MyExpanderTitle" style "clearlooks-subexpander"
widget "*.ExpanderBox.*.MyExpanderTitle.*" style "clearlooks-subexpander"
widget "*.AxisAdjuster" style "clearlooks-axisadjuster"
# The window of the tooltip is called "gtk-tooltip"
##################################################################

View File

@ -153,6 +153,13 @@ style "clearlooks-spinbutton" {
}
}
style "clearlooks-axisadjuster" = "clearlooks-spinbutton" {
# background
base[INSENSITIVE] = @rt_tool_bg
# text
text[INSENSITIVE] = @rt_fg_color
}
style "clearlooks-framelessspinbutton" {
# IMPORTANT!
@ -535,6 +542,7 @@ widget "*.MyExpanderTitle" style "clearlooks-expander"
widget "*.ExpanderBox" style "clearlooks-toolFrame"
widget "*.ExpanderBox.*.MyExpanderTitle" style "clearlooks-subexpander"
widget "*.ExpanderBox.*.MyExpanderTitle.*" style "clearlooks-subexpander"
widget "*.AxisAdjuster" style "clearlooks-axisadjuster"
# The window of the tooltip is called "gtk-tooltip"
##################################################################

View File

@ -153,6 +153,13 @@ style "clearlooks-spinbutton" {
}
}
style "clearlooks-axisadjuster" = "clearlooks-spinbutton" {
# background
base[INSENSITIVE] = @rt_tool_bg
# text
text[INSENSITIVE] = @rt_fg_color
}
style "clearlooks-framelessspinbutton" {
# IMPORTANT!
@ -880,6 +887,7 @@ widget "*.MyExpanderTitle" style "clearlooks-expander"
widget "*.ExpanderBox" style "clearlooks-toolFrame"
widget "*.ExpanderBox.*.MyExpanderTitle" style "clearlooks-subexpander"
widget "*.ExpanderBox.*.MyExpanderTitle.*" style "clearlooks-subexpander"
widget "*.AxisAdjuster" style "clearlooks-axisadjuster"
# The window of the tooltip is called "gtk-tooltip"
##################################################################

View File

@ -153,6 +153,13 @@ style "clearlooks-spinbutton" {
}
}
style "clearlooks-axisadjuster" = "clearlooks-spinbutton" {
# background
base[INSENSITIVE] = @rt_tool_bg
# text
text[INSENSITIVE] = @rt_fg_color
}
style "clearlooks-framelessspinbutton" {
# IMPORTANT!
@ -537,6 +544,7 @@ widget "*.MyExpanderTitle" style "clearlooks-expander"
widget "*.ExpanderBox" style "clearlooks-toolFrame"
widget "*.ExpanderBox.*.MyExpanderTitle" style "clearlooks-subexpander"
widget "*.ExpanderBox.*.MyExpanderTitle.*" style "clearlooks-subexpander"
widget "*.AxisAdjuster" style "clearlooks-axisadjuster"
# The window of the tooltip is called "gtk-tooltip"
##################################################################

View File

@ -153,6 +153,13 @@ style "clearlooks-spinbutton" {
}
}
style "clearlooks-axisadjuster" = "clearlooks-spinbutton" {
# background
base[INSENSITIVE] = @rt_tool_bg
# text
text[INSENSITIVE] = @rt_fg_color
}
style "clearlooks-framelessspinbutton" {
# IMPORTANT!
@ -520,6 +527,7 @@ widget "*.MyExpanderTitle" style "clearlooks-expander"
widget "*.ExpanderBox" style "clearlooks-toolFrame"
widget "*.ExpanderBox.*.MyExpanderTitle" style "clearlooks-subexpander"
widget "*.ExpanderBox.*.MyExpanderTitle.*" style "clearlooks-subexpander"
widget "*.AxisAdjuster" style "clearlooks-axisadjuster"
# The window of the tooltip is called "gtk-tooltip"
##################################################################

View File

@ -153,6 +153,13 @@ style "clearlooks-spinbutton" {
}
}
style "clearlooks-axisadjuster" = "clearlooks-spinbutton" {
# background
base[INSENSITIVE] = @rt_tool_bg
# text
text[INSENSITIVE] = @rt_fg_color
}
style "clearlooks-framelessspinbutton" {
# IMPORTANT!
@ -515,6 +522,7 @@ widget "*.MyExpanderTitle" style "clearlooks-expander"
widget "*.ExpanderBox" style "clearlooks-toolFrame"
widget "*.ExpanderBox.*.MyExpanderTitle" style "clearlooks-subexpander"
widget "*.ExpanderBox.*.MyExpanderTitle.*" style "clearlooks-subexpander"
widget "*.AxisAdjuster" style "clearlooks-axisadjuster"
# The window of the tooltip is called "gtk-tooltip"
##################################################################

View File

@ -84,7 +84,9 @@ protected:
unsigned int maxs;
float maxsf;
T * data;
unsigned int clip, size;
unsigned int clip;
unsigned int size;
unsigned int upperBound; // always equals size-1, parameter created for performance reason
private:
unsigned int owner;
#if defined( __SSE2__ ) && defined( __x86_64__ )
@ -109,6 +111,7 @@ public:
data = new T[s];
owner = 1;
size = s;
upperBound = size-1;
maxs=size-2;
maxsf = (float)maxs;
#if defined( __SSE2__ ) && defined( __x86_64__ )
@ -131,6 +134,7 @@ public:
data = new T[s];
owner = 1;
size = s;
upperBound = size-1;
maxs=size-2;
maxsf = (float)maxs;
#if defined( __SSE2__ ) && defined( __x86_64__ )
@ -155,6 +159,7 @@ public:
data = new T[s];
owner = 1;
size = s;
upperBound = size-1;
maxs=size-2;
maxsf = (float)maxs;
#if defined( __SSE2__ ) && defined( __x86_64__ )
@ -199,7 +204,7 @@ public:
* @return number of element in the array
*/
int getUpperBound() {
return size>0 ? size-1 : 0;
return size>0 ? upperBound : 0;
}
LUT<T> & operator=(LUT<T> &rhs) {
@ -214,6 +219,7 @@ public:
this->owner=1;
memcpy(this->data,rhs.data,rhs.size*sizeof(T));
this->size=rhs.size;
this->upperBound=rhs.upperBound;
this->maxs=this->size-2;
this->maxsf = (float)this->maxs;
#if defined( __SSE2__ ) && defined( __x86_64__ )
@ -228,7 +234,7 @@ public:
}
// use with integer indices
T& operator[](int index) const {
return data[ rtengine::LIM<int>(index, 0, size-1) ];
return data[ rtengine::LIM<int>(index, 0, upperBound) ];
}
#if defined( __SSE2__ ) && defined( __x86_64__ )
@ -350,7 +356,7 @@ public:
else if (index > maxsf)
{
if (clip & LUT_CLIP_ABOVE)
return data[size - 1];
return data[upperBound];
idx =maxs;
}
float diff = index - (float) idx;
@ -359,6 +365,27 @@ public:
return (p1 + p2*diff);
}
// Return the value for "index" that is in the [0-1] range.
T getVal01 (float index) const {
index *= float(upperBound);
int idx = (int)index; // don't use floor! The difference in negative space is no problems here
if (index<0.f)
{
if (clip & LUT_CLIP_BELOW)
return data[0];
idx=0;
}
else if (index > maxsf)
{
if (clip & LUT_CLIP_ABOVE)
return data[upperBound];
idx =maxs;
}
float diff = index - (float) idx;
T p1 = data[idx];
T p2 = data[idx + 1]-p1;
return (p1 + p2*diff);
}
#ifndef NDEBUG
// Debug facility ; dump the content of the LUT in a file. No control of the filename is done
@ -393,10 +420,16 @@ public:
data = NULL;
owner = 1;
size = 0;
upperBound=0;
maxs=0;
}
};
// TODO: HOMBRE: HueLUT is actually unused, could we delete this class now that LUT::getVal01 has been created?
/** @brief LUT subclass handling hue values specifically.
The array has a fixed size of float values and have to be in the [0.; 1.] range in both axis (no error checking implemented) */
class HueLUT : public LUTf {
@ -413,7 +446,7 @@ class HueLUT : public LUTf {
// use with integer indices
float& operator[](int index) const {
return data[ rtengine::LIM<int>(index, 0, size-1) ];
return data[ rtengine::LIM<int>(index, 0, upperBound) ];
}
// use with float indices in the [0.;1.] range
@ -422,7 +455,7 @@ class HueLUT : public LUTf {
if (index<0.f)
return data[0];
else if (index > 1.f)
return data[size - 1];
return data[upperBound];
float balance = index - float(idx/500.f);
float h1 = data[idx];

View File

@ -154,8 +154,14 @@ public:
* @return luminance value
*/
// xyz_sRGBD65 : conversion matrix from XYZ to sRGB for D65 illuminant: we use diagonal values
static float rgbLuminance(float r, float g, float b) { return r*float(xyz_sRGBd65[1][0]) + g*float(xyz_sRGBd65[1][1]) + b*float(xyz_sRGBd65[1][2]); }
static double rgbLuminance(double r, double g, double b) { return r*xyz_sRGBd65[1][0] + g*xyz_sRGBd65[1][1] + b*xyz_sRGBd65[1][2]; }
static float rgbLuminance(float r, float g, float b) {
// WArning: The sum of xyz_sRGBd65[1][] is > 1.0 (i.e. 1.0000001), so we use our own adapted values)
// 0.2126729, 0.7151521, 0.0721750
return r*0.2126729f + g*0.7151521f + b*0.0721750f;
}
static double rgbLuminance(double r, double g, double b) {
return r*0.2126729 + g*0.7151521 + b*0.0721750;
}
/**

View File

@ -585,6 +585,11 @@ class WeightedStdToneCurve : public ToneCurve {
void Apply(float& r, float& g, float& b) const;
};
class LuminanceToneCurve : public ToneCurve {
public:
void Apply(float& r, float& g, float& b) const;
};
class WeightedStdToneCurvebw : public ToneCurve {
private:
float Triangle(float refX, float refY, float X2) const;
@ -670,6 +675,18 @@ inline void AdobeToneCurvebw::RGBTone (float& r, float& g, float& b) const {
g = b + ((r - b) * (gold - bold) / (rold - bold));
}
// Modifying the Luminance channel only
inline void LuminanceToneCurve::Apply(float &r, float &g, float &b) const {
assert (lutToneCurve);
float currLuminance = r*0.2126729f + g*0.7151521f + b*0.0721750f;
float newLuminance = lutToneCurve[currLuminance];
float coef = newLuminance/currLuminance;
r = LIM<float>(r*coef, 0.f, 65535.f);
g = LIM<float>(g*coef, 0.f, 65535.f);
b = LIM<float>(b*coef, 0.f, 65535.f);
}
inline float WeightedStdToneCurve::Triangle(float a, float a1, float b) const {
if (a != b) {
float b1;

View File

@ -30,7 +30,7 @@ const double d50_d65[3][3] = {{ 1.0478112, 0.0228866, -0.0501270},
// Color space conversion to/from XYZ; color spaces adapted to D65
const double xyz_sRGBd65[3][3] = {{0.4124564, 0.3575761, 0.1804375},
{0.2126729, 0.7151522, 0.0721750},
{0.2126729, 0.7151522, 0.0721750}, // WARNING: the summ of this line is > 1.0
{0.0193339, 0.1191920, 0.9503041}};
const double sRGBd65_xyz[3][3] = {{ 3.2404542, -1.5371385, -0.4985314},

View File

@ -2658,6 +2658,17 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, EditBuffer *e
}
}
}
else if (curveMode==ToneCurveParams::TC_MODE_LUMINANCE){ // apply the curve to the luminance channel
const LuminanceToneCurve& userToneCurve = static_cast<const LuminanceToneCurve&>(customToneCurve1);
for (int i=istart,ti=0; i<tH; i++,ti++) {
for (int j=jstart,tj=0; j<tW; j++,tj++) {
rtemp[ti*TS+tj] = CLIP<float>(rtemp[ti*TS+tj]);
gtemp[ti*TS+tj] = CLIP<float>(gtemp[ti*TS+tj]);
btemp[ti*TS+tj] = CLIP<float>(btemp[ti*TS+tj]);
userToneCurve.Apply(rtemp[ti*TS+tj], gtemp[ti*TS+tj], btemp[ti*TS+tj]);
}
}
}
}
if (editID == EUID_ToneCurve2) { // filling the pipette buffer
@ -2703,6 +2714,14 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, EditBuffer *e
}
}
}
else if (curveMode2==ToneCurveParams::TC_MODE_LUMINANCE){ // apply the curve to the luminance channel
const LuminanceToneCurve& userToneCurve = static_cast<const LuminanceToneCurve&>(customToneCurve2);
for (int i=istart,ti=0; i<tH; i++,ti++) {
for (int j=jstart,tj=0; j<tW; j++,tj++) {
userToneCurve.Apply(rtemp[ti*TS+tj], gtemp[ti*TS+tj], btemp[ti*TS+tj]);
}
}
}
}
if (iGammaLUTf) {

View File

@ -1065,6 +1065,9 @@ int ProcParams::save (Glib::ustring fname, Glib::ustring fname2, bool fnameAbsol
case (ToneCurveParams::TC_MODE_WEIGHTEDSTD):
method = "WeightedStd";
break;
case (ToneCurveParams::TC_MODE_LUMINANCE):
method = "Luminance";
break;
}
keyFile.set_string ("Exposure", "CurveMode", method);
}
@ -1083,6 +1086,9 @@ int ProcParams::save (Glib::ustring fname, Glib::ustring fname2, bool fnameAbsol
case (ToneCurveParams::TC_MODE_WEIGHTEDSTD):
method = "WeightedStd";
break;
case (ToneCurveParams::TC_MODE_LUMINANCE):
method = "Luminance";
break;
}
keyFile.set_string ("Exposure", "CurveMode2", method);
}
@ -1909,6 +1915,7 @@ if (keyFile.has_group ("Exposure")) {
else if (sMode == "FilmLike") toneCurve.curveMode = ToneCurveParams::TC_MODE_FILMLIKE;
else if (sMode == "SatAndValueBlending") toneCurve.curveMode = ToneCurveParams::TC_MODE_SATANDVALBLENDING;
else if (sMode == "WeightedStd") toneCurve.curveMode = ToneCurveParams::TC_MODE_WEIGHTEDSTD;
else if (sMode == "Luminance") toneCurve.curveMode = ToneCurveParams::TC_MODE_LUMINANCE;
if (pedited) pedited->toneCurve.curveMode = true;
}
if (keyFile.has_key ("Exposure", "CurveMode2")) {
@ -1916,7 +1923,7 @@ if (keyFile.has_group ("Exposure")) {
if (sMode == "Standard") toneCurve.curveMode2 = ToneCurveParams::TC_MODE_STD;
else if (sMode == "FilmLike") toneCurve.curveMode2 = ToneCurveParams::TC_MODE_FILMLIKE;
else if (sMode == "SatAndValueBlending") toneCurve.curveMode2 = ToneCurveParams::TC_MODE_SATANDVALBLENDING;
else if (sMode == "WeightedStd") toneCurve.curveMode2 = ToneCurveParams::TC_MODE_WEIGHTEDSTD;
else if (sMode == "Luminance") toneCurve.curveMode2 = ToneCurveParams::TC_MODE_LUMINANCE;
if (pedited) pedited->toneCurve.curveMode2 = true;
}
if (ppVersion>200) {

View File

@ -193,7 +193,8 @@ class ToneCurveParams {
TC_MODE_STD, // Standard modes, the curve is applied on all component individually
TC_MODE_WEIGHTEDSTD, // Weighted standard mode
TC_MODE_FILMLIKE, // Film-like mode, as defined in Adobe's reference code
TC_MODE_SATANDVALBLENDING // Modify the Saturation and Value channel
TC_MODE_SATANDVALBLENDING, // Modify the Saturation and Value channel
TC_MODE_LUMINANCE // Modify the Luminance channel with coefficients from Rec 709's
};
bool autoexp;

View File

@ -4,7 +4,7 @@ set (BASESOURCEFILES
exportpanel.cc cursormanager.cc rtwindow.cc renamedlg.cc recentbrowser.cc placesbrowser.cc filepanel.cc editorpanel.cc batchqueuepanel.cc
ilabel.cc thumbbrowserbase.cc adjuster.cc filebrowserentry.cc filebrowser.cc filethumbnailbuttonset.cc
cachemanager.cc cacheimagedata.cc shcselector.cc perspective.cc thresholdselector.cc thresholdadjuster.cc
clipboard.cc thumbimageupdater.cc bqentryupdater.cc lensgeom.cc coloredbar.cc edit.cc
clipboard.cc thumbimageupdater.cc bqentryupdater.cc lensgeom.cc coloredbar.cc edit.cc coordinateadjuster.cc
coarsepanel.cc cacorrection.cc chmixer.cc blackwhite.cc
resize.cc icmpanel.cc crop.cc shadowshighlights.cc
impulsedenoise.cc dirpyrdenoise.cc epd.cc

198
rtgui/coordinateadjuster.cc Normal file
View File

@ -0,0 +1,198 @@
/*
* This file is part of RawTherapee.
*
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
*
* RawTherapee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RawTherapee is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RawTherapee. If not, see <http://www.gnu.org/licenses/>.
*/
#include "coordinateadjuster.h"
#include "multilangmgr.h"
#include <cassert>
#include "curveeditorgroup.h"
Axis::Axis()
: label(""), decimal(5), increment(0.001), pageIncrement(0.01), rangeLowerBound(0.), rangeUpperBound(1.)
{}
Axis::Axis(Glib::ustring label, unsigned int decimal, double increment, double pageIncrement, double valMin=0.0, double valMax=1.0)
: label(label), decimal(decimal), increment(increment), pageIncrement(pageIncrement), rangeLowerBound(valMin), rangeUpperBound(valMax)
{}
void Axis::setValues(Glib::ustring label, unsigned int decimal, double increment, double pageIncrement, double valMin, double valMax) {
this->label = label;
this->decimal = decimal;
this->increment = increment;
this->pageIncrement = pageIncrement;
this->rangeLowerBound = valMin;
this->rangeUpperBound = valMax;
}
CoordinateAdjuster::AxisAdjuster::AxisAdjuster(CoordinateAdjuster *parent, const Axis *axis, char index) : idx(index), parent(parent) {
label = Gtk::manage( new Gtk::Label(axis->label) );
spinButton = Gtk::manage( new Gtk::SpinButton() );
label = Gtk::manage (new Gtk::Label(axis->label));
//label->set_alignment(Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
spinButton = Gtk::manage (new Gtk::SpinButton());
spinButton->set_name("AxisAdjuster");
spinButton->set_digits(axis->decimal);
spinButton->set_increments(axis->increment, axis->pageIncrement);
spinButton->set_range(axis->rangeLowerBound, axis->rangeUpperBound);
spinButton->set_sensitive(false);
spinButtonConn = spinButton->signal_value_changed().connect( sigc::mem_fun(*this, &CoordinateAdjuster::AxisAdjuster::valueChanged) );
//spinButton->signal_key_press_event().connect( sigc::mem_fun(*this, &CoordinateAdjuster::AxisAdjuster::keyPressed) );
}
void CoordinateAdjuster::AxisAdjuster::updateGUI(const Axis &axis) {
label->set_text(axis.label);
spinButton->set_digits(axis.decimal);
spinButton->set_increments(axis.increment, axis.pageIncrement);
spinButton->set_range(axis.rangeLowerBound, axis.rangeUpperBound);
spinButton->set_sensitive(false);
rangeLowerBound = axis.rangeLowerBound;
rangeUpperBound = axis.rangeUpperBound;
}
void CoordinateAdjuster::AxisAdjuster::setValue(double newValue) {
float range = rangeUpperBound-rangeLowerBound;
spinButtonConn.block(true);
spinButton->set_value(newValue*range+rangeLowerBound);
spinButtonConn.block(false);
}
void CoordinateAdjuster::AxisAdjuster::valueChanged() {
float range = rangeUpperBound-rangeLowerBound;
parent->updatePos(idx, (spinButton->get_value()-rangeLowerBound)/range);
}
CoordinateAdjuster::CoordinateAdjuster(CoordinateProvider *provider, CurveEditorSubGroup *parent, const std::vector<Axis> &axis)
: status(CA_STATUS_IDLE), parent(parent), coordinateProvider(provider)
{
provider->setListener(this);
createWidgets(axis);
}
CoordinateAdjuster::CoordinateAdjuster(CoordinateProvider *provider, CurveEditorSubGroup *parent)
: status(CA_STATUS_IDLE), parent(parent), coordinateProvider(provider)
{
std::vector<Axis> defaultAxis;
Axis X(M("CURVEEDITOR_AXIS_IN"), 3, 0.1, 1., 0., 100.);
Axis Y(M("CURVEEDITOR_AXIS_OUT"), 3, 0.1, 1., 0., 100.);
defaultAxis.push_back(X);
defaultAxis.push_back(Y);
provider->setListener(this);
createWidgets(defaultAxis);
}
void CoordinateAdjuster::createWidgets(const std::vector<Axis> &axis) {
unsigned int count = axis.size();
if (!count) {
printf("CoordinateAdjuster - Error: the Axis list is empty!\n");
return;
}
assert (count <= 4);
axisAdjusters.resize(axis.size());
set_spacing(3);
AxisAdjuster *currAdjuster = NULL;
for (unsigned int i=0; i<count; ++i) {
const Axis *currAxis = &(axis.at(i));
axisAdjusters.at(i) = new AxisAdjuster(this, currAxis, i);
currAdjuster = axisAdjusters.at(i);
currAdjuster->rangeLowerBound = currAxis->rangeLowerBound;
currAdjuster->rangeUpperBound = currAxis->rangeUpperBound;
pack_start(*(currAdjuster->label), Gtk::PACK_SHRINK, 0);
pack_start(*(currAdjuster->spinButton), Gtk::PACK_SHRINK, 0);
}
}
void CoordinateAdjuster::updatePos(char index, double value) {
coordinateProvider->setPos(value, index);
}
void CoordinateAdjuster::setAxis(const std::vector<Axis> &axis) {
assert (axis.size() == axisAdjusters.size());
for (size_t i = 0; i<axisAdjusters.size(); ++i) {
axisAdjusters.at(i)->updateGUI(axis.at(i));
}
}
void CoordinateAdjuster::setPos(std::vector<double> &pos) {
if (is_visible()) {
for (size_t i=0; i<pos.size(); ++i) {
axisAdjusters.at(i)->setValue(pos.at(i));
}
}
}
void CoordinateAdjuster::startNumericalAdjustment(const std::vector<Boundaries> &newBoundaries) {
for (size_t i=0; i<axisAdjusters.size(); ++i) {
Gtk::SpinButton *currSpinButton = axisAdjusters.at(i)->spinButton;
currSpinButton->set_sensitive(true);
float range = axisAdjusters.at(i)->rangeUpperBound-axisAdjusters.at(i)->rangeLowerBound;
currSpinButton->set_range(newBoundaries.at(i).minVal*range+axisAdjusters.at(i)->rangeLowerBound, newBoundaries.at(i).maxVal*range+axisAdjusters.at(i)->rangeUpperBound);
}
axisAdjusters.at(0)->spinButton->grab_focus();
status = CA_STATUS_EDITING;
}
void CoordinateAdjuster::switchAdjustedPoint(std::vector<double> &pos, const std::vector<Boundaries> &newBoundaries) {
if (status != CA_STATUS_EDITING)
return;
for (size_t i=0; i<axisAdjusters.size(); ++i) {
AxisAdjuster *currAxis = axisAdjusters.at(i);
// disable events
currAxis->spinButtonConn.block(true);
// To avoid trimmed values, we have to...
// ...enlarge range to the maximum
currAxis->spinButton->set_range(axisAdjusters.at(i)->rangeLowerBound, axisAdjusters.at(i)->rangeUpperBound);
// ...set the new value
currAxis->setValue(pos.at(i));
// ...narrow the range to the new interval
float range = axisAdjusters.at(i)->rangeUpperBound-axisAdjusters.at(i)->rangeLowerBound;
currAxis->spinButton->set_range(newBoundaries.at(i).minVal*range+axisAdjusters.at(i)->rangeLowerBound, newBoundaries.at(i).maxVal*range+axisAdjusters.at(i)->rangeUpperBound);
// enable events
currAxis->spinButtonConn.block(false);
}
axisAdjusters.at(0)->spinButton->grab_focus();
status = CA_STATUS_EDITING;
}
void CoordinateAdjuster::showMe(CoordinateProvider *provider) {
parent->showCoordinateAdjuster(provider);
}
void CoordinateAdjuster::stopNumericalAdjustment() {
for (size_t i=0; i<axisAdjusters.size(); ++i) {
axisAdjusters.at(i)->spinButtonConn.block(true);
axisAdjusters.at(i)->spinButton->set_sensitive(false);
axisAdjusters.at(i)->spinButton->set_range(axisAdjusters.at(i)->rangeLowerBound, axisAdjusters.at(i)->rangeUpperBound);
axisAdjusters.at(i)->spinButtonConn.block(false);
}
status = CA_STATUS_IDLE;
}

157
rtgui/coordinateadjuster.h Normal file
View File

@ -0,0 +1,157 @@
/*
* This file is part of RawTherapee.
*
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
*
* RawTherapee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RawTherapee is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RawTherapee. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _COORDINATEADJUSTER_
#define _COORDINATEADJUSTER_
#include <gtkmm.h>
class CurveEditorSubGroup;
class Axis {
public:
Glib::ustring label;
unsigned int decimal;
double increment;
double pageIncrement;
double rangeLowerBound;
double rangeUpperBound;
Axis();
Axis(Glib::ustring label, unsigned int decimal, double increment, double pageIncrement, double valMin, double valMax);
void setValues(Glib::ustring label, unsigned int decimal, double increment, double pageIncrement, double valMin, double valMax);
};
class CoordinateAdjuster;
/**
* @brief Object that will emit NewCoordinates events
*/
class CoordinateProvider {
protected:
CoordinateAdjuster *coordinateAdjuster;
public:
CoordinateProvider() : coordinateAdjuster(NULL) {}
virtual ~CoordinateProvider() {}
void setListener(CoordinateAdjuster *adjuster) { coordinateAdjuster = adjuster; }
/** @brief Update the position of the edited point ; will trigger events
*
* @param pos New position
* @param chanIdx Chanel index as given in the std::vector upon instantiation
*/
virtual void setPos(double pos, int chanIdx)=0;
virtual void stopNumericalAdjustment()=0;
};
/**
* @brief Widget that displays spin buttons to adjust coordinates
*
* You can set up to 4 axis that will be displayed on a single line, so keep the labels short!
*
* The position of the Axis in the vector will be used in the communication between the Adjuster and the Provider to identify the Axis
*/
class CoordinateAdjuster : public Gtk::HBox {
public:
//-------------------------------- AxisAdjuster -------------------
class AxisAdjuster {
private:
char idx;
public:
CoordinateAdjuster *parent;
Gtk::Label *label;
Gtk::SpinButton *spinButton;
sigc::connection spinButtonConn;
float rangeLowerBound;
float rangeUpperBound;
AxisAdjuster(CoordinateAdjuster *parent, const Axis *axis, char index);
// used to update the AxisAdjuster's parameters
void updateGUI(const Axis &axis);
// useed to update the displayed value
void setValue(double newValue);
//bool keyPressed(GdkEventKey* event);
void valueChanged();
};
//----------------------------------------------------------------
//-------------------------------- Boundaries -------------------
class Boundaries {
public:
double minVal;
double maxVal;
};
//---------------------------------------------------------------
private:
typedef enum {
CA_STATUS_IDLE,
CA_STATUS_EDITING,
CA_STATUS_END_EDITING
} Status;
std::vector<AxisAdjuster*> axisAdjusters;
Status status;
CurveEditorSubGroup *parent;
void createWidgets(const std::vector<Axis> &axis);
protected:
friend class AxisAdjuster;
CoordinateProvider *coordinateProvider;
void updatePos(char index, double value);
public:
/// Basic X/Y adjuster, in the [0-1] range
CoordinateAdjuster(CoordinateProvider *provider, CurveEditorSubGroup *parent);
/// For more complex adjuster
CoordinateAdjuster(CoordinateProvider *provider, CurveEditorSubGroup *parent, const std::vector<Axis> &axis);
virtual ~CoordinateAdjuster() {}
// Update the Axis list, e.g. on Curve change, but MUST have the same axis count
void setAxis(const std::vector<Axis> &axis);
/** @brief Update the numbers in the spin buttons ; doesn't trigger any event
*
* @param pos Vector that gives the values of each channels
*/
void setPos(std::vector<double> &pos);
/// Start the adjustment session (enable the widget)
void startNumericalAdjustment(const std::vector<Boundaries> &newBoundaries);
/// Edit another point
void switchAdjustedPoint(std::vector<double> &pos, const std::vector<Boundaries> &newBoundaries);
/// Trigger the event to show the CoordinateAdjuster
void showMe(CoordinateProvider *provider);
/// Stop the adjustment session (disable the widget, i.e. you won't be able to edit the values)
void stopNumericalAdjustment();
};
#endif

View File

@ -46,6 +46,10 @@ CurveEditorGroup::~CurveEditorGroup() {
void CurveEditorGroup::hideCurrentCurve() {
// Setting the curve type to 'Unchanged' hide the CurveEditor
if (diagonalSubGroup)
diagonalSubGroup->stopNumericalAdjustment();
if (flatSubGroup)
flatSubGroup->stopNumericalAdjustment();
if (displayedCurve)
displayedCurve->curveType->set_active(false);
}
@ -280,10 +284,10 @@ void CurveEditorGroup::curveChanged () {
/*
* Listener called when the user has modified the curve
*/
float CurveEditorGroup::blendPipetteValues (float chan1, float chan2, float chan3) {
float CurveEditorGroup::blendPipetteValues (CurveEditor* ce, float chan1, float chan2, float chan3) {
if (cl)
return cl->blendPipetteValues(chan1, chan2, chan3);
return cl->blendPipetteValues(ce, chan1, chan2, chan3);
return -1.f;
}

View File

@ -89,10 +89,12 @@ protected:
void updateGUI (CurveEditor* ce);
void curveResetPressed ();
void curveChanged ();
float blendPipetteValues(float chan1, float chan2, float chan3);
float blendPipetteValues(CurveEditor* ce, float chan1, float chan2, float chan3);
void setUnChanged (bool uc, CurveEditor* ce);
};
class CoordinateProvider;
class CurveEditorSubGroup {
friend class CurveEditorGroup;
@ -105,6 +107,7 @@ protected:
int valLinear;
int valUnchanged;
CurveEditorGroup *parent;
int curveBBoxPos; // 0=above, 1=right, 2=below, 3=left
ColoredBar* leftBar;
ColoredBar* bottomBar;
@ -120,6 +123,9 @@ public:
virtual void refresh(CurveEditor *curveToRefresh) = 0;
virtual void editModeSwitchedOff() = 0;
virtual void showCoordinateAdjuster(CoordinateProvider *provider) = 0;
virtual void stopNumericalAdjustment() = 0;
virtual void pipetteMouseOver(EditDataProvider *provider, int modifierKey) =0;
virtual void pipetteButton1Pressed(EditDataProvider *provider, int modifierKey) =0;
virtual void pipetteButton1Released(EditDataProvider *provider) =0;

View File

@ -19,6 +19,8 @@
#ifndef _CURVELISTENER_
#define _CURVELISTENER_
#include <vector>
class CurveEditor;
class CurveListener {
@ -37,7 +39,7 @@ class CurveListener {
* @param ce CurveEditor that we want to reset
* @param curve Actual curve for the return value. The actual curve type (given by the first value of the vector)
* should be kept the same. Change the curve type if REALLY necessary! */
virtual bool getResetCurve(CurveEditor *ce, std::vector<double> &curve) { return false; };
virtual bool getResetCurve(CurveEditor *ce, std::vector<double> &curve) { return false; }
/** @brief Blend pipette values from its different channels into a single value
If the buffer has more than one channel and one channel, this method will blend them together.
@ -45,7 +47,7 @@ class CurveListener {
@param chan2 second channel's value
@param chan3 third channel's value
@return the blended value */
virtual float blendPipetteValues(float chan1, float chan2, float chan3) {
virtual float blendPipetteValues(CurveEditor *ce, float chan1, float chan2, float chan3) {
float retVal = 0.f;
int n = 0;
if (chan1 != -1.f) {

View File

@ -27,6 +27,7 @@
#include "shcselector.h"
#include "adjuster.h"
#include "mycurve.h"
#include "mydiagonalcurve.h"
#include "curveeditor.h"
#include "diagonalcurveeditorsubgroup.h"
@ -35,6 +36,8 @@ DiagonalCurveEditorSubGroup::DiagonalCurveEditorSubGroup (CurveEditorGroup* prt,
editedAdjuster = NULL;
editedAdjusterValue = 0;
curveBBoxPos = options.curvebboxpos;
valLinear = (int)DCT_Linear;
valUnchanged = (int)DCT_Unchanged;
parent = prt;
@ -66,6 +69,9 @@ DiagonalCurveEditorSubGroup::DiagonalCurveEditorSubGroup (CurveEditorGroup* prt,
saveCustom->add (*Gtk::manage (new RTImage ("gtk-save-large.png")));
loadCustom = Gtk::manage (new Gtk::Button ());
loadCustom->add (*Gtk::manage (new RTImage ("gtk-open.png")));
editPointCustom = Gtk::manage (new Gtk::ToggleButton ());
editPointCustom->add (*Gtk::manage (new RTImage ("gtk-edit.png")));
editPointCustom->set_tooltip_text(M("CURVEEDITOR_EDITPOINT_HINT"));
editCustom = Gtk::manage (new Gtk::ToggleButton());
editCustom->add (*Gtk::manage (new RTImage ("editmodehand.png")));
editCustom->set_tooltip_text(M("EDIT_PIPETTE_TOOLTIP"));
@ -75,6 +81,7 @@ DiagonalCurveEditorSubGroup::DiagonalCurveEditorSubGroup (CurveEditorGroup* prt,
custombbox->pack_end (*copyCustom, Gtk::PACK_SHRINK, 0);
custombbox->pack_end (*saveCustom, Gtk::PACK_SHRINK, 0);
custombbox->pack_end (*loadCustom, Gtk::PACK_SHRINK, 0);
custombbox->pack_start(*editPointCustom, Gtk::PACK_SHRINK, 0);
custombbox->pack_start(*editCustom, Gtk::PACK_SHRINK, 0);
customCurveAndButtons->pack_start (*customCurve, Gtk::PACK_EXPAND_WIDGET, 0);
@ -90,12 +97,21 @@ DiagonalCurveEditorSubGroup::DiagonalCurveEditorSubGroup (CurveEditorGroup* prt,
} else if (options.curvebboxpos==3) {
customCurveAndButtons->reorder_child(*custombbox, 0);
}
customCoordAdjuster = Gtk::manage (new CoordinateAdjuster(customCurve, this));
customCurveBox->pack_start(*customCoordAdjuster, Gtk::PACK_SHRINK, 0);
if (options.curvebboxpos == 2)
customCurveBox->reorder_child(*customCoordAdjuster, 2);
customCoordAdjuster->show_all();
customCurveBox->show_all ();
customCoordAdjuster->hide();
saveCustom->signal_clicked().connect( sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::savePressed) );
loadCustom->signal_clicked().connect( sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::loadPressed) );
copyCustom->signal_clicked().connect( sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::copyPressed) );
pasteCustom->signal_clicked().connect( sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::pastePressed) );
editPointCustomConn = editPointCustom->signal_toggled().connect( sigc::bind(sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::editPointToggled), editPointCustom) );
editCustomConn = editCustom->signal_toggled().connect( sigc::bind(sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::editToggled), editCustom) );
saveCustom->set_tooltip_text (M("CURVEEDITOR_TOOLTIPSAVE"));
@ -130,6 +146,9 @@ DiagonalCurveEditorSubGroup::DiagonalCurveEditorSubGroup (CurveEditorGroup* prt,
saveNURBS->add (*Gtk::manage (new RTImage ("gtk-save-large.png")));
loadNURBS = Gtk::manage (new Gtk::Button ());
loadNURBS->add (*Gtk::manage (new RTImage ("gtk-open.png")));
editPointNURBS = Gtk::manage (new Gtk::ToggleButton ());
editPointNURBS->add (*Gtk::manage (new RTImage ("gtk-edit.png")));
editPointNURBS->set_tooltip_text(M("CURVEEDITOR_EDITPOINT_HINT"));
editNURBS = Gtk::manage (new Gtk::ToggleButton());
editNURBS->add (*Gtk::manage (new RTImage ("editmodehand.png")));
editNURBS->set_tooltip_text(M("EDIT_PIPETTE_TOOLTIP"));
@ -138,6 +157,7 @@ DiagonalCurveEditorSubGroup::DiagonalCurveEditorSubGroup (CurveEditorGroup* prt,
NURBSbbox->pack_end (*copyNURBS, Gtk::PACK_SHRINK, 0);
NURBSbbox->pack_end (*saveNURBS, Gtk::PACK_SHRINK, 0);
NURBSbbox->pack_end (*loadNURBS, Gtk::PACK_SHRINK, 0);
NURBSbbox->pack_start(*editPointNURBS, Gtk::PACK_SHRINK, 0);
NURBSbbox->pack_start(*editNURBS, Gtk::PACK_SHRINK, 0);
NURBSCurveAndButtons->pack_start (*NURBSCurve, Gtk::PACK_EXPAND_WIDGET, 0);
@ -153,12 +173,21 @@ DiagonalCurveEditorSubGroup::DiagonalCurveEditorSubGroup (CurveEditorGroup* prt,
} else if (options.curvebboxpos==3) {
NURBSCurveAndButtons->reorder_child(*NURBSbbox, 0);
}
NURBSCoordAdjuster = Gtk::manage (new CoordinateAdjuster(NURBSCurve, this));
NURBSCurveBox->pack_start(*NURBSCoordAdjuster, Gtk::PACK_SHRINK, 0);
if (options.curvebboxpos == 2)
NURBSCurveBox->reorder_child(*NURBSCoordAdjuster, 2);
NURBSCoordAdjuster->show_all();
NURBSCurveBox->show_all ();
NURBSCoordAdjuster->hide();
saveNURBS->signal_clicked().connect( sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::savePressed) );
loadNURBS->signal_clicked().connect( sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::loadPressed) );
pasteNURBS->signal_clicked().connect( sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::pastePressed) );
copyNURBS->signal_clicked().connect( sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::copyPressed) );
editPointNURBSConn = editPointNURBS->signal_toggled().connect( sigc::bind(sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::editPointToggled), editPointNURBS) );
editNURBSConn = editNURBS->signal_toggled().connect( sigc::bind(sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::editToggled), editNURBS) );
saveNURBS->set_tooltip_text (M("CURVEEDITOR_TOOLTIPSAVE"));
@ -320,17 +349,17 @@ void DiagonalCurveEditorSubGroup::editModeSwitchedOff () {
bool prevState;
prevState = editCustomConn.block(true);
editCustom->set_active(false);
customCurve->pipetteMouseOver(NULL, 0);
customCurve->pipetteMouseOver(NULL, NULL, 0);
customCurve->setDirty(true);
if (!prevState) editCustomConn.block(false);
prevState = editNURBSConn.block(true);
editNURBS->set_active(false);
NURBSCurve->pipetteMouseOver(NULL, 0);
NURBSCurve->pipetteMouseOver(NULL, NULL, 0);
NURBSCurve->setDirty(true);
if (!prevState) editNURBSConn.block(false);
prevState = editParamConn.block(true);
editParam->set_active(false);
paramCurve->pipetteMouseOver(NULL, 0);
paramCurve->pipetteMouseOver(NULL, NULL, 0);
paramCurve->setDirty(true);
if (!prevState) editParamConn.block(false);
}
@ -339,12 +368,12 @@ void DiagonalCurveEditorSubGroup::pipetteMouseOver(EditDataProvider *provider, i
CurveEditor *curveEditor = static_cast<DiagonalCurveEditor*>(parent->displayedCurve);
switch((DiagonalCurveType)(curveEditor->curveType->getSelected())) {
case (DCT_Spline):
customCurve->pipetteMouseOver(provider, modifierKey);
customCurve->pipetteMouseOver(curveEditor, provider, modifierKey);
customCurve->setDirty(true);
break;
case (DCT_Parametric):
{
paramCurve->pipetteMouseOver(provider, modifierKey);
paramCurve->pipetteMouseOver(curveEditor, provider, modifierKey);
paramCurve->setDirty(true);
float pipetteVal = 0.f;
editedAdjuster = NULL;
@ -392,7 +421,7 @@ void DiagonalCurveEditorSubGroup::pipetteMouseOver(EditDataProvider *provider, i
}
break;
case (DCT_NURBS):
NURBSCurve->pipetteMouseOver(provider, modifierKey);
NURBSCurve->pipetteMouseOver(curveEditor, provider, modifierKey);
NURBSCurve->setDirty(true);
break;
default: // (DCT_Linear, DCT_Unchanged)
@ -462,6 +491,20 @@ void DiagonalCurveEditorSubGroup::pipetteDrag(EditDataProvider *provider, int mo
}
}
void DiagonalCurveEditorSubGroup::showCoordinateAdjuster(CoordinateProvider *provider) {
if (provider == customCurve) {
if (!editPointCustom->get_active()) editPointCustom->set_active(true);
}
else if (provider == NURBSCurve) {
if (!editPointNURBS->get_active()) editPointNURBS->set_active(true);
}
}
void DiagonalCurveEditorSubGroup::stopNumericalAdjustment() {
customCurve->stopNumericalAdjustment();
NURBSCurve->stopNumericalAdjustment();
}
/*
* Force the resize of the curve editor, if the displayed one is the requested one
*/
@ -773,6 +816,31 @@ void DiagonalCurveEditorSubGroup::pastePressed () {
return;
}
void DiagonalCurveEditorSubGroup::editPointToggled(Gtk::ToggleButton *button) {
if (button->get_active()) {
customCoordAdjuster->show();
NURBSCoordAdjuster->show();
}
else {
if (customCoordAdjuster) {
customCurve->stopNumericalAdjustment();
customCoordAdjuster->hide();
NURBSCurve->stopNumericalAdjustment();
NURBSCoordAdjuster->hide();
}
}
if (button == editPointCustom) {
editPointNURBSConn.block(true);
editPointNURBS->set_active(!editPointNURBS->get_active());
editPointNURBSConn.block(false);
}
else {
editPointCustomConn.block(true);
editPointCustom->set_active(!editPointCustom->get_active());
editPointCustomConn.block(false);
}
}
void DiagonalCurveEditorSubGroup::editToggled (Gtk::ToggleButton *button) {
DiagonalCurveEditor* dCurve = static_cast<DiagonalCurveEditor*>(parent->displayedCurve);
if (!dCurve)

View File

@ -46,18 +46,23 @@ protected:
Adjuster *editedAdjuster;
int editedAdjusterValue;
CoordinateAdjuster *customCoordAdjuster;
CoordinateAdjuster *NURBSCoordAdjuster;
Gtk::Button* saveCustom;
Gtk::Button* loadCustom;
Gtk::Button* copyCustom;
Gtk::Button* pasteCustom;
Gtk::ToggleButton* editPointCustom;
Gtk::ToggleButton* editCustom;
sigc::connection editCustomConn;
sigc::connection editCustomConn, editPointCustomConn;
Gtk::Button* saveNURBS;
Gtk::Button* loadNURBS;
Gtk::Button* copyNURBS;
Gtk::Button* pasteNURBS;
Gtk::ToggleButton* editPointNURBS;
Gtk::ToggleButton* editNURBS;
sigc::connection editNURBSConn;
sigc::connection editNURBSConn, editPointNURBSConn;
Gtk::Button* saveParam;
Gtk::Button* loadParam;
Gtk::Button* copyParam;
@ -80,6 +85,8 @@ public:
void pipetteButton1Pressed(EditDataProvider *provider, int modifierKey);
void pipetteButton1Released(EditDataProvider *provider);
void pipetteDrag(EditDataProvider *provider, int modifierKey);
void showCoordinateAdjuster(CoordinateProvider *provider);
void stopNumericalAdjustment();
bool curveReset (CurveEditor *ce);
@ -91,6 +98,7 @@ protected:
void loadPressed ();
void copyPressed ();
void pastePressed ();
void editPointToggled(Gtk::ToggleButton *button);
void editToggled (Gtk::ToggleButton *button);
void removeEditor ();
const std::vector<double> getCurveFromGUI (int type);

View File

@ -28,6 +28,7 @@
#include "shcselector.h"
#include "adjuster.h"
#include "mycurve.h"
#include "myflatcurve.h"
#include "curveeditor.h"
#include "flatcurveeditorsubgroup.h"
@ -37,6 +38,8 @@ FlatCurveEditorSubGroup::FlatCurveEditorSubGroup (CurveEditorGroup* prt, Glib::u
valUnchanged = (int)FCT_Unchanged;
parent = prt;
curveBBoxPos = options.curvebboxpos;
// ControlPoints curve
CPointsCurveBox = new Gtk::VBox ();
CPointsCurveBox->set_spacing(4);
@ -63,6 +66,9 @@ FlatCurveEditorSubGroup::FlatCurveEditorSubGroup (CurveEditorGroup* prt, Glib::u
loadCPoints = Gtk::manage (new Gtk::Button ());
loadCPoints->add (*Gtk::manage (new RTImage ("gtk-open.png")));
editCPoints = Gtk::manage (new Gtk::ToggleButton());
editPointCPoints = Gtk::manage (new Gtk::ToggleButton ());
editPointCPoints->add (*Gtk::manage (new RTImage ("gtk-edit.png")));
editPointCPoints->set_tooltip_text(M("CURVEEDITOR_EDITPOINT_HINT"));
editCPoints->add (*Gtk::manage (new RTImage ("editmodehand.png")));
editCPoints->set_tooltip_text(M("EDIT_PIPETTE_TOOLTIP"));
editCPoints->hide();
@ -71,6 +77,7 @@ FlatCurveEditorSubGroup::FlatCurveEditorSubGroup (CurveEditorGroup* prt, Glib::u
CPointsbbox->pack_end (*copyCPoints, Gtk::PACK_SHRINK, 0);
CPointsbbox->pack_end (*saveCPoints, Gtk::PACK_SHRINK, 0);
CPointsbbox->pack_end (*loadCPoints, Gtk::PACK_SHRINK, 0);
CPointsbbox->pack_start(*editPointCPoints, Gtk::PACK_SHRINK, 0);
CPointsbbox->pack_start(*editCPoints, Gtk::PACK_SHRINK, 0);
CPointsCurveAndButtons->pack_start (*CPointsCurve, Gtk::PACK_EXPAND_WIDGET, 0);
@ -86,12 +93,29 @@ FlatCurveEditorSubGroup::FlatCurveEditorSubGroup (CurveEditorGroup* prt, Glib::u
} else if (options.curvebboxpos==3) {
CPointsCurveAndButtons->reorder_child(*CPointsbbox, 0);
}
{
std::vector<Axis> axis;
axis.resize(4);
axis.at(0).setValues(M("CURVEEDITOR_AXIS_IN"), 5, 0.001, 0.01, 0., 1.);
axis.at(1).setValues(M("CURVEEDITOR_AXIS_OUT"), 5, 0.001, 0.01, 0., 1.);
axis.at(2).setValues(M("CURVEEDITOR_AXIS_LEFT_TAN"), 5, 0.01, 0.1, 0., 1.);
axis.at(3).setValues(M("CURVEEDITOR_AXIS_RIGHT_TAN"), 5, 0.01, 0.1, 0., 1.);
CPointsCoordAdjuster = Gtk::manage (new CoordinateAdjuster(CPointsCurve, this, axis));
CPointsCurveBox->pack_start(*CPointsCoordAdjuster, Gtk::PACK_SHRINK, 0);
if (options.curvebboxpos == 2)
CPointsCurveBox->reorder_child(*CPointsCoordAdjuster, 2);
CPointsCoordAdjuster->show_all();
}
CPointsCurveBox->show_all ();
CPointsCoordAdjuster->hide();
saveCPoints->signal_clicked().connect( sigc::mem_fun(*this, &FlatCurveEditorSubGroup::savePressed) );
loadCPoints->signal_clicked().connect( sigc::mem_fun(*this, &FlatCurveEditorSubGroup::loadPressed) );
copyCPoints->signal_clicked().connect( sigc::mem_fun(*this, &FlatCurveEditorSubGroup::copyPressed) );
pasteCPoints->signal_clicked().connect( sigc::mem_fun(*this, &FlatCurveEditorSubGroup::pastePressed) );
editPointCPointsConn = editPointCPoints->signal_toggled().connect( sigc::bind(sigc::mem_fun(*this, &FlatCurveEditorSubGroup::editPointToggled), editPointCPoints) );
editCPointsConn = editCPoints->signal_toggled().connect( sigc::bind(sigc::mem_fun(*this, &FlatCurveEditorSubGroup::editToggled), editCPoints) );
saveCPoints->set_tooltip_text (M("CURVEEDITOR_TOOLTIPSAVE"));
@ -117,6 +141,17 @@ FlatCurveEditor* FlatCurveEditorSubGroup::addCurve(Glib::ustring curveLabel, boo
return newCE;
}
void FlatCurveEditorSubGroup::showCoordinateAdjuster(CoordinateProvider *provider) {
if (provider == CPointsCurve) {
if (!editPointCPoints->get_active()) editPointCPoints->set_active(true);
}
}
void FlatCurveEditorSubGroup::stopNumericalAdjustment() {
CPointsCurve->stopNumericalAdjustment();
}
/*
* Force the resize of the curve editor, if the displayed one is the requested one
*/
@ -140,7 +175,7 @@ void FlatCurveEditorSubGroup::editModeSwitchedOff () {
// toggling off all edit buttons, even if only one is toggle on
bool prevState = editCPointsConn.block(true);
editCPoints->set_active(false);
CPointsCurve->pipetteMouseOver(NULL, 0);
CPointsCurve->pipetteMouseOver(NULL, NULL, 0);
CPointsCurve->setDirty(true);
if (!prevState) editCPointsConn.block(false);
}
@ -149,7 +184,7 @@ void FlatCurveEditorSubGroup::pipetteMouseOver(EditDataProvider *provider, int m
CurveEditor *curveEditor = static_cast<FlatCurveEditor*>(parent->displayedCurve);
switch((FlatCurveType)(curveEditor->curveType->getSelected())) {
case (FCT_MinMaxCPoints):
CPointsCurve->pipetteMouseOver(provider, modifierKey);
CPointsCurve->pipetteMouseOver(curveEditor, provider, modifierKey);
CPointsCurve->setDirty(true);
break;
default: // (DCT_Linear, DCT_Unchanged)
@ -380,6 +415,15 @@ void FlatCurveEditorSubGroup::pastePressed () {
return;
}
void FlatCurveEditorSubGroup::editPointToggled(Gtk::ToggleButton *button) {
if (button->get_active())
CPointsCoordAdjuster->show();
else {
CPointsCurve->stopNumericalAdjustment();
CPointsCoordAdjuster->hide();
}
}
void FlatCurveEditorSubGroup::editToggled (Gtk::ToggleButton *button) {
FlatCurveEditor* dCurve = static_cast<FlatCurveEditor*>(parent->displayedCurve);
if (!dCurve)

View File

@ -33,12 +33,15 @@ protected:
MyFlatCurve* CPointsCurve;
CoordinateAdjuster *CPointsCoordAdjuster;
Gtk::Button* saveCPoints;
Gtk::Button* loadCPoints;
Gtk::Button* copyCPoints;
Gtk::Button* pasteCPoints;
Gtk::ToggleButton* editPointCPoints;
Gtk::ToggleButton* editCPoints;
sigc::connection editCPointsConn;
sigc::connection editCPointsConn, editPointCPointsConn;
public:
FlatCurveEditorSubGroup(CurveEditorGroup* prt, Glib::ustring& curveDir);
@ -53,6 +56,8 @@ public:
void pipetteButton1Pressed(EditDataProvider *provider, int modifierKey);
void pipetteButton1Released(EditDataProvider *provider);
void pipetteDrag(EditDataProvider *provider, int modifierKey);
void showCoordinateAdjuster(CoordinateProvider *provider);
void stopNumericalAdjustment();
bool curveReset (CurveEditor *ce);
@ -66,6 +71,7 @@ protected:
void pastePressed ();
void removeEditor ();
const std::vector<double> getCurveFromGUI (int type);
void editPointToggled(Gtk::ToggleButton *button);
void editToggled (Gtk::ToggleButton *button);
};

View File

@ -36,6 +36,7 @@ MyCurve::MyCurve () : pipetteR(-1.f), pipetteG(-1.f), pipetteB(-1.f), pipetteVal
sized = RS_Pending;
snapToElmt = -100;
curveIsDirty = true;
edited_point = -1;
set_extension_events(Gdk::EXTENSION_EVENTS_ALL);
#if defined (__APPLE__)
@ -114,6 +115,15 @@ bool MyCurve::snapCoordinateY(double testedVal, double realVal) {
return false;
}
float MyCurve::getVal(LUTf &curve, int x) {
if ((graphW-2) == curve.getSize()) {
return curve[x];
}
else {
return curve.getVal01(float(x)/(graphW-3));
}
}
void MyCurve::on_style_changed (const Glib::RefPtr<Gtk::Style>& style) {
setDirty(true);
queue_draw ();

View File

@ -24,6 +24,7 @@
#include "curvelistener.h"
#include "cursormanager.h"
#include "coloredbar.h"
#include "coordinateadjuster.h"
#include "../rtengine/LUT.h"
#include "guiutils.h"
#include "options.h"
@ -59,8 +60,9 @@ enum ResizeState {
};
class MyCurveIdleHelper;
class CurveEditor;
class MyCurve : public Gtk::DrawingArea, public BackBuffer, public ColorCaller {
class MyCurve : public Gtk::DrawingArea, public BackBuffer, public ColorCaller, public CoordinateProvider {
friend class MyCurveIdleHelper;
@ -72,14 +74,14 @@ class MyCurve : public Gtk::DrawingArea, public BackBuffer, public ColorCaller {
ColoredBar *leftBar;
ColoredBar *bottomBar;
CursorShape cursor_type;
int graphX, graphY, graphW, graphH; /// dimensions of the graphic area, excluding surrounding space for the points of for the colored bar
int graphX, graphY, graphW, graphH; /// position and dimensions of the graphic area, excluding surrounding space for the points of for the colored bar
int prevGraphW, prevGraphH; /// previous inner width and height of the editor
Gdk::ModifierType mod_type;
int cursorX; /// X coordinate in the graph of the cursor
int cursorY; /// Y coordinate in the graph of the cursor
std::vector< Point<float> > point;
std::vector< Point<float> > upoint;
std::vector< Point<float> > lpoint;
LUTf point;
LUTf upoint;
LUTf lpoint;
bool buttonPressed;
/**
* snapToElmt, which will be used for the Y axis only, must be interpreted like this:
@ -98,10 +100,14 @@ class MyCurve : public Gtk::DrawingArea, public BackBuffer, public ColorCaller {
enum ResizeState sized;
bool curveIsDirty;
int edited_point; // > -1 when a point is being numerically edited
std::vector<double> editedPos;
virtual std::vector<double> get_vector (int veclen) = 0;
int getGraphMinSize() { return GRAPH_SIZE + RADIUS + 1; }
bool snapCoordinateX(double testedVal, double realVal);
bool snapCoordinateY(double testedVal, double realVal);
float getVal(LUTf &curve, int x);
// return value = new requested height
int calcDimensions ();
@ -123,7 +129,7 @@ class MyCurve : public Gtk::DrawingArea, public BackBuffer, public ColorCaller {
virtual bool handleEvents (GdkEvent* event) = 0;
virtual void reset (const std::vector<double> &resetCurve, double identityValue=0.5) = 0;
virtual void pipetteMouseOver (EditDataProvider *provider, int modifierKey) =0;
virtual void pipetteMouseOver (CurveEditor *ce, EditDataProvider *provider, int modifierKey) =0;
virtual void pipetteButton1Pressed(EditDataProvider *provider, int modifierKey) =0;
virtual void pipetteButton1Released(EditDataProvider *provider) =0;
virtual void pipetteDrag(EditDataProvider *provider, int modifierKey) =0;

View File

@ -33,6 +33,9 @@ MyDiagonalCurve::MyDiagonalCurve () : activeParam(-1), bghistvalid(false) {
bghist = new unsigned int[256];
editedPos.resize(2);
editedPos.at(0) = editedPos.at(1) = 0.0;
signal_event().connect( sigc::mem_fun(*this, &MyDiagonalCurve::handleEvents) );
curve.type = DCT_Spline;
@ -82,47 +85,77 @@ std::vector<double> MyDiagonalCurve::get_vector (int veclen) {
// calculate remaining points
std::vector<double> curveDescr = getPoints ();
rtengine::DiagonalCurve* rtcurve = new rtengine::DiagonalCurve (curveDescr, veclen*1.2);
rtengine::DiagonalCurve rtcurve(curveDescr, veclen*1.2);
std::vector<double> t;
t.resize (veclen);
for (int i = 0; i < veclen; i++)
t[i] = (double) i / (veclen - 1.0);
rtcurve->getVal (t, vector);
delete rtcurve;
rtcurve.getVal (t, vector);
return vector;
}
void MyDiagonalCurve::get_LUT (LUTf &lut) {
int size = lut.getSize();
if (curve.type != DCT_Parametric) {
// count active points:
double prev =- 1.0;
int active = 0;
int firstact = -1;
for (int i = 0; i < (int)curve.x.size(); ++i)
if (curve.x.at(i) > prev) {
if (firstact < 0)
firstact = i;
prev = curve.x.at(i);
++active;
}
// handle degenerate case:
if (active < 2) {
double ry;
if (active > 0)
ry = curve.y.at(firstact);
else
ry = 0.0;
if (ry < 0.0) ry = 0.0;
if (ry > 1.0) ry = 1.0;
for (int x = 0; x < size; ++x)
lut[x] = ry;
return;
}
}
// calculate remaining points
std::vector<double> curveDescr = getPoints ();
rtengine::DiagonalCurve rtcurve(curveDescr, lut.getUpperBound()*1.2);
double t;
double maxVal = double(lut.getUpperBound());
for (int i = 0; i < size; i++) {
t = double(i) / maxVal;
lut[i] = rtcurve.getVal (t);
}
return;
}
void MyDiagonalCurve::interpolate () {
prevGraphW = graphW;
prevGraphH = graphH;
int nbPoints = graphW-2;
point.resize (nbPoints);
std::vector<double> vector = get_vector (nbPoints);
for (int i = 0; i < nbPoints; ++i) {
float currX = float(i)/float(nbPoints-1);
point.at(i).setCoords(float(graphX)+1.5f+float(graphW-3)*currX, float(graphY)-1.5f-float(graphH-3)*float(vector.at(i)));
}
upoint.clear ();
lpoint.clear ();
int nbPoints = rtengine::max(graphW-2,201);
point(nbPoints);
get_LUT (point);
upoint.reset();
lpoint.reset ();
if (curve.type==DCT_Parametric && activeParam>0) {
double tmp = curve.x.at(activeParam-1);
if (activeParam>=4) {
upoint.resize(nbPoints);
lpoint.resize(nbPoints);
upoint(nbPoints);
lpoint(nbPoints);
curve.x.at(activeParam-1) = 100;
vector = get_vector (nbPoints);
for (int i = 0; i < nbPoints; ++i) {
float currX = float(i)/float(nbPoints-1);
upoint.at(i).setCoords(float(graphX)+1.5f+float(graphW-3)*currX, float(graphY)-1.5f-float(graphH-3)*float(vector.at(i)));
}
get_LUT(upoint);
curve.x.at(activeParam-1) = -100;
vector = get_vector (nbPoints);
for (int i = 0; i < nbPoints; ++i) {
float currX = float(i)/float(nbPoints-1);
lpoint.at(i).setCoords(float(graphX)+1.5f+float(graphW-3)*currX, float(graphY)-1.5f-float(graphH-3)*float(vector.at(i)));
}
get_LUT (lpoint);
curve.x.at(activeParam-1) = tmp;
}
}
@ -140,8 +173,10 @@ void MyDiagonalCurve::draw (int handle) {
return;
// re-calculate curve if dimensions changed
if (curveIsDirty || prevGraphW != graphW || prevGraphH != graphH || int(point.size()) != graphW-2)
int currPointSize = point.getUpperBound();
if (curveIsDirty || /*prevGraphW != graphW || prevGraphH != graphH ||*/ (currPointSize==200 && (graphW-3>200)) || (currPointSize>200 && (graphW-2<=200 || graphW-3!=currPointSize)))
interpolate ();
currPointSize = point.getUpperBound();
Gtk::StateType state = !is_sensitive() ? Gtk::STATE_INSENSITIVE : Gtk::STATE_NORMAL;
@ -214,15 +249,16 @@ void MyDiagonalCurve::draw (int handle) {
cr->set_line_width (1.0);
// draw upper and lower bounds
if (curve.type==DCT_Parametric && activeParam>0 && lpoint.size()>1 && upoint.size()>1) {
float graphH_ = float(graphH-3);
float graphX_ = float(graphX)+1.5;
float graphY_ = float(graphY)-1.5;
if (curve.type==DCT_Parametric && activeParam>0 && lpoint.getUpperBound()>1 && upoint.getUpperBound()>1) {
cr->set_source_rgba (0.0, 0.0, 0.0, 0.15);
cr->move_to (upoint[0].x, upoint[0].y);
for (int i=1; i<(int)upoint.size(); i++)
cr->line_to (upoint[i].x, upoint[i].y);
cr->line_to (lpoint[lpoint.size()-1].x, lpoint[lpoint.size()-1].y);
for (int i=(int)lpoint.size()-2; i>=0; i--)
cr->line_to (lpoint[i].x, lpoint[i].y);
cr->line_to (upoint[0].x, upoint[0].y);
cr->move_to (graphX_, getVal(upoint, 0)*-graphH_+graphY_);
for (int i=1; i<graphW-2; ++i)
cr->line_to (float(i)+graphX_, getVal(upoint, i)*-graphH_+graphY_);
for (int i=graphW-3; i>=0; --i)
cr->line_to (float(i)+graphX_, getVal(lpoint, i)*-graphH_+graphY_);
cr->fill ();
}
@ -299,10 +335,9 @@ void MyDiagonalCurve::draw (int handle) {
// draw curve
cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p());
cr->move_to (double(point.at(0).x), double(point.at(0).y));
for (int i=1; i<(int)point.size(); i++) {
cr->line_to (double(point.at(i).x), double(point.at(i).y));
}
cr->move_to (graphX_, getVal(point, 0)*-graphH_+graphY_);
for (int i=1; i<graphW-2; ++i)
cr->line_to (float(i)+graphX_, getVal(point, i)*-graphH_+graphY_);
cr->stroke ();
// draw the left colored bar
@ -348,7 +383,7 @@ void MyDiagonalCurve::draw (int handle) {
cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p());
}
else {
if (i == handle || i == snapToElmt)
if (i == handle || i == snapToElmt || i == edited_point)
cr->set_source_rgb (1.0, 0.0, 0.0);
else
cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p());
@ -359,6 +394,14 @@ void MyDiagonalCurve::draw (int handle) {
cr->arc (x, y, RADIUS+0.5, 0, 2*M_PI);
cr->fill ();
if (i == edited_point) {
cr->set_line_width(2.);
cr->arc (x, y, RADIUS+3.5, 0, 2*M_PI);
cr->stroke();
cr->set_line_width(1.);
}
}
}
setDirty(false);
@ -408,6 +451,7 @@ bool MyDiagonalCurve::handleEvents (GdkEvent* event) {
// setDrawRectangle will allocate the backbuffer Surface
if (setDrawRectangle(win, 0, 0, get_allocation().get_width(), get_allocation().get_height())) {
setDirty(true);
if (prevGraphW > 200 || graphW > 200)
curveIsDirty = true;
}
draw (lit_point);
@ -421,6 +465,7 @@ bool MyDiagonalCurve::handleEvents (GdkEvent* event) {
case Gdk::BUTTON_PRESS:
snapToElmt = -100;
if (curve.type!=DCT_Parametric) {
if (edited_point == -1) {
if (event->button.button == 1) {
std::vector<double>::iterator itx, ity;
buttonPressed = true;
@ -432,6 +477,9 @@ bool MyDiagonalCurve::handleEvents (GdkEvent* event) {
new_type = CSMove;
if (distanceX > minDistanceX) {
if (mod_type & GDK_CONTROL_MASK) {
clampedY = point.getVal01(clampedX);
}
/* insert a new control point */
if (num > 0) {
if (clampedX > curve.x.at(closest_point))
@ -444,7 +492,7 @@ bool MyDiagonalCurve::handleEvents (GdkEvent* event) {
curve.y.insert (ity, 0);
num++;
// the graph is refreshed only if a new point is created (snaped to a pixel)
// the graph is refreshed only if a new point is created
curve.x.at(closest_point) = clampedX;
curve.y.at(closest_point) = clampedY;
@ -458,13 +506,77 @@ bool MyDiagonalCurve::handleEvents (GdkEvent* event) {
ugpX = curve.x.at(closest_point);
ugpY = curve.y.at(closest_point);
}
else if (event->button.button == 3) {
if (lit_point>-1 && grab_point==-1) {
if (!coordinateAdjuster->is_visible())
coordinateAdjuster->showMe(this);
edited_point = lit_point;
std::vector<CoordinateAdjuster::Boundaries> newBoundaries(2);
unsigned int size = curve.x.size();
if (edited_point == 0) { newBoundaries.at(0).minVal = 0.; newBoundaries.at(0).maxVal = curve.x.at(1); }
else if (edited_point == size-1) { newBoundaries.at(0).minVal = curve.x.at(edited_point-1); newBoundaries.at(0).maxVal = 1.; }
else if (curve.x.size() > 2) { newBoundaries.at(0).minVal = curve.x.at(edited_point-1); newBoundaries.at(0).maxVal = curve.x.at(edited_point+1); }
newBoundaries.at(1).minVal = 0.; newBoundaries.at(1).maxVal = 1.;
editedPos.at(0) = curve.x.at(edited_point);
editedPos.at(1) = curve.y.at(edited_point);
coordinateAdjuster->setPos(editedPos);
coordinateAdjuster->startNumericalAdjustment(newBoundaries);
setDirty(true);
draw (lit_point);
new_type = CSArrow;
retval = true;
}
}
if (buttonPressed) retval = true;
}
else { // if (edited_point > -1)
if (event->button.button == 3) {
// do we edit another point?
if (edited_point>-1 && grab_point==-1) {
/* get the pointer position */
getCursorPosition(Gdk::EventType(event->type), event->motion.is_hint!=0, int(event->button.x), int(event->button.y), Gdk::ModifierType(event->button.state));
findClosestPoint();
if (cursorX>=0 && cursorX<=graphW && cursorY>=0 && cursorY<=graphH) {
if (distanceX <= minDistanceX) {
// the cursor is close to an existing point
lit_point = closest_point;
if (lit_point != edited_point) {
edited_point = lit_point;
curveIsDirty = true;
setDirty(true);
draw (lit_point);
std::vector<CoordinateAdjuster::Boundaries> newBoundaries;
newBoundaries.resize(2);
unsigned int size = curve.x.size();
if (edited_point == 0) { newBoundaries.at(0).minVal = 0.; newBoundaries.at(0).maxVal = curve.x.at(1); }
else if (edited_point == size-1) { newBoundaries.at(0).minVal = curve.x.at(edited_point-1); newBoundaries.at(0).maxVal = 1.; }
else if (curve.x.size() > 2) { newBoundaries.at(0).minVal = curve.x.at(edited_point-1); newBoundaries.at(0).maxVal = curve.x.at(edited_point+1); }
newBoundaries.at(1).minVal = 0.; newBoundaries.at(1).maxVal = 1.;
retval = true;
editedPos.at(0) = curve.x.at(edited_point);
editedPos.at(1) = curve.y.at(edited_point);
coordinateAdjuster->switchAdjustedPoint(editedPos, newBoundaries);
}
}
else {
// the cursor is inside the graph but away from existing points
new_type = CSPlus;
curveIsDirty = true;
stopNumericalAdjustment();
}
}
retval = true;
}
}
}
retval = true;
}
break;
case Gdk::BUTTON_RELEASE:
snapToElmt = -100;
if (curve.type!=DCT_Parametric) {
if (curve.type!=DCT_Parametric && edited_point==-1) {
if (buttonPressed && event->button.button == 1) {
std::vector<double>::iterator itx, ity;
int src, dst;
@ -542,6 +654,7 @@ bool MyDiagonalCurve::handleEvents (GdkEvent* event) {
getCursorPosition(Gdk::EventType(event->type), event->motion.is_hint!=0, int(event->button.x), int(event->button.y), Gdk::ModifierType(event->button.state));
if (grab_point == -1) {
if (edited_point==-1) {
// there's no point currently being moved
int previous_lit_point = lit_point;
findClosestPoint();
@ -551,16 +664,46 @@ bool MyDiagonalCurve::handleEvents (GdkEvent* event) {
lit_point = -1;
}
else if (distanceX <= minDistanceX) {
// the cursor is close to an existing point
new_type = CSMove;
lit_point = closest_point;
}
else {
// the cursor is inside the graph but away from existing points
new_type = CSPlus;
lit_point = -1;
}
if (lit_point != previous_lit_point) {
setDirty(true);
draw (lit_point);
if (lit_point > -1) {
editedPos.at(0) = curve.x.at(lit_point);
editedPos.at(1) = curve.y.at(lit_point);
}
coordinateAdjuster->setPos(editedPos);
}
if (lit_point == -1 && new_type == CSPlus) {
editedPos.at(0) = clampedX;
editedPos.at(1) = clampedY;
coordinateAdjuster->setPos(editedPos);
}
}
else { // if (edited_point > -1)
// there's no point currently being moved
int previous_lit_point = lit_point;
findClosestPoint();
if (distanceX <= minDistanceX) {
// the cursor is close to an existing point
lit_point = closest_point;
}
else {
// the cursor is outside the graph or inside the graph but away from existing points
lit_point = -1;
}
if (lit_point != previous_lit_point) {
setDirty(true);
draw (lit_point);
}
}
}
else {
@ -646,6 +789,12 @@ bool MyDiagonalCurve::handleEvents (GdkEvent* event) {
setDirty(true);
draw (lit_point);
notifyListener ();
if (coordinateAdjuster->is_visible()) {
editedPos.at(0) = curve.x.at(grab_point);
editedPos.at(1) = curve.y.at(grab_point);
coordinateAdjuster->setPos(editedPos);
}
}
}
}
@ -669,7 +818,7 @@ CursorShape MyDiagonalCurve::motionNotify(CursorShape type, double minDistanceX,
return new_type;
}
void MyDiagonalCurve::pipetteMouseOver (EditDataProvider *provider, int modifierKey) {
void MyDiagonalCurve::pipetteMouseOver (CurveEditor *ce, EditDataProvider *provider, int modifierKey) {
if (!provider) {
// occurs when leaving the preview area -> cleanup the curve editor
pipetteR = pipetteG = pipetteB = -1.f;
@ -681,8 +830,9 @@ void MyDiagonalCurve::pipetteMouseOver (EditDataProvider *provider, int modifier
pipetteG = provider->pipetteVal[1];
pipetteB = provider->pipetteVal[2];
pipetteVal = 0.f;
if (listener)
pipetteVal = listener->blendPipetteValues(pipetteR, pipetteG, pipetteB);
if (listener) {
pipetteVal = listener->blendPipetteValues(ce, pipetteR, pipetteG, pipetteB);
}
else {
int n = 0;
if (pipetteR != -1.f) {
@ -713,9 +863,9 @@ void MyDiagonalCurve::pipetteMouseOver (EditDataProvider *provider, int modifier
if (curve.type == DCT_Linear || curve.type == DCT_Spline || curve.type == DCT_NURBS) {
// get the pointer position
int px = graphX + int(float(graphW)*pipetteVal); // WARNING: converting pipetteVal from float to int, precision loss here!
getCursorPosition(Gdk::MOTION_NOTIFY, false, px, graphY, Gdk::ModifierType(modifierKey));
getCursorPositionFromCurve(pipetteVal);
if (edited_point==-1) {
if (grab_point == -1) {
// there's no point currently being moved
int previous_lit_point = lit_point;
@ -736,10 +886,22 @@ void MyDiagonalCurve::pipetteMouseOver (EditDataProvider *provider, int modifier
}
}
}
else
draw(lit_point);
if (edited_point==-1) {
editedPos.at(0) = pipetteVal;
editedPos.at(1) = point.getVal01(pipetteVal);
coordinateAdjuster->setPos(editedPos);
}
}
}
void MyDiagonalCurve::pipetteButton1Pressed(EditDataProvider *provider, int modifierKey) {
if (edited_point>1)
return;
int num = (int)curve.x.size();
/* graphW and graphH are the size of the graph */
@ -756,13 +918,10 @@ void MyDiagonalCurve::pipetteButton1Pressed(EditDataProvider *provider, int modi
buttonPressed = true;
// get the pointer position
int px = graphX + int(float(graphW)*pipetteVal); // WARNING: converting pipetteVal from float to int, precision loss here!
getCursorPosition(Gdk::BUTTON_PRESS, false, px, graphY, Gdk::ModifierType(modifierKey));
getCursorPositionFromCurve(pipetteVal);
findClosestPoint();
if (distanceX > minDistanceX) {
rtengine::DiagonalCurve rtCurve(getPoints(), 200);
/* insert a new control point */
if (num > 0) {
if (clampedX > curve.x.at(closest_point))
@ -771,17 +930,25 @@ void MyDiagonalCurve::pipetteButton1Pressed(EditDataProvider *provider, int modi
itx = curve.x.begin();
ity = curve.y.begin();
for (int i=0; i<closest_point; i++) { itx++; ity++; }
lit_point = closest_point;
curve.x.insert (itx, 0);
curve.y.insert (ity, 0);
num++;
// the graph is refreshed only if a new point is created (snaped to a pixel)
curve.x.at(closest_point) = clampedX;
curve.y.at(closest_point) = clampedY = rtCurve.getVal(pipetteVal);
// the graph is refreshed only if a new point is created (snapped to a pixel)
curve.x.at(lit_point) = clampedX;
curve.y.at(lit_point) = clampedY;
if (lit_point>-1 && grab_point==-1 && coordinateAdjuster->is_visible()) {
std::vector<double> position;
position.resize(2);
position.at(0) = clampedX;
position.at(1) = clampedY;
coordinateAdjuster->setPos(position);
}
curveIsDirty = true;
setDirty(true);
draw (closest_point);
draw (lit_point);
notifyListener ();
}
grab_point = closest_point;
@ -792,6 +959,9 @@ void MyDiagonalCurve::pipetteButton1Pressed(EditDataProvider *provider, int modi
}
void MyDiagonalCurve::pipetteButton1Released(EditDataProvider *provider) {
if (edited_point>1)
return;
/* graphW and graphH are the size of the graph */
calcDimensions();
@ -827,7 +997,7 @@ void MyDiagonalCurve::pipetteButton1Released(EditDataProvider *provider) {
}
void MyDiagonalCurve::pipetteDrag(EditDataProvider *provider, int modifierKey) {
if (curve.type==DCT_Parametric || graphW<0 || graphH<0)
if (edited_point>-1 || curve.type==DCT_Parametric || graphW<0 || graphH<0)
return;
snapToMinDistY = snapToMinDistX = 10.;
@ -837,7 +1007,7 @@ void MyDiagonalCurve::pipetteDrag(EditDataProvider *provider, int modifierKey) {
/* graphW and graphH are the size of the graph */
calcDimensions();
getCursorPosition(Gdk::MOTION_NOTIFY, false, cursorX+graphX, graphY+provider->deltaScreen.y, Gdk::ModifierType(modifierKey));
getCursorPosition(Gdk::MOTION_NOTIFY, false, cursorX+graphX, graphY-cursorY+provider->deltaPrevScreen.y, Gdk::ModifierType(modifierKey));
// we memorize the previous position of the point, for optimization purpose
double prevPosX = curve.x.at(grab_point);
@ -888,7 +1058,36 @@ void MyDiagonalCurve::pipetteDrag(EditDataProvider *provider, int modifierKey) {
setDirty(true);
draw (lit_point);
notifyListener ();
if (lit_point>-1 && coordinateAdjuster->is_visible()) {
std::vector<double> position;
position.resize(2);
position.at(0) = curve.x.at(grab_point);
position.at(1) = curve.y.at(grab_point);
coordinateAdjuster->setPos(position);
}
}
}
void MyDiagonalCurve::getCursorPositionFromCurve(float x) {
// the graph is refreshed only if a new point is created (snaped to a pixel)
clampedX = x;
clampedY = point.getVal01(x);
cursorX = int(clampedX*float(graphW-3)) + graphX+1.5;
cursorY = graphY - int(clampedY*float(graphH-3));
}
// x = cursor position found in the event
void MyDiagonalCurve::getCursorPositionFromCurve(int x) {
// the graph is refreshed only if a new point is created (snaped to a pixel)
cursorX = x-graphX;
clampedX = (float(cursorX)-1.5) / float(graphW-3);
clampedY = point.getVal01(clampedX);
cursorY = graphY - int(float(1.-clampedY)*float(graphH-3));
}
void MyDiagonalCurve::getCursorPosition(Gdk::EventType evType, bool isHint, int evX, int evY, Gdk::ModifierType modifierKey) {
@ -1006,6 +1205,7 @@ std::vector<double> MyDiagonalCurve::getPoints () {
void MyDiagonalCurve::setPoints (const std::vector<double>& p) {
int ix = 0;
stopNumericalAdjustment();
DiagonalCurveType t = (DiagonalCurveType)p[ix++];
curve.type = t;
if (t==DCT_Parametric) {
@ -1028,6 +1228,29 @@ void MyDiagonalCurve::setPoints (const std::vector<double>& p) {
queue_draw ();
}
void MyDiagonalCurve::setPos(double pos, int chanIdx) {
assert (edited_point > -1);
if (chanIdx == 0) {
curve.x.at(edited_point) = pos;
}
else if (chanIdx == 1) {
curve.y.at(edited_point) = pos;
}
curveIsDirty = true;
setDirty(true);
draw(lit_point);
notifyListener ();
}
void MyDiagonalCurve::stopNumericalAdjustment() {
if (edited_point>-1) {
edited_point = grab_point = lit_point = -1;
coordinateAdjuster->stopNumericalAdjustment();
setDirty(true);
draw(lit_point);
}
}
void MyDiagonalCurve::setType (DiagonalCurveType t) {
curve.type = t;
@ -1081,6 +1304,8 @@ void MyDiagonalCurve::updateBackgroundHistogram (LUTu & hist) {
void MyDiagonalCurve::reset(const std::vector<double> &resetCurve, double identityValue) {
stopNumericalAdjustment();
if (!resetCurve.empty()) {
setPoints(resetCurve);
return;

View File

@ -49,7 +49,7 @@ class MyDiagonalCurve : public MyCurve {
protected:
DiagonalCurveDescr curve;
int grab_point; // the point that the user is moving
int grab_point; // the point that the user is moving by mouse
int closest_point; // the point that is the closest from the cursor
int lit_point; // the point that is lit when the cursor is near it
double clampedX; // clamped grabbed point X coordinates in the [0;1] range
@ -63,12 +63,18 @@ class MyDiagonalCurve : public MyCurve {
int activeParam;
unsigned int* bghist; // histogram values
bool bghistvalid;
void draw (int handle);
void interpolate ();
void getCursorPosition(Gdk::EventType evType, bool isHint, int evX, int evY, Gdk::ModifierType modifierKey);
void findClosestPoint();
CursorShape motionNotify(CursorShape type, double minDistanceX, double minDistanceY, int num);
std::vector<double> get_vector (int veclen);
void get_LUT (LUTf &lut);
// Get the cursor position and unclamped position from the curve given an X value ; BEWARE: can be time consuming, use with care
void getCursorPositionFromCurve(float x);
void getCursorPositionFromCurve(int x);
// Get the cursor position and unclamped value depending on cursor's position in the graph
void getCursorPosition(Gdk::EventType evType, bool isHint, int evX, int evY, Gdk::ModifierType modifierKey);
public:
MyDiagonalCurve ();
@ -81,10 +87,13 @@ class MyDiagonalCurve : public MyCurve {
void reset (const std::vector<double> &resetCurve, double identityValue=0.5);
void updateBackgroundHistogram (LUTu & hist);
void pipetteMouseOver (EditDataProvider *provider, int modifierKey);
void pipetteMouseOver (CurveEditor *ce, EditDataProvider *provider, int modifierKey);
void pipetteButton1Pressed(EditDataProvider *provider, int modifierKey);
void pipetteButton1Released(EditDataProvider *provider);
void pipetteDrag(EditDataProvider *provider, int modifierKey);
virtual void setPos(double pos, int chanIdx);
virtual void stopNumericalAdjustment();
};
#endif

View File

@ -37,6 +37,9 @@ MyFlatCurve::MyFlatCurve () {
//bghist = new unsigned int[256];
editedPos.resize(4);
editedPos.at(0) = editedPos.at(1) = editedPos.at(2) = editedPos.at(3) = 0.0;
signal_event().connect( sigc::mem_fun(*this, &MyFlatCurve::handleEvents) );
// By default, we create a curve with 8 control points
@ -55,7 +58,7 @@ std::vector<double> MyFlatCurve::get_vector (int veclen) {
// Get the curve control points
std::vector<double> curveDescr = getPoints ();
rtengine::FlatCurve* rtcurve = new rtengine::FlatCurve (curveDescr, periodic, veclen*1.2 > 5000 ? 5000 : veclen*1.2);
rtengine::FlatCurve rtcurve(curveDescr, periodic, veclen*1.2 > 5000 ? 5000 : veclen*1.2);
// Create the sample values that will be converted
std::vector<double> samples;
@ -64,26 +67,38 @@ std::vector<double> MyFlatCurve::get_vector (int veclen) {
samples.at(i) = (double) i / (veclen - 1.0);
// Converting the values
rtcurve->getVal (samples, convertedValues);
rtcurve.getVal (samples, convertedValues);
// Cleanup and return
delete rtcurve;
return convertedValues;
}
void MyFlatCurve::get_LUT (LUTf &lut) {
int size = lut.getSize();
// Get the curve control points
std::vector<double> curveDescr = getPoints ();
rtengine::FlatCurve rtcurve(curveDescr, periodic, lut.getUpperBound()*1.2 > 5000 ? 5000 : lut.getUpperBound()*1.2);
double t;
double maxVal = double(lut.getUpperBound());
for (int i = 0; i < size; i++) {
t = double(i) / maxVal;
lut[i] = rtcurve.getVal (t);
}
return;
}
void MyFlatCurve::interpolate () {
prevGraphW = graphW;
prevGraphH = graphH;
int nbPoints = graphW-2;
point.resize (nbPoints);
std::vector<double> vector = get_vector (nbPoints);
for (int i = 0; i < nbPoints; ++i) {
float currX = float(i)/float(nbPoints-1);
point.at(i).setCoords(float(graphX)+1.5f+float(graphW-3)*currX, float(graphY)-1.5f-float(graphH-3)*float(vector.at(i)));
}
upoint.clear ();
lpoint.clear ();
point(nbPoints);
get_LUT (point);
upoint.reset ();
lpoint.reset ();
curveIsDirty = false;
}
@ -98,7 +113,8 @@ void MyFlatCurve::draw () {
return;
// re-calculate curve if dimensions changed
if (curveIsDirty || prevGraphW != graphW || prevGraphH != graphH || (int)point.size() != graphW-2)
int currPointSize = point.getUpperBound();
if (curveIsDirty || /*prevGraphW != graphW || prevGraphH != graphH ||*/ (currPointSize==200 && (graphW-3>200)) || (currPointSize>200 && (graphW-2<=200 || graphW-3!=currPointSize)))
interpolate ();
double innerW = double(graphW-2);
@ -228,36 +244,45 @@ void MyFlatCurve::draw () {
cr->set_line_width (coloredLineWidth);
// draw the lit_point's horizontal line
if (i == lit_point) {
bool drawHLine = false;
if (edited_point>-1) {
if (i == edited_point) {
cr->set_line_width (2*coloredLineWidth);
drawHLine = true;
}
}
else if (i == lit_point) {
if ( (area&(FCT_Area_H|FCT_Area_V|FCT_Area_Point)) || editedHandle==FCT_EditedHandle_CPointUD) {
if (editedHandle&(FCT_EditedHandle_CPointUD|FCT_EditedHandle_CPoint|FCT_EditedHandle_CPointY)) {
cr->set_line_width (2*coloredLineWidth);
drawHLine = true;
}
}
}
if (drawHLine) {
int point = edited_point>-1 ? edited_point : lit_point;
colorProvider->colorForValue(curve.x.at(i), curve.y.at(i), CCET_HORIZONTAL_BAR, colorCallerId, this);
cr->set_source_rgb (ccRed, ccGreen, ccBlue);
cr->move_to (double(graphX+1) , double(graphY-1) - innerH*curve.y.at(lit_point));
cr->move_to (double(graphX+1) , double(graphY-1) - innerH*curve.y.at(point));
cr->rel_line_to (innerW, 0.);
cr->stroke ();
}
}
}
}
// endif
cr->set_line_width (1.0);
}
else {
cr->set_source_rgb (0.5, 0.0, 0.0);
if ((lit_point>-1) && ((area&(FCT_Area_H|FCT_Area_V|FCT_Area_Point)) || editedHandle==FCT_EditedHandle_CPointUD) ) {
if (edited_point>-1 || ((lit_point>-1) && ((area&(FCT_Area_H|FCT_Area_V|FCT_Area_Point)) || editedHandle==FCT_EditedHandle_CPointUD)) ) {
// draw the lit_point's vertical line
if (editedHandle&(FCT_EditedHandle_CPointUD|FCT_EditedHandle_CPoint|FCT_EditedHandle_CPointY)) {
if (edited_point>-1 || (editedHandle&(FCT_EditedHandle_CPointUD|FCT_EditedHandle_CPoint|FCT_EditedHandle_CPointY))) {
cr->set_line_width (2.0);
}
cr->move_to (double(graphX)+1 + innerW*curve.x.at(lit_point), double(graphY-1));
int point = edited_point>-1 ? edited_point : lit_point;
cr->move_to (double(graphX)+1 + innerW*curve.x.at(point), double(graphY-1));
cr->rel_line_to (0., -innerH);
cr->stroke ();
cr->set_line_width (1.0);
@ -266,7 +291,7 @@ void MyFlatCurve::draw () {
if (editedHandle&(FCT_EditedHandle_CPointUD|FCT_EditedHandle_CPoint|FCT_EditedHandle_CPointY)) {
cr->set_line_width (2.0);
}
cr->move_to (double(graphX+1) , double(graphY-1) - innerH*curve.y.at(lit_point));
cr->move_to (double(graphX+1) , double(graphY-1) - innerH*curve.y.at(point));
cr->rel_line_to (innerW, 0.);
cr->stroke ();
cr->set_line_width (1.0);
@ -282,7 +307,7 @@ void MyFlatCurve::draw () {
cr->stroke ();
double lineMinLength = 1. / graphW * SQUARE * 0.9;
if (lit_point!=-1 && getHandles(lit_point) && curve.x.at(lit_point)!=-1.) {
if (tanHandlesDisplayed && lit_point!=-1 && getHandles(lit_point) && curve.x.at(lit_point)!=-1.) {
double x = double(graphX+1) + innerW*curve.x.at(lit_point);
double y = double(graphY) - innerH*curve.y.at(lit_point);
double x2;
@ -353,26 +378,30 @@ void MyFlatCurve::draw () {
// draw curve
c = style->get_fg (state);
cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p());
cr->move_to (point.at(0).x, point.at(0).y);
for (int i=1; i<(int)point.size(); i++)
cr->line_to (double(point.at(i).x), double(point.at(i).y));
float graphH_ = float(graphH-3);
float graphX_ = float(graphX)+1.5;
float graphY_ = float(graphY)-1.5;
cr->move_to (graphX_, getVal(point, 0)*-graphH_+graphY_);
for (int i=1; i<graphW-2; ++i)
cr->line_to (float(i)+graphX_, getVal(point, i)*-graphH_+graphY_);
cr->stroke ();
// draw bullets
//if (curve.type!=FCT_Parametric)
for (int i = 0; i < (int)curve.x.size(); ++i) {
if (curve.x.at(i) != -1.) {
if (i == lit_point) {
if (colorProvider) {
if (i == edited_point)
cr->set_source_rgb (1.0, 0.0, 0.0);
else if (i == lit_point) {
if (colorProvider && edited_point==-1) {
colorProvider->colorForValue(curve.x.at(i), curve.y.at(i), CCET_POINT, colorCallerId, this);
cr->set_source_rgb (ccRed, ccGreen, ccBlue);
}
else
cr->set_source_rgb (1.0, 0.0, 0.0);
}
else if (i == snapToElmt) {
else if (i == snapToElmt || i == edited_point)
cr->set_source_rgb (1.0, 0.0, 0.0);
}
else if (curve.y.at(i) == 0.5)
cr->set_source_rgb (0.0, 0.5, 0.0);
else
@ -383,6 +412,15 @@ void MyFlatCurve::draw () {
cr->arc (x, y, (double)RADIUS, 0, 2*M_PI);
cr->fill ();
if (i == edited_point) {
cr->set_source_rgb (1.0, 0.0, 0.0);
cr->set_line_width(2.);
cr->arc (x, y, RADIUS+3.5, 0, 2*M_PI);
cr->stroke();
cr->set_line_width(1.);
}
}
}
// endif
@ -499,6 +537,7 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) {
// setDrawRectangle will allocate the backbuffer Surface
if (setDrawRectangle(win, 0, 0, get_allocation().get_width(), get_allocation().get_height())) {
setDirty(true);
if (prevGraphW > 200 || graphW > 200)
curveIsDirty = true;
}
draw ();
@ -510,7 +549,7 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) {
}
case Gdk::BUTTON_PRESS:
//if (curve.type!=FCT_Parametric) {
if (edited_point==-1) { //curve.type!=FCT_Parametric) {
if (event->button.button == 1) {
buttonPressed = true;
add_modal_grab ();
@ -544,6 +583,10 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) {
curve.rightTangent.insert (itrt, 0);
num++;
if (mod_type & GDK_CONTROL_MASK) {
clampedY = point.getVal01(clampedX);
}
// the graph is refreshed only if a new point is created
curve.x.at(closest_point) = clampedX;
curve.y.at(closest_point) = clampedY;
@ -593,14 +636,85 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) {
default:
break;
}
}
else if (event->button.button == 3) {
/* get the pointer position */
getCursorPosition(Gdk::EventType(event->type), event->motion.is_hint!=0, int(event->button.x), int(event->button.y), Gdk::ModifierType(event->button.state));
getMouseOverArea();
if (lit_point>-1 && lit_point!=edited_point) {
if (editedHandle == FCT_EditedHandle_None) {
if (area == FCT_Area_Point || area == FCT_Area_V) {
// the cursor is close to an existing point
if (!coordinateAdjuster->is_visible())
coordinateAdjuster->showMe(this);
new_type = CSArrow;
tanHandlesDisplayed = false;
edited_point = lit_point;
setDirty(true);
draw ();
std::vector<CoordinateAdjuster::Boundaries> newBoundaries(4);
unsigned int size = curve.x.size();
if (edited_point == 0) { newBoundaries.at(0).minVal = 0.; newBoundaries.at(0).maxVal = curve.x.at(1); }
else if (edited_point == size-1) { newBoundaries.at(0).minVal = curve.x.at(edited_point-1); newBoundaries.at(0).maxVal = 1.; }
else if (curve.x.size() > 2) { newBoundaries.at(0).minVal = curve.x.at(edited_point-1); newBoundaries.at(0).maxVal = curve.x.at(edited_point+1); }
newBoundaries.at(1).minVal = 0.; newBoundaries.at(1).maxVal = 1.;
newBoundaries.at(2).minVal = 0.; newBoundaries.at(2).maxVal = 1.;
newBoundaries.at(3).minVal = 0.; newBoundaries.at(3).maxVal = 1.;
retval = true;
editedPos.at(0) = curve.x.at(edited_point);
editedPos.at(1) = curve.y.at(edited_point);
editedPos.at(2) = curve.leftTangent.at(edited_point);
editedPos.at(3) = curve.rightTangent.at(edited_point);
coordinateAdjuster->setPos(editedPos);
coordinateAdjuster->startNumericalAdjustment(newBoundaries);
}
}
}
retval = true;
}
if (buttonPressed) retval = true;
//}
}
else { // if (edited_point > -1)
if (event->button.button == 3) {
// do we edit another point?
/* get the pointer position */
getCursorPosition(Gdk::EventType(event->type), event->motion.is_hint!=0, int(event->button.x), int(event->button.y), Gdk::ModifierType(event->button.state));
getMouseOverArea();
if (area == FCT_Area_Point || area == FCT_Area_V) {
// the cursor is close to an existing point
if (lit_point != edited_point) {
edited_point = lit_point;
setDirty(true);
draw ();
std::vector<CoordinateAdjuster::Boundaries> newBoundaries(4);
unsigned int size = curve.x.size();
if (edited_point == 0) { newBoundaries.at(0).minVal = 0.; newBoundaries.at(0).maxVal = curve.x.at(1); }
else if (edited_point == size-1) { newBoundaries.at(0).minVal = curve.x.at(edited_point-1); newBoundaries.at(0).maxVal = 1.; }
else if (curve.x.size() > 2) { newBoundaries.at(0).minVal = curve.x.at(edited_point-1); newBoundaries.at(0).maxVal = curve.x.at(edited_point+1); }
newBoundaries.at(1).minVal = 0.; newBoundaries.at(1).maxVal = 1.;
newBoundaries.at(2).minVal = 0.; newBoundaries.at(2).maxVal = 1.;
newBoundaries.at(3).minVal = 0.; newBoundaries.at(3).maxVal = 1.;
editedPos.at(0) = curve.x.at(edited_point);
editedPos.at(1) = curve.y.at(edited_point);
editedPos.at(2) = curve.leftTangent.at(edited_point);
editedPos.at(3) = curve.rightTangent.at(edited_point);
coordinateAdjuster->switchAdjustedPoint(editedPos, newBoundaries);
retval = true;
}
}
else if (area == FCT_Area_Insertion) {
// the cursor is inside the graph but away from existing points
new_type = CSPlus;
curveIsDirty = true;
stopNumericalAdjustment();
}
}
}
break;
case Gdk::BUTTON_RELEASE:
//if (curve.type!=FCT_Parametric) {
if (edited_point==-1) { //curve.type!=FCT_Parametric) {
if (buttonPressed && event->button.button == 1) {
int src, dst;
buttonPressed = false;
@ -684,7 +798,7 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) {
retval = true;
//notifyListener ();
}
//}
}
break;
case Gdk::MOTION_NOTIFY:
@ -722,7 +836,7 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) {
case (FCT_EditedHandle_None): {
if ((lit_point != -1 && previous_lit_point != lit_point) && (area&(FCT_Area_V|FCT_Area_Point))) {
if ((lit_point != -1 && previous_lit_point != lit_point) && (area&(FCT_Area_V|FCT_Area_Point)) && edited_point==-1) {
bool sameSide = false;
@ -771,11 +885,12 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) {
}
}
}
else if (lit_point == -1) {
else if (lit_point == -1 || edited_point>-1) {
tanHandlesDisplayed = false;
}
if (edited_point==-1) {
switch (area) {
case (FCT_Area_Insertion):
@ -801,24 +916,61 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) {
new_type = CSArrow;
break;
}
}
if ((lit_point != previous_lit_point) || (prevArea != area)) {
setDirty(true);
draw ();
}
if (coordinateAdjuster->is_visible() && edited_point==-1) {
if (lit_point > -1) {
if (lit_point != previous_lit_point) {
editedPos.at(0) = curve.x.at(lit_point);
editedPos.at(1) = curve.y.at(lit_point);
editedPos.at(2) = curve.leftTangent.at(lit_point);
editedPos.at(3) = curve.rightTangent.at(lit_point);
}
coordinateAdjuster->setPos(editedPos);
}
else if (area == FCT_Area_Insertion) {
editedPos.at(0) = clampedX;
editedPos.at(1) = clampedY;
editedPos.at(2) = 0.;
editedPos.at(3) = 0.;
coordinateAdjuster->setPos(editedPos);
}
else {
editedPos.at(0) = editedPos.at(1) = editedPos.at(2) = editedPos.at(3) = 0;
coordinateAdjuster->setPos(editedPos);
}
}
break;
}
case (FCT_EditedHandle_CPoint):
movePoint(true, true);
if (coordinateAdjuster->is_visible()) {
editedPos.at(0) = curve.x.at(lit_point);
editedPos.at(1) = curve.y.at(lit_point);
coordinateAdjuster->setPos(editedPos);
}
break;
case (FCT_EditedHandle_CPointX):
movePoint(true, false);
if (coordinateAdjuster->is_visible()) {
editedPos.at(0) = curve.x.at(lit_point);
coordinateAdjuster->setPos(editedPos);
}
break;
case (FCT_EditedHandle_CPointY):
movePoint(false, true);
if (coordinateAdjuster->is_visible()) {
editedPos.at(1) = curve.y.at(lit_point);
coordinateAdjuster->setPos(editedPos);
}
break;
case (FCT_EditedHandle_LeftTan): {
@ -843,6 +995,10 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) {
setDirty(true);
draw ();
notifyListener ();
if (coordinateAdjuster->is_visible()) {
editedPos.at(2) = curve.leftTangent.at(lit_point);
coordinateAdjuster->setPos(editedPos);
}
}
break;
}
@ -869,6 +1025,8 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) {
setDirty(true);
draw ();
notifyListener ();
editedPos.at(3) = curve.rightTangent.at(lit_point);
coordinateAdjuster->setPos(editedPos);
}
break;
}
@ -879,6 +1037,23 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) {
default:
break;
}
if (edited_point==-1) {
if (lit_point == -1) {
editedPos.at(0) = editedPos.at(1) = editedPos.at(2) = editedPos.at(3) = 0;
}
else if (editedPos.at(0) != curve.x.at(lit_point)
|| editedPos.at(1) != curve.y.at(lit_point)
|| editedPos.at(2) != curve.leftTangent.at(lit_point)
|| editedPos.at(3) != curve.rightTangent.at(lit_point))
{
editedPos.at(0) = curve.x.at(lit_point);
editedPos.at(1) = curve.y.at(lit_point);
editedPos.at(2) = curve.leftTangent.at(lit_point);
editedPos.at(3) = curve.rightTangent.at(lit_point);
coordinateAdjuster->setPos(editedPos);
}
}
}
retval = true;
@ -908,7 +1083,7 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) {
return retval;
}
void MyFlatCurve::pipetteMouseOver (EditDataProvider *provider, int modifierKey) {
void MyFlatCurve::pipetteMouseOver (CurveEditor *ce, EditDataProvider *provider, int modifierKey) {
if (!provider) {
// occurs when leaving the preview area -> cleanup the curve editor
pipetteR = pipetteG = pipetteB = -1.f;
@ -921,7 +1096,7 @@ void MyFlatCurve::pipetteMouseOver (EditDataProvider *provider, int modifierKey)
pipetteB = provider->pipetteVal[2];
pipetteVal = 0.f;
if (listener)
pipetteVal = listener->blendPipetteValues(pipetteR, pipetteG, pipetteB);
pipetteVal = listener->blendPipetteValues(ce, pipetteR, pipetteG, pipetteB);
else {
int n = 0;
if (pipetteR != -1.f) {
@ -958,6 +1133,7 @@ void MyFlatCurve::pipetteMouseOver (EditDataProvider *provider, int modifierKey)
// get the pointer position
int px = graphX + int(float(graphW)*pipetteVal); // WARNING: converting pipetteVal from float to int, precision loss here!
getCursorPosition(Gdk::EventType(Gdk::BUTTON_PRESS), false, px, graphY, Gdk::ModifierType(modifierKey));
if (edited_point==-1)
getMouseOverArea();
if (area==FCT_Area_Point)
@ -966,13 +1142,26 @@ void MyFlatCurve::pipetteMouseOver (EditDataProvider *provider, int modifierKey)
snapToMinDistY = snapToMinDistX = 10.;
snapToValY = snapToValX = 0.;
if (edited_point==-1) {
if (editedHandle==FCT_EditedHandle_None && lit_point != previous_lit_point) {
setDirty(true);
draw ();
}
}
else
draw();
if (edited_point==-1) {
editedPos.at(0) = pipetteVal;
editedPos.at(1) = point.getVal01(pipetteVal);
coordinateAdjuster->setPos(editedPos);
}
}
void MyFlatCurve::pipetteButton1Pressed(EditDataProvider *provider, int modifierKey) {
if (edited_point>-1)
return;
buttonPressed = true;
// get the pointer position
@ -1040,6 +1229,9 @@ void MyFlatCurve::pipetteButton1Pressed(EditDataProvider *provider, int modifier
}
void MyFlatCurve::pipetteButton1Released(EditDataProvider *provider) {
if (edited_point>-1)
return;
buttonPressed = false;
remove_modal_grab ();
@ -1057,6 +1249,8 @@ void MyFlatCurve::pipetteButton1Released(EditDataProvider *provider) {
}
void MyFlatCurve::pipetteDrag(EditDataProvider *provider, int modifierKey) {
if (edited_point>-1)
return;
snapToMinDistY = snapToMinDistX = 10.;
snapToValY = snapToValX = 0.;
@ -1423,6 +1617,7 @@ std::vector<double> MyFlatCurve::getPoints () {
void MyFlatCurve::setPoints (const std::vector<double>& p) {
int ix = 0;
stopNumericalAdjustment();
FlatCurveType t = (FlatCurveType)p[ix++];
curve.type = t;
if (t==FCT_MinMaxCPoints) {
@ -1442,6 +1637,38 @@ void MyFlatCurve::setPoints (const std::vector<double>& p) {
queue_draw ();
}
void MyFlatCurve::setPos(double pos, int chanIdx) {
assert (edited_point > -1);
switch (chanIdx) {
case (0):
curve.x.at(edited_point) = pos;
break;
case (1):
curve.y.at(edited_point) = pos;
break;
case (2):
curve.leftTangent.at(edited_point) = pos;
break;
case (3):
curve.rightTangent.at(edited_point) = pos;
break;
}
curveIsDirty = true;
setDirty(true);
draw();
notifyListener ();
}
void MyFlatCurve::stopNumericalAdjustment() {
if (edited_point>-1) {
edited_point = lit_point = -1;
area = FCT_Area_None;
coordinateAdjuster->stopNumericalAdjustment();
setDirty(true);
draw();
}
}
void MyFlatCurve::setType (FlatCurveType t) {
curve.type = t;
@ -1451,6 +1678,8 @@ void MyFlatCurve::setType (FlatCurveType t) {
void MyFlatCurve::reset(const std::vector<double> &resetCurve, double identityValue) {
calcDimensions();
stopNumericalAdjustment();
// If a resetCurve exist (non empty)
if (!resetCurve.empty()) {
setPoints(resetCurve);

View File

@ -110,6 +110,7 @@ class MyFlatCurve : public MyCurve {
bool getHandles(int n);
CursorShape motionNotify(CursorShape type, double minDistanceX, double minDistanceY, int num);
std::vector<double> get_vector (int veclen);
void get_LUT (LUTf &lut);
public:
MyFlatCurve ();
@ -122,10 +123,13 @@ class MyFlatCurve : public MyCurve {
void reset (const std::vector<double> &resetCurve, double identityValue=0.5);
//void updateBackgroundHistogram (unsigned int* hist);
void pipetteMouseOver (EditDataProvider *provider, int modifierKey);
void pipetteMouseOver (CurveEditor *ce, EditDataProvider *provider, int modifierKey);
void pipetteButton1Pressed(EditDataProvider *provider, int modifierKey);
void pipetteButton1Released(EditDataProvider *provider);
void pipetteDrag(EditDataProvider *provider, int modifierKey);
void setPos(double pos, int chanIdx);
virtual void stopNumericalAdjustment();
};
#endif

View File

@ -2,11 +2,13 @@
#define _PPVERSION_
// This number has to be incremented whenever the PP3 file format is modified or the behaviour of a tool changes
#define PPVERSION 322
#define PPVERSION 323
#define PPVERSION_AEXP 301 //value of PPVERSION when auto exposure algorithm was modified
/*
Log of version changes
323 2015-10-05
[Exposure] Added 'Luminance' tone curve mode
322 2015-01-31
[Wavelet] new tool using wavelet levels
321 2014-08-17

View File

@ -123,6 +123,7 @@ ToneCurve::ToneCurve () : FoldableToolPanel(this, "tonecurve", M("TP_EXPOSURE_LA
toneCurveMode->append_text (M("TP_EXPOSURE_TCMODE_WEIGHTEDSTD"));
toneCurveMode->append_text (M("TP_EXPOSURE_TCMODE_FILMLIKE"));
toneCurveMode->append_text (M("TP_EXPOSURE_TCMODE_SATANDVALBLENDING"));
toneCurveMode->append_text (M("TP_EXPOSURE_TCMODE_LUMINANCE"));
toneCurveMode->set_active (0);
toneCurveMode->set_tooltip_text(M("TP_EXPOSURE_TCMODE_LABEL1"));
@ -148,6 +149,7 @@ ToneCurve::ToneCurve () : FoldableToolPanel(this, "tonecurve", M("TP_EXPOSURE_LA
toneCurveMode2->append_text (M("TP_EXPOSURE_TCMODE_WEIGHTEDSTD"));
toneCurveMode2->append_text (M("TP_EXPOSURE_TCMODE_FILMLIKE"));
toneCurveMode2->append_text (M("TP_EXPOSURE_TCMODE_SATANDVALBLENDING"));
toneCurveMode2->append_text (M("TP_EXPOSURE_TCMODE_LUMINANCE"));
toneCurveMode2->set_active (0);
toneCurveMode2->set_tooltip_text(M("TP_EXPOSURE_TCMODE_LABEL2"));
@ -224,10 +226,10 @@ void ToneCurve::read (const ProcParams* pp, const ParamsEdited* pedited) {
shape->setUnChanged (!pedited->toneCurve.curve);
shape2->setUnChanged (!pedited->toneCurve.curve2);
if (!pedited->toneCurve.curveMode) {
toneCurveMode->set_active(4);
toneCurveMode->set_active(5);
}
if (!pedited->toneCurve.curveMode2) {
toneCurveMode2->set_active(4);
toneCurveMode2->set_active(5);
}
}
if (pedited)
@ -292,12 +294,14 @@ void ToneCurve::write (ProcParams* pp, ParamsEdited* pedited) {
else if (tcMode == 1) pp->toneCurve.curveMode = ToneCurveParams::TC_MODE_WEIGHTEDSTD;
else if (tcMode == 2) pp->toneCurve.curveMode = ToneCurveParams::TC_MODE_FILMLIKE;
else if (tcMode == 3) pp->toneCurve.curveMode = ToneCurveParams::TC_MODE_SATANDVALBLENDING;
else if (tcMode == 4) pp->toneCurve.curveMode = ToneCurveParams::TC_MODE_LUMINANCE;
tcMode = toneCurveMode2->get_active_row_number();
if (tcMode == 0) pp->toneCurve.curveMode2 = ToneCurveParams::TC_MODE_STD;
else if (tcMode == 1) pp->toneCurve.curveMode2 = ToneCurveParams::TC_MODE_WEIGHTEDSTD;
else if (tcMode == 2) pp->toneCurve.curveMode2 = ToneCurveParams::TC_MODE_FILMLIKE;
else if (tcMode == 3) pp->toneCurve.curveMode2 = ToneCurveParams::TC_MODE_SATANDVALBLENDING;
else if (tcMode == 4) pp->toneCurve.curveMode2 = ToneCurveParams::TC_MODE_LUMINANCE;
if (pedited) {
pedited->toneCurve.expcomp = expcomp->getEditedState ();
@ -312,8 +316,8 @@ void ToneCurve::write (ProcParams* pp, ParamsEdited* pedited) {
pedited->toneCurve.clip = clipDirty;
pedited->toneCurve.curve = !shape->isUnChanged ();
pedited->toneCurve.curve2 = !shape2->isUnChanged ();
pedited->toneCurve.curveMode = toneCurveMode->get_active_row_number() != 4;
pedited->toneCurve.curveMode2 = toneCurveMode2->get_active_row_number() != 4;
pedited->toneCurve.curveMode = toneCurveMode->get_active_row_number() != 5;
pedited->toneCurve.curveMode2 = toneCurveMode2->get_active_row_number() != 5;
}
if (pedited) {
pedited->toneCurve.method = method->get_active_row_number()!=4;
@ -446,6 +450,19 @@ bool ToneCurve::curveMode2Changed_ () {
return false;
}
float ToneCurve::blendPipetteValues(CurveEditor *ce, float chan1, float chan2, float chan3) {
// assuming that all the channels are used...
if (ce == shape) {
if (toneCurveMode->get_active_row_number() == 4)
return chan1*0.2126729f + chan2*0.7151521f + chan3*0.0721750f;
}
else if (ce == shape2) {
if (toneCurveMode2->get_active_row_number() == 4)
return chan1*0.2126729f + chan2*0.7151521f + chan3*0.0721750f;
}
return CurveListener::blendPipetteValues(ce, chan1, chan2, chan3);
}
void ToneCurve::adjusterChanged (Adjuster* a, double newval) {
// Switch off auto exposure if user changes sliders manually

View File

@ -84,6 +84,7 @@ class ToneCurve : public ToolParamBlock, public AdjusterListener, public Foldabl
void autoOpenCurve ();
void setEditProvider (EditDataProvider *provider);
virtual float blendPipetteValues (CurveEditor *ce, float chan1, float chan2, float chan3);
void adjusterChanged (Adjuster* a, double newval);
void neutral_pressed ();