Support for artistic tone curves in DCPs
see issue 1527
This commit is contained in:
@@ -926,8 +926,8 @@ TP_HSVEQUALIZER_LABEL;HSV-Equalizer
|
||||
TP_HSVEQUALIZER_NEUTRAL;Neutral
|
||||
TP_HSVEQUALIZER_SAT;S
|
||||
TP_HSVEQUALIZER_VAL;V
|
||||
TP_ICM_BLENDCMSMATRIX;Lichter aus Matrix einmischen
|
||||
TP_ICM_BLENDCMSMATRIX_TOOLTIP;Stellt bei Verwendung von LUT-basierten\nICC-Profilen die Lichter wieder her
|
||||
TP_ICM_BLENDCMSMATRIX;ICC Lichter aus Matrix einmischen
|
||||
TP_ICM_BLENDCMSMATRIX_TOOLTIP;Stellt bei Verwendung von LUT-basierten\nICC-Profilen die Lichter wieder her.
|
||||
TP_ICM_FILEDLGFILTERANY;Alle Dateien
|
||||
TP_ICM_FILEDLGFILTERICM;Profildateien
|
||||
TP_ICM_INPUTCAMERAICC;Kameraspezifisches Profil
|
||||
@@ -952,6 +952,8 @@ TP_ICM_PREFERREDPROFILE_2;Glühlampe
|
||||
TP_ICM_PREFERREDPROFILE_3;Leuchtstofflampe
|
||||
TP_ICM_PREFERREDPROFILE_4;Blitz
|
||||
TP_ICM_SAVEREFERENCE;Referenzbild für Profil speichern
|
||||
TP_ICM_TONECURVE;DCP Tonwertkurve verwenden
|
||||
TP_ICM_TONECURVE_TOOLTIP;Verwendet die Tonwertkurve, die in DCP profilen eingebetten sein kann.
|
||||
TP_ICM_WORKINGPROFILE;Arbeitsfarbraum
|
||||
TP_IMPULSEDENOISE_LABEL;Impulsrauschminderung
|
||||
TP_IMPULSEDENOISE_THRESH;Schwellenwert
|
||||
|
||||
@@ -950,7 +950,7 @@ TP_HSVEQUALIZER_LABEL;HSV Equalizer
|
||||
TP_HSVEQUALIZER_NEUTRAL;Neutral
|
||||
TP_HSVEQUALIZER_SAT;S
|
||||
TP_HSVEQUALIZER_VAL;V
|
||||
TP_ICM_BLENDCMSMATRIX;Blend highlights with matrix
|
||||
TP_ICM_BLENDCMSMATRIX;Blend ICC highlights with matrix
|
||||
TP_ICM_BLENDCMSMATRIX_TOOLTIP;Enable to recover blown highlights when using LUT based ICC profiles
|
||||
TP_ICM_FILEDLGFILTERANY;Any files
|
||||
TP_ICM_FILEDLGFILTERICM;Color profiles
|
||||
@@ -976,6 +976,8 @@ TP_ICM_PREFERREDPROFILE_2;Tungsten
|
||||
TP_ICM_PREFERREDPROFILE_3;Fluorescent
|
||||
TP_ICM_PREFERREDPROFILE_4;Flash
|
||||
TP_ICM_SAVEREFERENCE;Save reference image for profiling
|
||||
TP_ICM_TONECURVE;Use DCP tone curve
|
||||
TP_ICM_TONECURVE_TOOLTIP;Enable to use tone curves that may be contained in DCP profiles.
|
||||
TP_ICM_WORKINGPROFILE;Working Profile
|
||||
TP_IMPULSEDENOISE_LABEL;Impulse Noise Reduction
|
||||
TP_IMPULSEDENOISE_THRESH;Impulse NR Threshold
|
||||
|
||||
@@ -135,7 +135,7 @@ public:
|
||||
return *this;
|
||||
}
|
||||
// use with integer indices
|
||||
T& operator[](int index) {
|
||||
T& operator[](int index) const {
|
||||
if (((unsigned int)index)<size) return data[index];
|
||||
else
|
||||
{
|
||||
@@ -147,7 +147,7 @@ public:
|
||||
|
||||
}
|
||||
// use with float indices
|
||||
T operator[](float index) {
|
||||
T operator[](float index) const {
|
||||
int idx = (int)index; // don't use floor! The difference in negative space is no problems here
|
||||
if (((unsigned int)idx) > maxs) {
|
||||
if (idx<0)
|
||||
@@ -185,7 +185,7 @@ public:
|
||||
#endif
|
||||
|
||||
|
||||
operator bool (void)
|
||||
operator bool (void) const
|
||||
{
|
||||
return size>0;
|
||||
}
|
||||
|
||||
467
rtengine/dcp.cc
467
rtengine/dcp.cc
@@ -25,17 +25,19 @@
|
||||
#include "rawimagesource.h"
|
||||
#include "improcfun.h"
|
||||
#include "rt_math.h"
|
||||
#include "curves.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace rtengine;
|
||||
using namespace rtexif;
|
||||
|
||||
DCPProfile::DCPProfile(Glib::ustring fname) {
|
||||
DCPProfile::DCPProfile(Glib::ustring fname, bool isRTProfile) {
|
||||
const int TIFFFloatSize=4;
|
||||
const int TagColorMatrix1=50721, TagColorMatrix2=50722, TagProfileHueSatMapDims=50937;
|
||||
const int TagProfileHueSatMapData1=50938, TagProfileHueSatMapData2=50939;
|
||||
const int TagCalibrationIlluminant1=50778, TagCalibrationIlluminant2=50779;
|
||||
const int TagProfileLookTableData=50982, TagProfileLookTableDims=50981; // ProfileLookup is the low quality variant
|
||||
const int TagProfileToneCurve=50940;
|
||||
|
||||
aDeltas1=aDeltas2=NULL; iHueDivisions=iSatDivisions=iValDivisions=iArrayCount=0;
|
||||
|
||||
@@ -115,6 +117,24 @@ DCPProfile::DCPProfile(Glib::ustring fname) {
|
||||
}
|
||||
}
|
||||
|
||||
// Read tone curve points, if any, but disable to RTs own profiles
|
||||
// the DCP tone curve is subjective and of low quality in comparison to RTs tone curves
|
||||
tag = tagDir->getTag(TagProfileToneCurve);
|
||||
if (tag!=NULL && !isRTProfile) {
|
||||
std::vector<double> cPoints;
|
||||
cPoints.push_back(double(DCT_Spline)); // The first value is the curve type
|
||||
|
||||
// push back each X/Y coordinates in a loop
|
||||
for (int i=0;i<tag->getCount();i++) cPoints.push_back( tag->toDouble(i*TIFFFloatSize) );
|
||||
|
||||
// Create the curve
|
||||
DiagonalCurve toneCurve(cPoints, CURVES_MIN_POLY_POINTS);
|
||||
|
||||
// Fill a LUT with X/Y, ranged 0xffff
|
||||
lutToneCurve(0xffff);
|
||||
for (int i=0;i<0xffff;i++) lutToneCurve[i] = toneCurve.getVal(i/(double)0xffff) * 0xffff;
|
||||
}
|
||||
|
||||
if (pFile!=NULL) fclose(pFile);
|
||||
delete tagDir;
|
||||
}
|
||||
@@ -123,42 +143,42 @@ DCPProfile::~DCPProfile() {
|
||||
delete[] aDeltas1; delete[] aDeltas2;
|
||||
}
|
||||
|
||||
// Convert DNG color matrix to xyz_cam compatible matrix
|
||||
// Convert DNG color matrix to xyz_cam compatible matrix
|
||||
void DCPProfile::ConvertDNGMatrix2XYZCAM(const double (*mColorMatrix)[3], double (*mXYZCAM)[3]) {
|
||||
int i,j,k;
|
||||
int i,j,k;
|
||||
|
||||
double cam_xyz[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
|
||||
for (i=0; i<3; i++)
|
||||
for (j=0; j<3; j++)
|
||||
for (k=0; k<3; k++)
|
||||
cam_xyz[i][j] += mColorMatrix[j][k] * (i==k);
|
||||
double cam_xyz[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
|
||||
for (i=0; i<3; i++)
|
||||
for (j=0; j<3; j++)
|
||||
for (k=0; k<3; k++)
|
||||
cam_xyz[i][j] += mColorMatrix[j][k] * (i==k);
|
||||
|
||||
|
||||
// Multiply out XYZ colorspace
|
||||
double cam_rgb[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
|
||||
for (i=0; i < 3; i++)
|
||||
for (j=0; j < 3; j++)
|
||||
for (k=0; k < 3; k++)
|
||||
cam_rgb[i][j] += cam_xyz[i][k] * xyz_sRGB[k][j];
|
||||
// Multiply out XYZ colorspace
|
||||
double cam_rgb[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
|
||||
for (i=0; i < 3; i++)
|
||||
for (j=0; j < 3; j++)
|
||||
for (k=0; k < 3; k++)
|
||||
cam_rgb[i][j] += cam_xyz[i][k] * xyz_sRGB[k][j];
|
||||
|
||||
// Normalize cam_rgb so that: cam_rgb * (1,1,1) is (1,1,1,1)
|
||||
double num;
|
||||
for (i=0; i<3; i++) {
|
||||
for (num=j=0; j<3; j++) num += cam_rgb[i][j];
|
||||
for (j=0; j<3; j++) cam_rgb[i][j] /= num;
|
||||
}
|
||||
// Normalize cam_rgb so that: cam_rgb * (1,1,1) is (1,1,1,1)
|
||||
double num;
|
||||
for (i=0; i<3; i++) {
|
||||
for (num=j=0; j<3; j++) num += cam_rgb[i][j];
|
||||
for (j=0; j<3; j++) cam_rgb[i][j] /= num;
|
||||
}
|
||||
|
||||
double rgb_cam[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
|
||||
RawImageSource::inverse33 (cam_rgb, rgb_cam);
|
||||
double rgb_cam[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
|
||||
RawImageSource::inverse33 (cam_rgb, rgb_cam);
|
||||
|
||||
for (i=0; i<3; i++)
|
||||
for (j=0; j<3; j++) mXYZCAM[i][j]=0;
|
||||
|
||||
for (i=0; i<3; i++)
|
||||
for (j=0; j<3; j++)
|
||||
for (k=0; k<3; k++)
|
||||
mXYZCAM[i][j] += xyz_sRGB[i][k] * rgb_cam[k][j];
|
||||
}
|
||||
for (i=0; i<3; i++)
|
||||
for (j=0; j<3; j++)
|
||||
for (k=0; k<3; k++)
|
||||
mXYZCAM[i][j] += xyz_sRGB[i][k] * rgb_cam[k][j];
|
||||
}
|
||||
|
||||
|
||||
const DCPProfile::HSBModify* DCPProfile::GetBestProfile(DCPLightType preferredProfile, double (*mXYZCAM)[3]) const {
|
||||
@@ -171,21 +191,21 @@ const DCPProfile::HSBModify* DCPProfile::GetBestProfile(DCPLightType preferredPr
|
||||
if (t2==Daylight) use2=true;
|
||||
|
||||
switch (preferredProfile) {
|
||||
case Tungsten:
|
||||
if (t1==Tungsten) use2=false; else if (t2==Tungsten) use2=true;
|
||||
break;
|
||||
case Tungsten:
|
||||
if (t1==Tungsten) use2=false; else if (t2==Tungsten) use2=true;
|
||||
break;
|
||||
|
||||
case Fluorescent:
|
||||
if (t1==Fluorescent) use2=false; else if (t2==Fluorescent) use2=true;
|
||||
break;
|
||||
case Fluorescent:
|
||||
if (t1==Fluorescent) use2=false; else if (t2==Fluorescent) use2=true;
|
||||
break;
|
||||
|
||||
case Flash:
|
||||
if (t1==Flash) use2=false; else if (t2==Flash) use2=true;
|
||||
break;
|
||||
case Flash:
|
||||
if (t1==Flash) use2=false; else if (t2==Flash) use2=true;
|
||||
break;
|
||||
|
||||
default: break; // e.g. Daylight
|
||||
default: break; // e.g. Daylight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// printf("DCP using LightSource %i: %i for requested %i\n", use2?2:1, use2?iLightSource2:iLightSource1, (int)preferredProfile);
|
||||
|
||||
@@ -193,7 +213,7 @@ const DCPProfile::HSBModify* DCPProfile::GetBestProfile(DCPLightType preferredPr
|
||||
for (int col=0;col<3;col++) {
|
||||
mXYZCAM[col][row]= (use2 ? mXYZCAM2[col][row] : mXYZCAM1[col][row]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return use2?aDeltas2:aDeltas1;
|
||||
}
|
||||
@@ -205,14 +225,16 @@ DCPLightType DCPProfile::GetLightType(short iLightSource) const {
|
||||
return Daylight;
|
||||
}
|
||||
|
||||
void DCPProfile::Apply(Imagefloat *pImg, DCPLightType preferredProfile, Glib::ustring workingSpace, float rawWhiteFac) const {
|
||||
void DCPProfile::Apply(Imagefloat *pImg, DCPLightType preferredProfile, Glib::ustring workingSpace, float rawWhiteFac, bool useToneCurve) const {
|
||||
TMatrix mWork = iccStore->workingSpaceInverseMatrix (workingSpace);
|
||||
|
||||
double mXYZCAM[3][3];
|
||||
const HSBModify* tableBase=GetBestProfile(preferredProfile,mXYZCAM);
|
||||
|
||||
if (iArrayCount==0) {
|
||||
//===== No LUT- Calculate matrix for direct conversion raw>working space
|
||||
bool hasLUT=(iArrayCount>0); useToneCurve&=lutToneCurve;
|
||||
|
||||
if (!hasLUT && !useToneCurve) {
|
||||
//===== The fast path: no LUT and not tone curve- Calculate matrix for direct conversion raw>working space
|
||||
double mat[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
|
||||
for (int i=0; i<3; i++)
|
||||
for (int j=0; j<3; j++)
|
||||
@@ -270,160 +292,163 @@ void DCPProfile::Apply(Imagefloat *pImg, DCPLightType preferredProfile, Glib::us
|
||||
newb = m2ProPhoto[2][0]*pImg->r[y][x] + m2ProPhoto[2][1]*pImg->g[y][x] + m2ProPhoto[2][2]*pImg->b[y][x];
|
||||
|
||||
// if point is in negative area, just the matrix, but not the LUT
|
||||
if (newr>=0 && newg>=0 && newb>=0) {
|
||||
Color::rgb2hsv(newr, newg, newb, h , s, v);
|
||||
h*=6.f; // RT calculates in [0,1]
|
||||
if (hasLUT && newr>=0 && newg>=0 && newb>=0) {
|
||||
Color::rgb2hsv(newr, newg, newb, h , s, v);
|
||||
h*=6.f; // RT calculates in [0,1]
|
||||
|
||||
if (useRawWhite) {
|
||||
// Retro-calculate what the point was like before RAW white came in
|
||||
Color::rgb2hsv(newr/rawWhiteFac, newg/rawWhiteFac, newb/rawWhiteFac, hs, ss, vs);
|
||||
hs*=6.f; // RT calculates in [0,1]
|
||||
} else {
|
||||
hs=h; ss=s; vs=v;
|
||||
}
|
||||
|
||||
// Apply the HueSatMap. Ported from Adobes reference implementation
|
||||
float hueShift, satScale, valScale;
|
||||
|
||||
if (iValDivisions < 2) // Optimize most common case of "2.5D" table.
|
||||
{
|
||||
float hScaled = hs * hScale;
|
||||
float sScaled = ss * sScale;
|
||||
|
||||
int hIndex0 = max((int)hScaled, 0);
|
||||
int sIndex0 = max(min((int)sScaled,maxSatIndex0),0);
|
||||
|
||||
int hIndex1 = hIndex0 + 1;
|
||||
|
||||
if (hIndex0 >= maxHueIndex0)
|
||||
{
|
||||
hIndex0 = maxHueIndex0;
|
||||
hIndex1 = 0;
|
||||
if (useRawWhite) {
|
||||
// Retro-calculate what the point was like before RAW white came in
|
||||
Color::rgb2hsv(newr/rawWhiteFac, newg/rawWhiteFac, newb/rawWhiteFac, hs, ss, vs);
|
||||
hs*=6.f; // RT calculates in [0,1]
|
||||
} else {
|
||||
hs=h; ss=s; vs=v;
|
||||
}
|
||||
|
||||
float hFract1 = hScaled - (float) hIndex0;
|
||||
float sFract1 = sScaled - (float) sIndex0;
|
||||
// Apply the HueSatMap. Ported from Adobes reference implementation
|
||||
float hueShift, satScale, valScale;
|
||||
|
||||
float hFract0 = 1.0f - hFract1;
|
||||
float sFract0 = 1.0f - sFract1;
|
||||
|
||||
const HSBModify *entry00 = tableBase + hIndex0 * hueStep +
|
||||
sIndex0;
|
||||
|
||||
const HSBModify *entry01 = entry00 + (hIndex1 - hIndex0) * hueStep;
|
||||
|
||||
float hueShift0 = hFract0 * entry00->fHueShift +
|
||||
hFract1 * entry01->fHueShift;
|
||||
|
||||
float satScale0 = hFract0 * entry00->fSatScale +
|
||||
hFract1 * entry01->fSatScale;
|
||||
|
||||
float valScale0 = hFract0 * entry00->fValScale +
|
||||
hFract1 * entry01->fValScale;
|
||||
|
||||
entry00++;
|
||||
entry01++;
|
||||
|
||||
float hueShift1 = hFract0 * entry00->fHueShift +
|
||||
hFract1 * entry01->fHueShift;
|
||||
|
||||
float satScale1 = hFract0 * entry00->fSatScale +
|
||||
hFract1 * entry01->fSatScale;
|
||||
|
||||
float valScale1 = hFract0 * entry00->fValScale +
|
||||
hFract1 * entry01->fValScale;
|
||||
|
||||
hueShift = sFract0 * hueShift0 + sFract1 * hueShift1;
|
||||
satScale = sFract0 * satScale0 + sFract1 * satScale1;
|
||||
valScale = sFract0 * valScale0 + sFract1 * valScale1;
|
||||
|
||||
} else {
|
||||
|
||||
float hScaled = hs * hScale;
|
||||
float sScaled = ss * sScale;
|
||||
float vScaled = vs * vScale;
|
||||
|
||||
int hIndex0 = (int) hScaled;
|
||||
int sIndex0 = max(min((int)sScaled,maxSatIndex0),0);
|
||||
int vIndex0 = max(min((int)vScaled,maxValIndex0),0);
|
||||
|
||||
int hIndex1 = hIndex0 + 1;
|
||||
|
||||
if (hIndex0 >= maxHueIndex0)
|
||||
if (iValDivisions < 2) // Optimize most common case of "2.5D" table.
|
||||
{
|
||||
hIndex0 = maxHueIndex0;
|
||||
hIndex1 = 0;
|
||||
float hScaled = hs * hScale;
|
||||
float sScaled = ss * sScale;
|
||||
|
||||
int hIndex0 = max((int)hScaled, 0);
|
||||
int sIndex0 = max(min((int)sScaled,maxSatIndex0),0);
|
||||
|
||||
int hIndex1 = hIndex0 + 1;
|
||||
|
||||
if (hIndex0 >= maxHueIndex0)
|
||||
{
|
||||
hIndex0 = maxHueIndex0;
|
||||
hIndex1 = 0;
|
||||
}
|
||||
|
||||
float hFract1 = hScaled - (float) hIndex0;
|
||||
float sFract1 = sScaled - (float) sIndex0;
|
||||
|
||||
float hFract0 = 1.0f - hFract1;
|
||||
float sFract0 = 1.0f - sFract1;
|
||||
|
||||
const HSBModify *entry00 = tableBase + hIndex0 * hueStep +
|
||||
sIndex0;
|
||||
|
||||
const HSBModify *entry01 = entry00 + (hIndex1 - hIndex0) * hueStep;
|
||||
|
||||
float hueShift0 = hFract0 * entry00->fHueShift +
|
||||
hFract1 * entry01->fHueShift;
|
||||
|
||||
float satScale0 = hFract0 * entry00->fSatScale +
|
||||
hFract1 * entry01->fSatScale;
|
||||
|
||||
float valScale0 = hFract0 * entry00->fValScale +
|
||||
hFract1 * entry01->fValScale;
|
||||
|
||||
entry00++;
|
||||
entry01++;
|
||||
|
||||
float hueShift1 = hFract0 * entry00->fHueShift +
|
||||
hFract1 * entry01->fHueShift;
|
||||
|
||||
float satScale1 = hFract0 * entry00->fSatScale +
|
||||
hFract1 * entry01->fSatScale;
|
||||
|
||||
float valScale1 = hFract0 * entry00->fValScale +
|
||||
hFract1 * entry01->fValScale;
|
||||
|
||||
hueShift = sFract0 * hueShift0 + sFract1 * hueShift1;
|
||||
satScale = sFract0 * satScale0 + sFract1 * satScale1;
|
||||
valScale = sFract0 * valScale0 + sFract1 * valScale1;
|
||||
|
||||
} else {
|
||||
|
||||
float hScaled = hs * hScale;
|
||||
float sScaled = ss * sScale;
|
||||
float vScaled = vs * vScale;
|
||||
|
||||
int hIndex0 = (int) hScaled;
|
||||
int sIndex0 = max(min((int)sScaled,maxSatIndex0),0);
|
||||
int vIndex0 = max(min((int)vScaled,maxValIndex0),0);
|
||||
|
||||
int hIndex1 = hIndex0 + 1;
|
||||
|
||||
if (hIndex0 >= maxHueIndex0)
|
||||
{
|
||||
hIndex0 = maxHueIndex0;
|
||||
hIndex1 = 0;
|
||||
}
|
||||
|
||||
float hFract1 = hScaled - (float) hIndex0;
|
||||
float sFract1 = sScaled - (float) sIndex0;
|
||||
float vFract1 = vScaled - (float) vIndex0;
|
||||
|
||||
float hFract0 = 1.0f - hFract1;
|
||||
float sFract0 = 1.0f - sFract1;
|
||||
float vFract0 = 1.0f - vFract1;
|
||||
|
||||
const HSBModify *entry00 = tableBase + vIndex0 * valStep +
|
||||
hIndex0 * hueStep +
|
||||
sIndex0;
|
||||
|
||||
const HSBModify *entry01 = entry00 + (hIndex1 - hIndex0) * hueStep;
|
||||
|
||||
const HSBModify *entry10 = entry00 + valStep;
|
||||
const HSBModify *entry11 = entry01 + valStep;
|
||||
|
||||
float hueShift0 = vFract0 * (hFract0 * entry00->fHueShift +
|
||||
hFract1 * entry01->fHueShift) +
|
||||
vFract1 * (hFract0 * entry10->fHueShift +
|
||||
hFract1 * entry11->fHueShift);
|
||||
|
||||
float satScale0 = vFract0 * (hFract0 * entry00->fSatScale +
|
||||
hFract1 * entry01->fSatScale) +
|
||||
vFract1 * (hFract0 * entry10->fSatScale +
|
||||
hFract1 * entry11->fSatScale);
|
||||
|
||||
float valScale0 = vFract0 * (hFract0 * entry00->fValScale +
|
||||
hFract1 * entry01->fValScale) +
|
||||
vFract1 * (hFract0 * entry10->fValScale +
|
||||
hFract1 * entry11->fValScale);
|
||||
|
||||
entry00++;
|
||||
entry01++;
|
||||
entry10++;
|
||||
entry11++;
|
||||
|
||||
float hueShift1 = vFract0 * (hFract0 * entry00->fHueShift +
|
||||
hFract1 * entry01->fHueShift) +
|
||||
vFract1 * (hFract0 * entry10->fHueShift +
|
||||
hFract1 * entry11->fHueShift);
|
||||
|
||||
float satScale1 = vFract0 * (hFract0 * entry00->fSatScale +
|
||||
hFract1 * entry01->fSatScale) +
|
||||
vFract1 * (hFract0 * entry10->fSatScale +
|
||||
hFract1 * entry11->fSatScale);
|
||||
|
||||
float valScale1 = vFract0 * (hFract0 * entry00->fValScale +
|
||||
hFract1 * entry01->fValScale) +
|
||||
vFract1 * (hFract0 * entry10->fValScale +
|
||||
hFract1 * entry11->fValScale);
|
||||
|
||||
hueShift = sFract0 * hueShift0 + sFract1 * hueShift1;
|
||||
satScale = sFract0 * satScale0 + sFract1 * satScale1;
|
||||
valScale = sFract0 * valScale0 + sFract1 * valScale1;
|
||||
}
|
||||
|
||||
float hFract1 = hScaled - (float) hIndex0;
|
||||
float sFract1 = sScaled - (float) sIndex0;
|
||||
float vFract1 = vScaled - (float) vIndex0;
|
||||
hueShift *= (6.0f / 360.0f); // Convert to internal hue range.
|
||||
|
||||
float hFract0 = 1.0f - hFract1;
|
||||
float sFract0 = 1.0f - sFract1;
|
||||
float vFract0 = 1.0f - vFract1;
|
||||
h += hueShift;
|
||||
s *= satScale; // no clipping here, we are RT float :-)
|
||||
v *= valScale;
|
||||
|
||||
const HSBModify *entry00 = tableBase + vIndex0 * valStep +
|
||||
hIndex0 * hueStep +
|
||||
sIndex0;
|
||||
|
||||
const HSBModify *entry01 = entry00 + (hIndex1 - hIndex0) * hueStep;
|
||||
|
||||
const HSBModify *entry10 = entry00 + valStep;
|
||||
const HSBModify *entry11 = entry01 + valStep;
|
||||
|
||||
float hueShift0 = vFract0 * (hFract0 * entry00->fHueShift +
|
||||
hFract1 * entry01->fHueShift) +
|
||||
vFract1 * (hFract0 * entry10->fHueShift +
|
||||
hFract1 * entry11->fHueShift);
|
||||
|
||||
float satScale0 = vFract0 * (hFract0 * entry00->fSatScale +
|
||||
hFract1 * entry01->fSatScale) +
|
||||
vFract1 * (hFract0 * entry10->fSatScale +
|
||||
hFract1 * entry11->fSatScale);
|
||||
|
||||
float valScale0 = vFract0 * (hFract0 * entry00->fValScale +
|
||||
hFract1 * entry01->fValScale) +
|
||||
vFract1 * (hFract0 * entry10->fValScale +
|
||||
hFract1 * entry11->fValScale);
|
||||
|
||||
entry00++;
|
||||
entry01++;
|
||||
entry10++;
|
||||
entry11++;
|
||||
|
||||
float hueShift1 = vFract0 * (hFract0 * entry00->fHueShift +
|
||||
hFract1 * entry01->fHueShift) +
|
||||
vFract1 * (hFract0 * entry10->fHueShift +
|
||||
hFract1 * entry11->fHueShift);
|
||||
|
||||
float satScale1 = vFract0 * (hFract0 * entry00->fSatScale +
|
||||
hFract1 * entry01->fSatScale) +
|
||||
vFract1 * (hFract0 * entry10->fSatScale +
|
||||
hFract1 * entry11->fSatScale);
|
||||
|
||||
float valScale1 = vFract0 * (hFract0 * entry00->fValScale +
|
||||
hFract1 * entry01->fValScale) +
|
||||
vFract1 * (hFract0 * entry10->fValScale +
|
||||
hFract1 * entry11->fValScale);
|
||||
|
||||
hueShift = sFract0 * hueShift0 + sFract1 * hueShift1;
|
||||
satScale = sFract0 * satScale0 + sFract1 * satScale1;
|
||||
valScale = sFract0 * valScale0 + sFract1 * valScale1;
|
||||
// RT range correction
|
||||
if (h < 0.0f) h += 6.0f;
|
||||
if (h >= 6.0f) h -= 6.0f;
|
||||
h/=6.f;
|
||||
Color::hsv2rgb( h, s, v, newr, newg, newb);
|
||||
}
|
||||
|
||||
hueShift *= (6.0f / 360.0f); // Convert to internal hue range.
|
||||
|
||||
h += hueShift;
|
||||
s *= satScale; // no clipping here, we are RT float :-)
|
||||
v *= valScale;
|
||||
|
||||
// RT range correction
|
||||
if (h < 0.0f) h += 6.0f;
|
||||
if (h >= 6.0f) h -= 6.0f;
|
||||
h/=6.f;
|
||||
Color::hsv2rgb( h, s, v, newr, newg, newb);
|
||||
}
|
||||
// tone curve
|
||||
if (useToneCurve) ApplyToneCurve(newr, newg, newb);
|
||||
|
||||
pImg->r[y][x] = m2Work[0][0]*newr + m2Work[0][1]*newg + m2Work[0][2]*newb;
|
||||
pImg->g[y][x] = m2Work[1][0]*newr + m2Work[1][1]*newg + m2Work[1][2]*newb;
|
||||
@@ -433,14 +458,73 @@ void DCPProfile::Apply(Imagefloat *pImg, DCPLightType preferredProfile, Glib::us
|
||||
}
|
||||
}
|
||||
|
||||
// Tone curve according to Adobes reference implementation
|
||||
void DCPProfile::ApplyToneCurve (float& r, float& g, float& b) const {
|
||||
if (r >= g)
|
||||
{
|
||||
if (g > b)
|
||||
{
|
||||
// Case 1: r >= g > b
|
||||
RGBTone (r, g, b);
|
||||
}
|
||||
|
||||
else if (b > r)
|
||||
{
|
||||
// Case 2: b > r >= g
|
||||
RGBTone (b, r, g);
|
||||
}
|
||||
|
||||
else if (b > g)
|
||||
{
|
||||
// Case 3: r >= b > g
|
||||
RGBTone (r, b, g);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// Case 4: r >= g == b
|
||||
r = lutToneCurve[r];
|
||||
g = lutToneCurve[g];
|
||||
b = g;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (r >= b)
|
||||
{
|
||||
// Case 5: g > r >= b
|
||||
RGBTone (g, r, b);
|
||||
}
|
||||
else if (b > g)
|
||||
{
|
||||
// Case 6: b > g > r
|
||||
RGBTone (b, g, r);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Case 7: g >= b > r
|
||||
RGBTone (g, b, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DCPProfile::RGBTone (float& r, float& g, float& b) const {
|
||||
float rold=r,gold=g,bold=b;
|
||||
|
||||
r = lutToneCurve[rold];
|
||||
b = lutToneCurve[bold];
|
||||
g = b + ((r - b) * (gold - bold) / (rold - bold));
|
||||
}
|
||||
|
||||
// Integer variant is legacy, only used for thumbs. Simply take the matrix here
|
||||
void DCPProfile::Apply(Image16 *pImg, DCPLightType preferredProfile, Glib::ustring workingSpace) const {
|
||||
void DCPProfile::Apply(Image16 *pImg, DCPLightType preferredProfile, Glib::ustring workingSpace, bool useToneCurve) const {
|
||||
TMatrix mWork = iccStore->workingSpaceInverseMatrix (workingSpace);
|
||||
|
||||
double mXYZCAM[3][3];
|
||||
const HSBModify* tableBase=GetBestProfile(preferredProfile,mXYZCAM);
|
||||
|
||||
useToneCurve&=lutToneCurve;
|
||||
|
||||
// Calculate matrix for direct conversion raw>working space
|
||||
double mat[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
|
||||
for (int i=0; i<3; i++)
|
||||
@@ -457,6 +541,9 @@ void DCPProfile::Apply(Image16 *pImg, DCPLightType preferredProfile, Glib::ustri
|
||||
newg = mat[1][0]*pImg->r[y][x] + mat[1][1]*pImg->g[y][x] + mat[1][2]*pImg->b[y][x];
|
||||
newb = mat[2][0]*pImg->r[y][x] + mat[2][1]*pImg->g[y][x] + mat[2][2]*pImg->b[y][x];
|
||||
|
||||
// tone curve
|
||||
if (useToneCurve) ApplyToneCurve(newr, newg, newb);
|
||||
|
||||
pImg->r[y][x] = CLIP((int)newr); pImg->g[y][x] = CLIP((int)newg); pImg->b[y][x] = CLIP((int)newb);
|
||||
}
|
||||
}
|
||||
@@ -523,14 +610,14 @@ void DCPStore::init (Glib::ustring rtProfileDir) {
|
||||
}
|
||||
}
|
||||
|
||||
DCPProfile* DCPStore::getProfile (Glib::ustring filename) {
|
||||
DCPProfile* DCPStore::getProfile (Glib::ustring filename, bool isRTProfile) {
|
||||
Glib::Mutex::Lock lock(mtx);
|
||||
|
||||
std::map<Glib::ustring, DCPProfile*>::iterator r = profileCache.find (filename);
|
||||
if (r!=profileCache.end()) return r->second;
|
||||
|
||||
// Add profile
|
||||
profileCache[filename]=new DCPProfile(filename);
|
||||
profileCache[filename]=new DCPProfile(filename, isRTProfile);
|
||||
|
||||
return profileCache[filename];
|
||||
}
|
||||
@@ -540,7 +627,7 @@ DCPProfile* DCPStore::getStdProfile(Glib::ustring camShortName) {
|
||||
|
||||
// Warning: do NOT use map.find(), since it does not seem to work reliably here
|
||||
for (std::map<Glib::ustring, Glib::ustring>::iterator i=fileStdProfiles.begin();i!=fileStdProfiles.end();i++)
|
||||
if (name2==(*i).first) return getProfile((*i).second);
|
||||
if (name2==(*i).first) return getProfile((*i).second, true);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -548,5 +635,5 @@ DCPProfile* DCPStore::getStdProfile(Glib::ustring camShortName) {
|
||||
bool DCPStore::isValidDCPFileName(Glib::ustring filename) const {
|
||||
if (!safe_file_test (filename, Glib::FILE_TEST_EXISTS) || safe_file_test (filename, Glib::FILE_TEST_IS_DIR)) return false;
|
||||
size_t pos=filename.find_last_of ('.');
|
||||
return pos>0 && !filename.casefold().compare (pos, 4, ".dcp");
|
||||
return pos>0 && (!filename.casefold().compare (pos, 4, ".dcp") || !filename.casefold().compare (pos, 4, ".dng"));
|
||||
}
|
||||
@@ -47,6 +47,10 @@ namespace rtengine {
|
||||
|
||||
int iHueStep, iValStep, iArrayCount;
|
||||
|
||||
LUTf lutToneCurve; // 0..0xffff values to 0..1
|
||||
void ApplyToneCurve(float& r, float& g, float& b) const;
|
||||
void RGBTone(float& r, float& g, float& b) const; // helper for tone curve
|
||||
|
||||
void ConvertDNGMatrix2XYZCAM(const double (*mColorMatrix)[3], double (*mXYZCAM)[3]);
|
||||
|
||||
const HSBModify* GetBestProfile(DCPLightType preferredProfile, double (*mXYZCAM)[3]) const;
|
||||
@@ -54,11 +58,11 @@ namespace rtengine {
|
||||
DCPLightType GetLightType(short iLightSource) const;
|
||||
|
||||
public:
|
||||
DCPProfile(Glib::ustring fname);
|
||||
DCPProfile(Glib::ustring fname, bool isRTProfile);
|
||||
~DCPProfile();
|
||||
|
||||
void Apply(Imagefloat *pImg, DCPLightType preferredProfile, Glib::ustring workingSpace, float rawWhiteFac=1) const;
|
||||
void Apply(Image16 *pImg, DCPLightType preferredProfile, Glib::ustring workingSpace) const;
|
||||
void Apply(Imagefloat *pImg, DCPLightType preferredProfile, Glib::ustring workingSpace, float rawWhiteFac=1, bool useToneCurve=false) const;
|
||||
void Apply(Image16 *pImg, DCPLightType preferredProfile, Glib::ustring workingSpace, bool useToneCurve) const;
|
||||
};
|
||||
|
||||
class DCPStore {
|
||||
@@ -75,7 +79,7 @@ namespace rtengine {
|
||||
|
||||
bool isValidDCPFileName(Glib::ustring filename) const;
|
||||
|
||||
DCPProfile* getProfile(Glib::ustring filename);
|
||||
DCPProfile* getProfile(Glib::ustring filename, bool isRTProfile=false);
|
||||
DCPProfile* getStdProfile(Glib::ustring camShortName);
|
||||
|
||||
static DCPStore* getInstance();
|
||||
|
||||
@@ -69,7 +69,7 @@ enum ProcEvent {
|
||||
EvLDNEdgeTolerance=44, // obsolete
|
||||
EvCDNEnabled=45, // obsolete
|
||||
EvCDNRadius=46, // obsolete
|
||||
EvCDNEdgeTolerance=47, // obsolete
|
||||
EvDCPToneCurve=47,
|
||||
EvPrefProfile=48,
|
||||
EvSHEnabled=49,
|
||||
EvSHHighlights=50,
|
||||
|
||||
@@ -309,6 +309,7 @@ void ProcParams::setDefaults () {
|
||||
|
||||
icm.input = "";
|
||||
icm.blendCMSMatrix = false;
|
||||
icm.toneCurve = false;
|
||||
icm.preferredProfile = (short)rtengine::Daylight;
|
||||
icm.working = "sRGB";
|
||||
icm.output = "sRGB";
|
||||
@@ -614,6 +615,7 @@ int ProcParams::save (Glib::ustring fname, Glib::ustring fname2, ParamsEdited* p
|
||||
|
||||
// save color management settings
|
||||
if (!pedited || pedited->icm.input) keyFile.set_string ("Color Management", "InputProfile", icm.input);
|
||||
if (!pedited || pedited->icm.toneCurve) keyFile.set_boolean ("Color Management", "ToneCurve", icm.toneCurve);
|
||||
if (!pedited || pedited->icm.blendCMSMatrix) keyFile.set_boolean ("Color Management", "BlendCMSMatrix", icm.blendCMSMatrix);
|
||||
if (!pedited || pedited->icm.preferredProfile) keyFile.set_boolean ("Color Management", "PreferredProfile", icm.preferredProfile);
|
||||
if (!pedited || pedited->icm.working) keyFile.set_string ("Color Management", "WorkingProfile", icm.working);
|
||||
@@ -1068,6 +1070,7 @@ if (keyFile.has_group ("Resize")) {
|
||||
// load color management settings
|
||||
if (keyFile.has_group ("Color Management")) {
|
||||
if (keyFile.has_key ("Color Management", "InputProfile")) { icm.input = keyFile.get_string ("Color Management", "InputProfile"); if (pedited) pedited->icm.input = true; }
|
||||
if (keyFile.has_key ("Color Management", "ToneCurve")) { icm.toneCurve = keyFile.get_boolean ("Color Management", "ToneCurve"); if (pedited) pedited->icm.toneCurve = true; }
|
||||
if (keyFile.has_key ("Color Management", "BlendCMSMatrix")) { icm.blendCMSMatrix = keyFile.get_boolean ("Color Management", "BlendCMSMatrix"); if (pedited) pedited->icm.blendCMSMatrix = true; }
|
||||
if (keyFile.has_key ("Color Management", "PreferredProfile")) { icm.preferredProfile = keyFile.get_boolean ("Color Management", "PreferredProfile"); if (pedited) pedited->icm.preferredProfile = true; }
|
||||
if (keyFile.has_key ("Color Management", "WorkingProfile")) { icm.working = keyFile.get_string ("Color Management", "WorkingProfile"); if (pedited) pedited->icm.working = true; }
|
||||
@@ -1369,6 +1372,7 @@ bool ProcParams::operator== (const ProcParams& other) {
|
||||
&& raw.greenthresh == other.raw.greenthresh
|
||||
&& raw.linenoise == other.raw.linenoise
|
||||
&& icm.input == other.icm.input
|
||||
&& icm.toneCurve == other.icm.toneCurve
|
||||
&& icm.blendCMSMatrix == other.icm.blendCMSMatrix
|
||||
&& icm.preferredProfile == other.icm.preferredProfile
|
||||
&& icm.working == other.icm.working
|
||||
|
||||
@@ -569,6 +569,7 @@ class ColorManagementParams {
|
||||
|
||||
public:
|
||||
Glib::ustring input;
|
||||
bool toneCurve;
|
||||
bool blendCMSMatrix;
|
||||
short preferredProfile;
|
||||
Glib::ustring working;
|
||||
|
||||
@@ -1650,7 +1650,7 @@ void RawImageSource::colorSpaceConversion (Imagefloat* im, ColorManagementParams
|
||||
if (!findInputProfile(cmp.input, embedded, camName, &dcpProf, in)) return;
|
||||
|
||||
if (dcpProf!=NULL) {
|
||||
dcpProf->Apply(im, (DCPLightType)cmp.preferredProfile, cmp.working, (float)raw.expos);
|
||||
dcpProf->Apply(im, (DCPLightType)cmp.preferredProfile, cmp.working, (float)raw.expos, cmp.toneCurve);
|
||||
} else {
|
||||
// Calculate matrix for direct conversion raw>working space
|
||||
TMatrix work = iccStore->workingSpaceInverseMatrix (cmp.working);
|
||||
@@ -1874,7 +1874,7 @@ void RawImageSource::colorSpaceConversion16 (Image16* im, ColorManagementParams
|
||||
if (!findInputProfile(cmp.input, embedded, camName, &dcpProf, in)) return;
|
||||
|
||||
if (dcpProf!=NULL) {
|
||||
dcpProf->Apply(im, (DCPLightType)cmp.preferredProfile, cmp.working);
|
||||
dcpProf->Apply(im, (DCPLightType)cmp.preferredProfile, cmp.working, cmp.toneCurve);
|
||||
} else {
|
||||
if (in==NULL) {
|
||||
// Take camprofile from DCRAW
|
||||
|
||||
@@ -67,7 +67,7 @@ WHITEBALANCE, // EvWBGreen,
|
||||
0, // EvLDNEdgeTolerance: obsolete,
|
||||
0, // EvCDNEnabled:obsolete,
|
||||
0, // EvCDNRadius: obsolete,
|
||||
0, // EvCDNEdgeTolerance: obsolete,
|
||||
ALL, // EvDCPToneCurve,
|
||||
ALL, // EvPrefProfile,
|
||||
RETINEX, // EvSHEnabled,
|
||||
RGBCURVE, // EvSHHighlights,
|
||||
|
||||
@@ -87,6 +87,11 @@ ICMPanel::ICMPanel () : Gtk::VBox(), FoldableToolPanel(this), iunchanged(NULL),
|
||||
hb->pack_start(*prefprof);
|
||||
pack_start (*hb, Gtk::PACK_SHRINK, 4);
|
||||
|
||||
ckbToneCurve = Gtk::manage (new Gtk::CheckButton (M("TP_ICM_TONECURVE")));
|
||||
ckbToneCurve->set_sensitive (false);
|
||||
ckbToneCurve->set_tooltip_text (M("TP_ICM_TONECURVE_TOOLTIP"));
|
||||
pack_start (*ckbToneCurve, Gtk::PACK_SHRINK, 4);
|
||||
|
||||
ckbBlendCMSMatrix = Gtk::manage (new Gtk::CheckButton (M("TP_ICM_BLENDCMSMATRIX")));
|
||||
ckbBlendCMSMatrix->set_sensitive (false);
|
||||
ckbBlendCMSMatrix->set_tooltip_text (M("TP_ICM_BLENDCMSMATRIX_TOOLTIP"));
|
||||
@@ -176,6 +181,8 @@ ICMPanel::ICMPanel () : Gtk::VBox(), FoldableToolPanel(this), iunchanged(NULL),
|
||||
filter_icc.set_name(M("TP_ICM_FILEDLGFILTERICM"));
|
||||
filter_icc.add_pattern("*.dcp");
|
||||
filter_icc.add_pattern("*.DCP");
|
||||
filter_icc.add_pattern("*.dng");
|
||||
filter_icc.add_pattern("*.DNG");
|
||||
filter_icc.add_pattern("*.icc");
|
||||
filter_icc.add_pattern("*.icm");
|
||||
filter_icc.add_pattern("*.ICC");
|
||||
@@ -199,6 +206,8 @@ ICMPanel::ICMPanel () : Gtk::VBox(), FoldableToolPanel(this), iunchanged(NULL),
|
||||
iembedded->signal_toggled().connect( sigc::mem_fun(*this, &ICMPanel::ipChanged) );
|
||||
ifromfile->signal_toggled().connect( sigc::mem_fun(*this, &ICMPanel::ipChanged) );
|
||||
ckbBlendCMSMatrix->signal_toggled().connect( sigc::mem_fun(*this, &ICMPanel::iccTogglesChanged) );
|
||||
ckbToneCurve->signal_toggled().connect( sigc::mem_fun(*this, &ICMPanel::toneCurveChanged) );
|
||||
|
||||
ipc = ipDialog->signal_selection_changed().connect( sigc::mem_fun(*this, &ICMPanel::ipSelectionChanged) );
|
||||
saveRef->signal_pressed().connect( sigc::mem_fun(*this, &ICMPanel::saveReferencePressed) );
|
||||
|
||||
@@ -211,33 +220,33 @@ void ICMPanel::read (const ProcParams* pp, const ParamsEdited* pedited) {
|
||||
|
||||
ipc.block (true);
|
||||
if (pp->icm.input == "(none)" && icamera->get_state()!=Gtk::STATE_INSENSITIVE) {
|
||||
inone->set_active (true); prefprof->set_sensitive (false);
|
||||
inone->set_active (true); prefprof->set_sensitive (false); ckbToneCurve->set_sensitive (false);
|
||||
ckbBlendCMSMatrix->set_sensitive (false);
|
||||
}
|
||||
else if (pp->icm.input == "(embedded)" || ((pp->icm.input == "(camera)" || pp->icm.input=="") && icamera->get_state()==Gtk::STATE_INSENSITIVE)) {
|
||||
iembedded->set_active (true); prefprof->set_sensitive (false);
|
||||
iembedded->set_active (true); prefprof->set_sensitive (false); ckbToneCurve->set_sensitive (false);
|
||||
ckbBlendCMSMatrix->set_sensitive (false);
|
||||
}
|
||||
else if ((pp->icm.input == "(cameraICC)") && icameraICC->get_state()!=Gtk::STATE_INSENSITIVE) {
|
||||
icameraICC->set_active (true); prefprof->set_sensitive (true);
|
||||
icameraICC->set_active (true); prefprof->set_sensitive (true); ckbToneCurve->set_sensitive (true);
|
||||
ckbBlendCMSMatrix->set_sensitive (true);
|
||||
}
|
||||
else if ((pp->icm.input == "(cameraICC)") && icameraICC->get_state()==Gtk::STATE_INSENSITIVE) {
|
||||
// this is the case when (cameraICC) is instructed by packaged profiles, but ICC file is not found
|
||||
// therefore falling back UI to explicitly reflect the (camera) option
|
||||
icamera->set_active (true);
|
||||
prefprof->set_sensitive (false); // RT's own are always single-illuminant
|
||||
prefprof->set_sensitive (false); ckbToneCurve->set_sensitive (false); // RT's own are always single-illuminant and tone curve disabled
|
||||
ckbBlendCMSMatrix->set_sensitive (false);
|
||||
}
|
||||
else if ((pp->icm.input == "(camera)" || pp->icm.input=="") && icamera->get_state()!=Gtk::STATE_INSENSITIVE) {
|
||||
icamera->set_active (true);
|
||||
ckbBlendCMSMatrix->set_sensitive (false); prefprof->set_sensitive (false);
|
||||
ckbBlendCMSMatrix->set_sensitive (false); prefprof->set_sensitive (false); ckbToneCurve->set_sensitive (false);
|
||||
}
|
||||
else {
|
||||
ifromfile->set_active (true);
|
||||
oldip = pp->icm.input.substr(5); // cut of "file:"
|
||||
ipDialog->set_filename (pp->icm.input.substr(5));
|
||||
ckbBlendCMSMatrix->set_sensitive (true); prefprof->set_sensitive (true);
|
||||
ckbBlendCMSMatrix->set_sensitive (true); prefprof->set_sensitive (true); ckbToneCurve->set_sensitive (true);
|
||||
}
|
||||
|
||||
wnames->set_active_text (pp->icm.working);
|
||||
@@ -253,12 +262,14 @@ void ICMPanel::read (const ProcParams* pp, const ParamsEdited* pedited) {
|
||||
|
||||
prefprof->set_active(pp->icm.preferredProfile-1);
|
||||
|
||||
ckbToneCurve->set_active (pp->icm.toneCurve);
|
||||
ckbBlendCMSMatrix->set_active (pp->icm.blendCMSMatrix);
|
||||
onames->set_sensitive(wgamma->get_active_row_number()==0 || freegamma->get_active()); //"default"
|
||||
wgamma->set_sensitive(!freegamma->get_active());
|
||||
|
||||
if (pedited) {
|
||||
iunchanged->set_active (!pedited->icm.input);
|
||||
ckbToneCurve->set_sensitive (false);
|
||||
ckbBlendCMSMatrix->set_sensitive (false);
|
||||
if (!pedited->icm.working)
|
||||
wnames->set_active_text(M("GENERAL_UNCHANGED"));
|
||||
@@ -317,6 +328,7 @@ void ICMPanel::write (ProcParams* pp, ParamsEdited* pedited) {
|
||||
else
|
||||
pp->icm.output = onames->get_active_text();
|
||||
pp->icm.freegamma = freegamma->get_active();
|
||||
pp->icm.toneCurve = ckbToneCurve->get_active ();
|
||||
pp->icm.blendCMSMatrix = ckbBlendCMSMatrix->get_active ();
|
||||
pp->icm.gampos =(double) gampos->getValue();
|
||||
pp->icm.slpos =(double) slpos->getValue();
|
||||
@@ -326,6 +338,7 @@ void ICMPanel::write (ProcParams* pp, ParamsEdited* pedited) {
|
||||
pedited->icm.working = wnames->get_active_text()!=M("GENERAL_UNCHANGED");
|
||||
pedited->icm.output = onames->get_active_text()!=M("GENERAL_UNCHANGED");
|
||||
pedited->icm.preferredProfile = prefprof->get_active_text()!=M("GENERAL_UNCHANGED");
|
||||
pedited->icm.toneCurve = !ckbToneCurve->get_inconsistent ();
|
||||
pedited->icm.blendCMSMatrix = !ckbBlendCMSMatrix->get_inconsistent ();
|
||||
pedited->icm.gamma = wgamma->get_active_text()!=M("GENERAL_UNCHANGED");
|
||||
pedited->icm.freegamma =!freegamma->get_inconsistent();
|
||||
@@ -389,28 +402,33 @@ void ICMPanel::prefProfChanged() {
|
||||
listener->panelChanged (EvPrefProfile, prefprof->get_active_text ());
|
||||
}
|
||||
|
||||
void ICMPanel::toneCurveChanged() {
|
||||
if (listener)
|
||||
listener->panelChanged (EvDCPToneCurve, "");
|
||||
}
|
||||
|
||||
void ICMPanel::ipChanged () {
|
||||
|
||||
std::string profname;
|
||||
if (inone->get_active()) {
|
||||
profname = "(none)";
|
||||
ckbBlendCMSMatrix->set_sensitive(false); prefprof->set_sensitive (false);
|
||||
ckbBlendCMSMatrix->set_sensitive(false); prefprof->set_sensitive (false); ckbToneCurve->set_sensitive (false);
|
||||
}
|
||||
else if (iembedded->get_active ()) {
|
||||
profname = "(embedded)";
|
||||
ckbBlendCMSMatrix->set_sensitive(false); prefprof->set_sensitive (false);
|
||||
ckbBlendCMSMatrix->set_sensitive(false); prefprof->set_sensitive (false); ckbToneCurve->set_sensitive (false);
|
||||
}
|
||||
else if (icamera->get_active ()) {
|
||||
profname = "(camera)";
|
||||
ckbBlendCMSMatrix->set_sensitive(false); prefprof->set_sensitive (false);
|
||||
ckbBlendCMSMatrix->set_sensitive(false); prefprof->set_sensitive (false); ckbToneCurve->set_sensitive (false);
|
||||
}
|
||||
else if (icameraICC->get_active ()) {
|
||||
profname = "(cameraICC)";
|
||||
ckbBlendCMSMatrix->set_sensitive(true); prefprof->set_sensitive (false);
|
||||
ckbBlendCMSMatrix->set_sensitive(true); prefprof->set_sensitive (false); ckbToneCurve->set_sensitive (false);
|
||||
}
|
||||
else {
|
||||
profname = ipDialog->get_filename ();
|
||||
ckbBlendCMSMatrix->set_sensitive(true); prefprof->set_sensitive (true);
|
||||
ckbBlendCMSMatrix->set_sensitive(true); prefprof->set_sensitive (true); ckbToneCurve->set_sensitive (true);
|
||||
}
|
||||
|
||||
if (listener && profname!=oldip)
|
||||
|
||||
@@ -51,6 +51,7 @@ class ICMPanel : public Gtk::VBox, public AdjusterListener, public FoldableToolP
|
||||
Gtk::RadioButton* icameraICC;
|
||||
Gtk::RadioButton* ifromfile;
|
||||
MyComboBoxText* prefprof;
|
||||
Gtk::CheckButton* ckbToneCurve;
|
||||
Gtk::CheckButton* ckbBlendCMSMatrix;
|
||||
MyComboBoxText* wnames;
|
||||
MyComboBoxText* wgamma;
|
||||
@@ -88,6 +89,7 @@ class ICMPanel : public Gtk::VBox, public AdjusterListener, public FoldableToolP
|
||||
void ipSelectionChanged ();
|
||||
void iccTogglesChanged();
|
||||
void prefProfChanged();
|
||||
void toneCurveChanged();
|
||||
|
||||
void setRawMeta (bool raw, const rtengine::ImageData* pMeta);
|
||||
void saveReferencePressed ();
|
||||
|
||||
@@ -172,6 +172,7 @@ void ParamsEdited::set (bool v) {
|
||||
resize.height = v;
|
||||
resize.enabled = v;
|
||||
icm.input = v;
|
||||
icm.toneCurve = v;
|
||||
icm.blendCMSMatrix = v;
|
||||
icm.preferredProfile = v;
|
||||
icm.working = v;
|
||||
@@ -373,6 +374,7 @@ void ParamsEdited::initFrom (const std::vector<rtengine::procparams::ProcParams>
|
||||
resize.height = resize.height && p.resize.height == other.resize.height;
|
||||
resize.enabled = resize.enabled && p.resize.enabled == other.resize.enabled;
|
||||
icm.input = icm.input && p.icm.input == other.icm.input;
|
||||
icm.toneCurve = icm.toneCurve && p.icm.toneCurve == other.icm.toneCurve;
|
||||
icm.blendCMSMatrix = icm.blendCMSMatrix && p.icm.blendCMSMatrix == other.icm.blendCMSMatrix;
|
||||
icm.preferredProfile = icm.preferredProfile && p.icm.preferredProfile == other.icm.preferredProfile;
|
||||
icm.working = icm.working && p.icm.working == other.icm.working;
|
||||
@@ -578,6 +580,7 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten
|
||||
if (resize.height) toEdit.resize.height = mods.resize.height;
|
||||
if (resize.enabled) toEdit.resize.enabled = mods.resize.enabled;
|
||||
if (icm.input) toEdit.icm.input = mods.icm.input;
|
||||
if (icm.toneCurve) toEdit.icm.toneCurve = mods.icm.toneCurve;
|
||||
if (icm.blendCMSMatrix) toEdit.icm.blendCMSMatrix = mods.icm.blendCMSMatrix;
|
||||
if (icm.preferredProfile) toEdit.icm.preferredProfile = mods.icm.preferredProfile;
|
||||
if (icm.working) toEdit.icm.working = mods.icm.working;
|
||||
|
||||
@@ -313,6 +313,7 @@ class ColorManagementParamsEdited {
|
||||
|
||||
public:
|
||||
bool input;
|
||||
bool toneCurve;
|
||||
bool blendCMSMatrix;
|
||||
bool preferredProfile;
|
||||
bool working;
|
||||
|
||||
Reference in New Issue
Block a user