Issue 1987: improved DCP support; Issue 2042: batch queue crash fix

This commit is contained in:
torger
2013-11-19 08:37:42 +01:00
parent 047fd34e4e
commit 114f7e4531
21 changed files with 824 additions and 305 deletions

View File

@@ -283,7 +283,7 @@ HISTORY_MSG_45;Lum. Denoising Edge Tolerance
HISTORY_MSG_46;Color Denoising HISTORY_MSG_46;Color Denoising
HISTORY_MSG_47;Blend ICC highlights with matrix HISTORY_MSG_47;Blend ICC highlights with matrix
HISTORY_MSG_48;Use DCP's tone curve HISTORY_MSG_48;Use DCP's tone curve
HISTORY_MSG_49;Edge Sensitive Color Denoising HISTORY_MSG_49;DCP Illuminant
HISTORY_MSG_50;Shadow/Highlight HISTORY_MSG_50;Shadow/Highlight
HISTORY_MSG_51;Highlights HISTORY_MSG_51;Highlights
HISTORY_MSG_52;Shadows HISTORY_MSG_52;Shadows
@@ -1234,6 +1234,9 @@ TP_HSVEQUALIZER_SAT;S
TP_HSVEQUALIZER_VAL;V TP_HSVEQUALIZER_VAL;V
TP_ICM_BLENDCMSMATRIX;Blend ICC 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_BLENDCMSMATRIX_TOOLTIP;Enable to recover blown highlights when using LUT based ICC profiles
TP_ICM_DCPILLUMINANT;Illuminant
TP_ICM_DCPILLUMINANT_TOOLTIP;Select which embedded DCP illuminant to employ. Default is "interpolated" which is a mix between the two based on white balance. The setting is only enabled if a Dual-Illuminant DCP with interpolation support is selected.
TP_ICM_DCPILLUMINANT_INTERPOLATED;Interpolated
TP_ICM_FILEDLGFILTERANY;Any files TP_ICM_FILEDLGFILTERANY;Any files
TP_ICM_FILEDLGFILTERICM;Color profiles TP_ICM_FILEDLGFILTERICM;Color profiles
TP_ICM_INPUTCAMERAICC;Auto-matched camera-specific color profile TP_ICM_INPUTCAMERAICC;Auto-matched camera-specific color profile
@@ -1252,14 +1255,9 @@ TP_ICM_LABEL;Color Management
TP_ICM_NOICM;No ICM: sRGB Output TP_ICM_NOICM;No ICM: sRGB Output
TP_ICM_OUTPUTDLGLABEL;Select Output ICC Profile... TP_ICM_OUTPUTDLGLABEL;Select Output ICC Profile...
TP_ICM_OUTPUTPROFILE;Output Profile TP_ICM_OUTPUTPROFILE;Output Profile
TP_ICM_PREFERREDPROFILE;Preferred DCP profile
TP_ICM_PREFERREDPROFILE_1;Daylight
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_SAVEREFERENCE;Save Reference Image for Profiling
TP_ICM_TONECURVE;Use DCP's tone curve TP_ICM_TONECURVE;Use DCP's tone curve
TP_ICM_TONECURVE_TOOLTIP;Enable to use tone curves that may be contained in DCP profiles. TP_ICM_TONECURVE_TOOLTIP;Employ the embedded DCP tone curve. The setting is only enabled if the selected DCP has a tone curve.
TP_ICM_WORKINGPROFILE;Working Profile TP_ICM_WORKINGPROFILE;Working Profile
TP_IMPULSEDENOISE_LABEL;Impulse Noise Reduction TP_IMPULSEDENOISE_LABEL;Impulse Noise Reduction
TP_IMPULSEDENOISE_THRESH;Threshold TP_IMPULSEDENOISE_THRESH;Threshold

View File

@@ -62,6 +62,7 @@ class ColorTemp {
void update (const double rmul, const double gmul, const double bmul, const double equal) { this->equal = equal; mul2temp (rmul, gmul, bmul, this->equal, temp, green); } void update (const double rmul, const double gmul, const double bmul, const double equal) { this->equal = equal; mul2temp (rmul, gmul, bmul, this->equal, temp, green); }
void useDefaults (const double equal) { temp = 6504; green = 1.0; this->equal = equal; } // Values copied from procparams.cc void useDefaults (const double equal) { temp = 6504; green = 1.0; this->equal = equal; } // Values copied from procparams.cc
inline Glib::ustring getMethod() { return method; }
inline double getTemp () { return temp; } inline double getTemp () { return temp; }
inline double getGreen () { return green; } inline double getGreen () { return green; }
inline double getEqual () { return equal; } inline double getEqual () { return equal; }

View File

@@ -30,15 +30,319 @@ using namespace std;
using namespace rtengine; using namespace rtengine;
using namespace rtexif; using namespace rtexif;
static void Invert3x3(const double (*A)[3], double (*B)[3]) {
double a00 = A[0][0];
double a01 = A[0][1];
double a02 = A[0][2];
double a10 = A[1][0];
double a11 = A[1][1];
double a12 = A[1][2];
double a20 = A[2][0];
double a21 = A[2][1];
double a22 = A[2][2];
double temp [3][3];
temp[0][0] = a11 * a22 - a21 * a12;
temp[0][1] = a21 * a02 - a01 * a22;
temp[0][2] = a01 * a12 - a11 * a02;
temp[1][0] = a20 * a12 - a10 * a22;
temp[1][1] = a00 * a22 - a20 * a02;
temp[1][2] = a10 * a02 - a00 * a12;
temp[2][0] = a10 * a21 - a20 * a11;
temp[2][1] = a20 * a01 - a00 * a21;
temp[2][2] = a00 * a11 - a10 * a01;
double det = a00 * temp[0][0] + a01 * temp[1][0] + a02 * temp[2][0];
if (fabs(det) < 1.0E-10) {
abort(); // can't be inverted, we shouldn't be dealing with such matrices
}
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 3; k++) {
B[j][k] = temp[j][k] / det;
}
}
}
static void Multiply3x3(const double (*A)[3], const double (*B)[3], double (*C)[3]) {
// use temp to support having output same as input
double M[3][3];
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
M[i][j] = 0;
for (int k = 0; k < 3; k++) {
M[i][j] += A[i][k] * B[k][j];
}
}
}
memcpy(C, M, 3 * 3 * sizeof(double));
}
static void Multiply3x3_v3(const double (*A)[3], const double B[3], double C[3]) {
// use temp to support having output same as input
double M[3] = { 0, 0, 0 };
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
M[i] += A[i][j] * B[j];
}
}
memcpy(C, M, 3 * sizeof(double));
}
static void Mix3x3(const double (*A)[3], double mulA, const double (*B)[3], double mulB, double (*C)[3]) {
double M[3][3];
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
M[i][j] = A[i][j] * mulA + B[i][j] * mulB;
}
}
memcpy(C, M, 3 * 3 * sizeof(double));
}
static void MapWhiteMatrix(const double white1[3], const double white2[3], double (*B)[3]) {
// code adapted from dng_color_spec::MapWhiteMatrix
// Use the linearized Bradford adaptation matrix.
double Mb[3][3] = { { 0.8951, 0.2664, -0.1614 }, { -0.7502, 1.7135, 0.0367 }, { 0.0389, -0.0685, 1.0296 }};
double w1[3];
Multiply3x3_v3(Mb, white1, w1);
double w2[3];
Multiply3x3_v3(Mb, white2, w2);
// Negative white coordinates are kind of meaningless.
w1[0] = std::max(w1[0], 0.0);
w1[1] = std::max(w1[1], 0.0);
w1[2] = std::max(w1[2], 0.0);
w2[0] = std::max(w2[0], 0.0);
w2[1] = std::max(w2[1], 0.0);
w2[2] = std::max(w2[2], 0.0);
// Limit scaling to something reasonable.
double A[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
A[0][0] = std::max(0.1, std::min(w1[0] > 0.0 ? w2[0] / w1[0] : 10.0, 10.0));
A[1][1] = std::max(0.1, std::min(w1[1] > 0.0 ? w2[1] / w1[1] : 10.0, 10.0));
A[2][2] = std::max(0.1, std::min(w1[2] > 0.0 ? w2[2] / w1[2] : 10.0, 10.0));
double temp[3][3];
Invert3x3(Mb, temp);
Multiply3x3(temp, A, temp);
Multiply3x3(temp, Mb, B);
}
enum dngCalibrationIlluminant {
lsUnknown = 0,
lsDaylight = 1,
lsFluorescent = 2,
lsTungsten = 3,
lsFlash = 4,
lsFineWeather = 9,
lsCloudyWeather = 10,
lsShade = 11,
lsDaylightFluorescent = 12, // D 5700 - 7100K
lsDayWhiteFluorescent = 13, // N 4600 - 5500K
lsCoolWhiteFluorescent = 14, // W 3800 - 4500K
lsWhiteFluorescent = 15, // WW 3250 - 3800K
lsWarmWhiteFluorescent = 16, // L 2600 - 3250K
lsStandardLightA = 17,
lsStandardLightB = 18,
lsStandardLightC = 19,
lsD55 = 20,
lsD65 = 21,
lsD75 = 22,
lsD50 = 23,
lsISOStudioTungsten = 24,
lsOther = 255
};
// should probably be moved to colortemp.cc
static double calibrationIlluminantToTemperature(int light) {
// these temperatures are those found in DNG SDK reference code.
switch (light) {
case lsStandardLightA:
case lsTungsten:
return 2850.0;
case lsISOStudioTungsten:
return 3200.0;
case lsD50:
return 5000.0;
case lsD55:
case lsDaylight:
case lsFineWeather:
case lsFlash:
case lsStandardLightB:
return 5500.0;
case lsD65:
case lsStandardLightC:
case lsCloudyWeather:
return 6500.0;
case lsD75:
case lsShade:
return 7500.0;
case lsDaylightFluorescent:
return (5700.0 + 7100.0) * 0.5;
case lsDayWhiteFluorescent:
return (4600.0 + 5500.0) * 0.5;
case lsCoolWhiteFluorescent:
case lsFluorescent:
return (3800.0 + 4500.0) * 0.5;
case lsWhiteFluorescent:
return (3250.0 + 3800.0) * 0.5;
case lsWarmWhiteFluorescent:
return (2600.0 + 3250.0) * 0.5;
default:
return 0.0;
}
}
void DCPProfile::MakeXYZCAM(ColorTemp &wb, int preferredIlluminant, double (*mXYZCAM)[3]) const
{
// code adapted from dng_color_spec::FindXYZtoCamera
// note that we do not support monochrome or colorplanes > 3 (no reductionMatrix support)
// we do not support cameracalibration either
bool hasFwd1 = hasForwardMatrix1;
bool hasFwd2 = hasForwardMatrix2;
bool hasCol1 = hasColorMatrix1;
bool hasCol2 = hasColorMatrix2;
if (preferredIlluminant == 1) {
if (hasFwd1) hasFwd2 = false;
if (hasCol1) hasCol2 = false;
} else if (preferredIlluminant == 2) {
if (hasFwd2) hasFwd1 = false;
if (hasCol2) hasCol1 = false;
}
// mix if we have two matrices
double mix;
if (wb.getTemp() <= temperature1) {
mix = 1.0;
} else if (wb.getTemp() >= temperature2) {
mix = 0.0;
} else {
double invT = 1.0 / wb.getTemp();
mix = (invT - (1.0 / temperature2)) / ((1.0 / temperature1) - (1.0 / temperature2));
}
if (hasFwd1 || hasFwd2) {
// always prefer ForwardMatrix ahead of ColorMatrix
double mFwd[3][3];
if (hasFwd1 && hasFwd2) {
// interpolate
if (mix >= 1.0) {
memcpy(mFwd, mForwardMatrix1, sizeof(mFwd));
} else if (mix <= 0.0) {
memcpy(mFwd, mForwardMatrix2, sizeof(mFwd));
} else {
Mix3x3(mForwardMatrix1, mix, mForwardMatrix2, 1.0 - mix, mFwd);
}
} else if (hasFwd1) {
memcpy(mFwd, mForwardMatrix1, sizeof(mFwd));
} else {
memcpy(mFwd, mForwardMatrix2, sizeof(mFwd));
}
ConvertDNGForwardMatrix2XYZCAM(mFwd,mXYZCAM,wb);
} else {
// Colormatrix
double mCol[3][3];
if (hasCol1 && hasCol2) {
// interpolate
if (mix >= 1.0) {
memcpy(mCol, mColorMatrix1, sizeof(mCol));
} else if (mix <= 0.0) {
memcpy(mCol, mColorMatrix2, sizeof(mCol));
} else {
Mix3x3(mColorMatrix1, mix, mColorMatrix2, 1.0 - mix, mCol);
}
} else if (hasCol1) {
memcpy(mCol, mColorMatrix1, sizeof(mCol));
} else {
memcpy(mCol, mColorMatrix2, sizeof(mCol));
}
ConvertDNGMatrix2XYZCAM(mCol,mXYZCAM);
}
}
const DCPProfile::HSBModify* DCPProfile::MakeHueSatMap(ColorTemp &wb, int preferredIlluminant, HSBModify **deleteHandle) const {
*deleteHandle = NULL;
if (!aDeltas1) {
return NULL;
}
if (!aDeltas2) {
return aDeltas1;
}
if (preferredIlluminant == 1) {
return aDeltas1;
} else if (preferredIlluminant == 2) {
return aDeltas2;
}
// Interpolate based on color temperature.
if (temperature1 <= 0.0 || temperature2 <= 0.0 || temperature1 == temperature2) {
return aDeltas1;
}
bool reverseOrder = temperature1 > temperature2;
double t1, t2;
if (reverseOrder) {
t1 = temperature2;
t2 = temperature1;
} else {
t1 = temperature1;
t2 = temperature2;
}
double mix;
if (wb.getTemp() <= t1) {
mix = 1.0;
} else if (wb.getTemp() >= t2) {
mix = 0.0;
} else {
double invT = 1.0 / wb.getTemp();
mix = (invT - (1.0 / t2)) / ((1.0 / t1) - (1.0 / t2));
}
if (reverseOrder) {
mix = 1.0 - mix;
}
if (mix >= 1.0) {
return aDeltas1;
} else if (mix <= 0.0) {
return aDeltas2;
}
// Interpolate between the tables.
HSBModify *aDeltas = new HSBModify[DeltaInfo.iArrayCount];
*deleteHandle = aDeltas;
float w1 = (float)mix;
float w2 = 1.0f - (float)mix;
for (int i = 0; i < DeltaInfo.iArrayCount; i++) {
aDeltas[i].fHueShift = w1 * aDeltas1[i].fHueShift + w2 * aDeltas2[i].fHueShift;
aDeltas[i].fSatScale = w1 * aDeltas1[i].fSatScale + w2 * aDeltas2[i].fSatScale;
aDeltas[i].fValScale = w1 * aDeltas1[i].fValScale + w2 * aDeltas2[i].fValScale;
}
return aDeltas;
}
DCPProfile::DCPProfile(Glib::ustring fname, bool isRTProfile) { DCPProfile::DCPProfile(Glib::ustring fname, bool isRTProfile) {
const int TIFFFloatSize=4; const int TIFFFloatSize=4;
const int TagColorMatrix1=50721, TagColorMatrix2=50722, TagProfileHueSatMapDims=50937; const int TagColorMatrix1=50721, TagColorMatrix2=50722, TagProfileHueSatMapDims=50937;
const int TagForwardMatrix1=50964, TagForwardMatrix2=50965;
const int TagProfileHueSatMapData1=50938, TagProfileHueSatMapData2=50939; const int TagProfileHueSatMapData1=50938, TagProfileHueSatMapData2=50939;
const int TagCalibrationIlluminant1=50778, TagCalibrationIlluminant2=50779; const int TagCalibrationIlluminant1=50778, TagCalibrationIlluminant2=50779;
const int TagProfileLookTableData=50982, TagProfileLookTableDims=50981; // ProfileLookup is the low quality variant const int TagProfileLookTableData=50982, TagProfileLookTableDims=50981; // ProfileLookup is the low quality variant
const int TagProfileToneCurve=50940; const int TagProfileToneCurve=50940;
aDeltas1=aDeltas2=NULL; iHueDivisions=iSatDivisions=iValDivisions=iArrayCount=0; aDeltas1=aDeltas2=aLookTable=NULL;
FILE *pFile = safe_g_fopen(fname, "rb"); FILE *pFile = safe_g_fopen(fname, "rb");
@@ -46,47 +350,106 @@ DCPProfile::DCPProfile(Glib::ustring fname, bool isRTProfile) {
Tag* tag = tagDir->getTag(TagCalibrationIlluminant1); iLightSource1 = (tag!=NULL ? tag->toInt(0,rtexif::SHORT) : -1); Tag* tag = tagDir->getTag(TagCalibrationIlluminant1); iLightSource1 = (tag!=NULL ? tag->toInt(0,rtexif::SHORT) : -1);
tag = tagDir->getTag(TagCalibrationIlluminant2); iLightSource2 = (tag!=NULL ? tag->toInt(0,rtexif::SHORT) : -1); tag = tagDir->getTag(TagCalibrationIlluminant2); iLightSource2 = (tag!=NULL ? tag->toInt(0,rtexif::SHORT) : -1);
temperature1 = calibrationIlluminantToTemperature(iLightSource1);
temperature2 = calibrationIlluminantToTemperature(iLightSource2);
bool hasSecondHueSat = tagDir->getTag(TagProfileHueSatMapData2)!=NULL; // some profiles have two matrices, but just one huesat bool hasSecondHueSat = tagDir->getTag(TagProfileHueSatMapData2)!=NULL; // some profiles have two matrices, but just one huesat
// Fetch Forward Matrices, if any
hasForwardMatrix1 = false;
hasForwardMatrix2 = false;
hasColorMatrix1 = false;
hasColorMatrix2 = false;
hasToneCurve = false;
tag = tagDir->getTag(TagForwardMatrix1);
if (tag) {
hasForwardMatrix1 = true;
for (int row=0;row<3;row++) {
for (int col=0;col<3;col++) {
mForwardMatrix1[col][row]=(float)tag->toDouble((col+row*3)*8);
}
}
}
tag = tagDir->getTag(TagForwardMatrix2);
if (tag) {
hasForwardMatrix2 = true;
for (int row=0;row<3;row++) {
for (int col=0;col<3;col++) {
mForwardMatrix2[col][row]=(float)tag->toDouble((col+row*3)*8);
}
}
}
// Color Matrix (1 is always there) // Color Matrix (1 is always there)
tag = tagDir->getTag(TagColorMatrix1); tag = tagDir->getTag(TagColorMatrix1);
if (!tag) {
// FIXME: better error handling
fprintf(stderr, "Bad DCP, no ColorMatrix1\n");
abort();
}
hasColorMatrix1 = true;
for (int row=0;row<3;row++) { for (int row=0;row<3;row++) {
for (int col=0;col<3;col++) { for (int col=0;col<3;col++) {
mColorMatrix1[col][row]=(float)tag->toDouble((col+row*3)*8); mColorMatrix1[col][row]=(float)tag->toDouble((col+row*3)*8);
} }
} }
ConvertDNGMatrix2XYZCAM(mColorMatrix1,mXYZCAM1);
// LUT profile? Divisions counts
bool useSimpleLookup=false;
tag = tagDir->getTag(TagProfileHueSatMapDims);
if (tag==NULL) {
tag=tagDir->getTag(TagProfileLookTableDims); tag=tagDir->getTag(TagProfileLookTableDims);
useSimpleLookup=true; if (tag!=NULL) {
LookInfo.iHueDivisions=tag->toInt(0); LookInfo.iSatDivisions=tag->toInt(4); LookInfo.iValDivisions=tag->toInt(8);
tag = tagDir->getTag(TagProfileLookTableData);
LookInfo.iArrayCount = tag->getCount()/3;
aLookTable =new HSBModify[LookInfo.iArrayCount];
for (int i=0;i<LookInfo.iArrayCount;i++) {
aLookTable[i].fHueShift=tag->toDouble((i*3)*TIFFFloatSize);
aLookTable[i].fSatScale=tag->toDouble((i*3+1)*TIFFFloatSize);
aLookTable[i].fValScale=tag->toDouble((i*3+2)*TIFFFloatSize);
} }
// precalculated constants for table application
LookInfo.pc.hScale = (LookInfo.iHueDivisions < 2) ? 0.0f : (LookInfo.iHueDivisions * (1.0f / 6.0f));
LookInfo.pc.sScale = (float) (LookInfo.iSatDivisions - 1);
LookInfo.pc.vScale = (float) (LookInfo.iValDivisions - 1);
LookInfo.pc.maxHueIndex0 = LookInfo.iHueDivisions - 1;
LookInfo.pc.maxSatIndex0 = LookInfo.iSatDivisions - 2;
LookInfo.pc.maxValIndex0 = LookInfo.iValDivisions - 2;
LookInfo.pc.hueStep = LookInfo.iSatDivisions;
LookInfo.pc.valStep = LookInfo.iHueDivisions * LookInfo.pc.hueStep;
}
tag = tagDir->getTag(TagProfileHueSatMapDims);
if (tag!=NULL) { if (tag!=NULL) {
iHueDivisions=tag->toInt(0); iSatDivisions=tag->toInt(4); iValDivisions=tag->toInt(8); DeltaInfo.iHueDivisions=tag->toInt(0); DeltaInfo.iSatDivisions=tag->toInt(4); DeltaInfo.iValDivisions=tag->toInt(8);
// Saturation maps. Need to be unwinded. tag = tagDir->getTag(TagProfileHueSatMapData1);
tag = tagDir->getTag(useSimpleLookup ? TagProfileLookTableData : TagProfileHueSatMapData1); DeltaInfo.iArrayCount = tag->getCount()/3;
iArrayCount = tag->getCount()/3;
aDeltas1=new HSBModify[iArrayCount]; aDeltas1=new HSBModify[DeltaInfo.iArrayCount];
for (int i=0;i<iArrayCount;i++) { for (int i=0;i<DeltaInfo.iArrayCount;i++) {
aDeltas1[i].fHueShift=tag->toDouble((i*3)*TIFFFloatSize); aDeltas1[i].fHueShift=tag->toDouble((i*3)*TIFFFloatSize);
aDeltas1[i].fSatScale=tag->toDouble((i*3+1)*TIFFFloatSize); aDeltas1[i].fSatScale=tag->toDouble((i*3+1)*TIFFFloatSize);
aDeltas1[i].fValScale=tag->toDouble((i*3+2)*TIFFFloatSize); aDeltas1[i].fValScale=tag->toDouble((i*3+2)*TIFFFloatSize);
} }
DeltaInfo.pc.hScale = (DeltaInfo.iHueDivisions < 2) ? 0.0f : (DeltaInfo.iHueDivisions * (1.0f / 6.0f));
DeltaInfo.pc.sScale = (float) (DeltaInfo.iSatDivisions - 1);
DeltaInfo.pc.vScale = (float) (DeltaInfo.iValDivisions - 1);
DeltaInfo.pc.maxHueIndex0 = DeltaInfo.iHueDivisions - 1;
DeltaInfo.pc.maxSatIndex0 = DeltaInfo.iSatDivisions - 2;
DeltaInfo.pc.maxValIndex0 = DeltaInfo.iValDivisions - 2;
DeltaInfo.pc.hueStep = DeltaInfo.iSatDivisions;
DeltaInfo.pc.valStep = DeltaInfo.iHueDivisions * DeltaInfo.pc.hueStep;
} }
// For second profile, copy everything from first profile is no better data is available
if (iLightSource2!=-1) { if (iLightSource2!=-1) {
// Second matrix // Second matrix
tag = tagDir->getTag(TagColorMatrix2); tag = tagDir->getTag(TagColorMatrix2);
hasColorMatrix2 = true;
for (int row=0;row<3;row++) { for (int row=0;row<3;row++) {
for (int col=0;col<3;col++) { for (int col=0;col<3;col++) {
@@ -94,25 +457,18 @@ DCPProfile::DCPProfile(Glib::ustring fname, bool isRTProfile) {
} }
} }
ConvertDNGMatrix2XYZCAM(mColorMatrix2,mXYZCAM2); // Second huesatmap
// Second huesatmap, or copy of first
if (hasSecondHueSat) { if (hasSecondHueSat) {
aDeltas2=new HSBModify[iArrayCount]; aDeltas2=new HSBModify[DeltaInfo.iArrayCount];
// Saturation maps. Need to be unwinded. // Saturation maps. Need to be unwinded.
tag = tagDir->getTag(TagProfileHueSatMapData2); tag = tagDir->getTag(TagProfileHueSatMapData2);
for (int i=0;i<iArrayCount;i++) { for (int i=0;i<DeltaInfo.iArrayCount;i++) {
aDeltas2[i].fHueShift=tag->toDouble((i*3)*TIFFFloatSize); aDeltas2[i].fHueShift=tag->toDouble((i*3)*TIFFFloatSize);
aDeltas2[i].fSatScale=tag->toDouble((i*3+1)*TIFFFloatSize); aDeltas2[i].fSatScale=tag->toDouble((i*3+1)*TIFFFloatSize);
aDeltas2[i].fValScale=tag->toDouble((i*3+2)*TIFFFloatSize); aDeltas2[i].fValScale=tag->toDouble((i*3+2)*TIFFFloatSize);
} }
} else {
if (aDeltas1!=NULL) {
aDeltas2=new HSBModify[iArrayCount];
for (int i=0;i<iArrayCount;i++) aDeltas2[i]=aDeltas1[i];
}
} }
} }
@@ -124,12 +480,45 @@ DCPProfile::DCPProfile(Glib::ustring fname, bool isRTProfile) {
cPoints.push_back(double(DCT_Spline)); // The first value is the curve type cPoints.push_back(double(DCT_Spline)); // The first value is the curve type
// push back each X/Y coordinates in a loop // push back each X/Y coordinates in a loop
for (int i=0;i<tag->getCount();i++) cPoints.push_back( tag->toDouble(i*TIFFFloatSize) ); bool curve_is_linear = true;
for (int i=0;i<tag->getCount(); i+= 2) {
double x = tag->toDouble((i+0)*TIFFFloatSize);
double y = tag->toDouble((i+1)*TIFFFloatSize);
if (x != y) {
curve_is_linear = false;
}
cPoints.push_back( x );
cPoints.push_back( y );
}
if (!curve_is_linear) {
// Create the curve // Create the curve
DiagonalCurve rawCurve(cPoints, CURVES_MIN_POLY_POINTS); DiagonalCurve rawCurve(cPoints, CURVES_MIN_POLY_POINTS);
toneCurve.Set((Curve*)&rawCurve); toneCurve.Set((Curve*)&rawCurve);
hasToneCurve = true;
}
}
willInterpolate = false;
if (hasForwardMatrix1) {
if (hasForwardMatrix2) {
if (memcmp(mForwardMatrix1, mForwardMatrix2, sizeof(mForwardMatrix1)) != 0) {
// common that forward matrices are the same!
willInterpolate = true;
}
if (aDeltas1 && aDeltas2) {
// we assume tables are different
willInterpolate = true;
}
}
} else if (hasColorMatrix1 && hasColorMatrix2) {
if (memcmp(mColorMatrix1, mColorMatrix2, sizeof(mColorMatrix1)) != 0) {
willInterpolate = true;
}
if (aDeltas1 && aDeltas2) {
willInterpolate = true;
}
} }
if (pFile!=NULL) fclose(pFile); if (pFile!=NULL) fclose(pFile);
@@ -141,7 +530,7 @@ DCPProfile::~DCPProfile() {
} }
// 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]) { void DCPProfile::ConvertDNGMatrix2XYZCAM(const double (*mColorMatrix)[3], double (*mXYZCAM)[3]) const {
int i,j,k; int i,j,k;
double cam_xyz[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; double cam_xyz[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
@@ -177,146 +566,24 @@ void DCPProfile::ConvertDNGMatrix2XYZCAM(const double (*mColorMatrix)[3], double
mXYZCAM[i][j] += xyz_sRGB[i][k] * rgb_cam[k][j]; mXYZCAM[i][j] += xyz_sRGB[i][k] * rgb_cam[k][j];
} }
void DCPProfile::HSDApply(const HSDTableInfo &ti, const HSBModify *tableBase, const float hs, const float ss, const float vs, float &h, float &s, float &v) const {
const DCPProfile::HSBModify* DCPProfile::GetBestProfile(DCPLightType preferredProfile, double (*mXYZCAM)[3]) const {
bool use2=false;
if (iLightSource2!=-1) {
DCPLightType t1=GetLightType(iLightSource1); DCPLightType t2=GetLightType(iLightSource2);
// usually second is the daylight (default if nothing else found)
if (t2==Daylight) use2=true;
switch (preferredProfile) {
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 Flash:
if (t1==Flash) use2=false; else if (t2==Flash) use2=true;
break;
default: break; // e.g. Daylight
}
}
// printf("DCP using LightSource %i: %i for requested %i\n", use2?2:1, use2?iLightSource2:iLightSource1, (int)preferredProfile);
for (int row=0;row<3;row++) {
for (int col=0;col<3;col++) {
mXYZCAM[col][row]= (use2 ? mXYZCAM2[col][row] : mXYZCAM1[col][row]);
}
}
return use2?aDeltas2:aDeltas1;
}
DCPLightType DCPProfile::GetLightType(short iLightSource) const {
if (iLightSource==3 || iLightSource==17 || iLightSource==24) return Tungsten;
if (iLightSource==2 || (iLightSource>=12 && iLightSource<=15)) return Fluorescent;
if (iLightSource==4) return Flash;
return Daylight;
}
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);
bool hasLUT=(iArrayCount>0); useToneCurve&=toneCurve;
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++)
for (int k=0; k<3; k++)
mat[i][j] += mWork[i][k] * mXYZCAM[k][j];
// Apply the matrix part
#pragma omp parallel for
for (int y=0; y<pImg->height; y++) {
float newr, newg, newb;
for (int x=0; x<pImg->width; x++) {
newr = mat[0][0]*pImg->r(y,x) + mat[0][1]*pImg->g(y,x) + mat[0][2]*pImg->b(y,x);
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);
pImg->r(y,x) = newr; pImg->g(y,x) = newg; pImg->b(y,x) = newb;
}
}
}
else {
//===== LUT available- Calculate matrix for conversion raw>ProPhoto
double m2ProPhoto[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++)
for (int k=0; k<3; k++)
m2ProPhoto[i][j] += prophoto_xyz[i][k] * mXYZCAM[k][j];
double m2Work[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++)
for (int k=0; k<3; k++)
m2Work[i][j] += mWork[i][k] * xyz_prophoto[k][j];
// Preperations for LUT
float hScale = (iHueDivisions < 2) ? 0.0f : (iHueDivisions * (1.0f / 6.0f));
float sScale = (float) (iSatDivisions - 1);
float vScale = (float) (iValDivisions - 1);
int maxHueIndex0 = iHueDivisions - 1;
int maxSatIndex0 = iSatDivisions - 2;
int maxValIndex0 = iValDivisions - 2;
int hueStep = iSatDivisions;
int valStep = iHueDivisions * hueStep;
bool useRawWhite=fabs(rawWhiteFac)>0.001;
// Convert to prophoto and apply LUT
#pragma omp parallel for
for (int y=0; y<pImg->height; y++) {
float newr, newg, newb, h,s,v,hs,ss,vs;
for (int x=0; x<pImg->width; x++) {
newr = m2ProPhoto[0][0]*pImg->r(y,x) + m2ProPhoto[0][1]*pImg->g(y,x) + m2ProPhoto[0][2]*pImg->b(y,x);
newg = m2ProPhoto[1][0]*pImg->r(y,x) + m2ProPhoto[1][1]*pImg->g(y,x) + m2ProPhoto[1][2]*pImg->b(y,x);
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 (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 // Apply the HueSatMap. Ported from Adobes reference implementation
float hueShift, satScale, valScale; float hueShift, satScale, valScale;
if (iValDivisions < 2) // Optimize most common case of "2.5D" table. if (ti.iValDivisions < 2) // Optimize most common case of "2.5D" table.
{ {
float hScaled = hs * hScale; float hScaled = hs * ti.pc.hScale;
float sScaled = ss * sScale; float sScaled = ss * ti.pc.sScale;
int hIndex0 = max((int)hScaled, 0); int hIndex0 = max((int)hScaled, 0);
int sIndex0 = max(min((int)sScaled,maxSatIndex0),0); int sIndex0 = max(min((int)sScaled,ti.pc.maxSatIndex0),0);
int hIndex1 = hIndex0 + 1; int hIndex1 = hIndex0 + 1;
if (hIndex0 >= maxHueIndex0) if (hIndex0 >= ti.pc.maxHueIndex0)
{ {
hIndex0 = maxHueIndex0; hIndex0 = ti.pc.maxHueIndex0;
hIndex1 = 0; hIndex1 = 0;
} }
@@ -326,19 +593,12 @@ void DCPProfile::Apply(Imagefloat *pImg, DCPLightType preferredProfile, Glib::us
float hFract0 = 1.0f - hFract1; float hFract0 = 1.0f - hFract1;
float sFract0 = 1.0f - sFract1; float sFract0 = 1.0f - sFract1;
const HSBModify *entry00 = tableBase + hIndex0 * hueStep + const HSBModify *entry00 = tableBase + hIndex0 * ti.pc.hueStep + sIndex0;
sIndex0; const HSBModify *entry01 = entry00 + (hIndex1 - hIndex0) * ti.pc.hueStep;
const HSBModify *entry01 = entry00 + (hIndex1 - hIndex0) * hueStep; float hueShift0 = hFract0 * entry00->fHueShift + hFract1 * entry01->fHueShift;
float satScale0 = hFract0 * entry00->fSatScale + hFract1 * entry01->fSatScale;
float hueShift0 = hFract0 * entry00->fHueShift + float valScale0 = hFract0 * entry00->fValScale + hFract1 * entry01->fValScale;
hFract1 * entry01->fHueShift;
float satScale0 = hFract0 * entry00->fSatScale +
hFract1 * entry01->fSatScale;
float valScale0 = hFract0 * entry00->fValScale +
hFract1 * entry01->fValScale;
entry00++; entry00++;
entry01++; entry01++;
@@ -358,19 +618,19 @@ void DCPProfile::Apply(Imagefloat *pImg, DCPLightType preferredProfile, Glib::us
} else { } else {
float hScaled = hs * hScale; float hScaled = hs * ti.pc.hScale;
float sScaled = ss * sScale; float sScaled = ss * ti.pc.sScale;
float vScaled = vs * vScale; float vScaled = vs * ti.pc.vScale;
int hIndex0 = (int) hScaled; int hIndex0 = (int) hScaled;
int sIndex0 = max(min((int)sScaled,maxSatIndex0),0); int sIndex0 = max(min((int)sScaled,ti.pc.maxSatIndex0),0);
int vIndex0 = max(min((int)vScaled,maxValIndex0),0); int vIndex0 = max(min((int)vScaled,ti.pc.maxValIndex0),0);
int hIndex1 = hIndex0 + 1; int hIndex1 = hIndex0 + 1;
if (hIndex0 >= maxHueIndex0) if (hIndex0 >= ti.pc.maxHueIndex0)
{ {
hIndex0 = maxHueIndex0; hIndex0 = ti.pc.maxHueIndex0;
hIndex1 = 0; hIndex1 = 0;
} }
@@ -382,14 +642,12 @@ void DCPProfile::Apply(Imagefloat *pImg, DCPLightType preferredProfile, Glib::us
float sFract0 = 1.0f - sFract1; float sFract0 = 1.0f - sFract1;
float vFract0 = 1.0f - vFract1; float vFract0 = 1.0f - vFract1;
const HSBModify *entry00 = tableBase + vIndex0 * valStep + const HSBModify *entry00 = tableBase + vIndex0 * ti.pc.valStep + hIndex0 * ti.pc.hueStep + sIndex0;
hIndex0 * hueStep +
sIndex0;
const HSBModify *entry01 = entry00 + (hIndex1 - hIndex0) * hueStep; const HSBModify *entry01 = entry00 + (hIndex1 - hIndex0) * ti.pc.hueStep;
const HSBModify *entry10 = entry00 + valStep; const HSBModify *entry10 = entry00 + ti.pc.valStep;
const HSBModify *entry11 = entry01 + valStep; const HSBModify *entry11 = entry01 + ti.pc.valStep;
float hueShift0 = vFract0 * (hFract0 * entry00->fHueShift + float hueShift0 = vFract0 * (hFract0 * entry00->fHueShift +
hFract1 * entry01->fHueShift) + hFract1 * entry01->fHueShift) +
@@ -436,6 +694,117 @@ void DCPProfile::Apply(Imagefloat *pImg, DCPLightType preferredProfile, Glib::us
h += hueShift; h += hueShift;
s *= satScale; // no clipping here, we are RT float :-) s *= satScale; // no clipping here, we are RT float :-)
v *= valScale; v *= valScale;
}
// Convert DNG forward matrix to xyz_cam compatible matrix
void DCPProfile::ConvertDNGForwardMatrix2XYZCAM(const double (*mForwardMatrix)[3], double (*mXYZCAM)[3], ColorTemp &wb) const {
// Convert ForwardMatrix (white-balanced CameraRGB -> XYZ D50 matrix)
// into a ColorMatrix (XYZ -> CameraRGB)
double X, Z;
ColorTemp::temp2mulxyz(wb.getTemp(), wb.getGreen(), wb.getMethod(), X, Z);
const double white_xyz[3] = { X, 1, Z };
const double white_d50[3] = { 0.3457, 0.3585, 0.2958 }; // D50
// Cancel out the white balance to get a CameraRGB -> XYZ D50 matrixx (CameraToPCS in dng terminology)
double whiteDiag[3][3] = {{white_xyz[0], 0, 0}, {0, white_xyz[1], 0}, {0, 0, white_xyz[2]}};
double whiteDiagInv[3][3];
Invert3x3(whiteDiag, whiteDiagInv);
double rgb2xyzD50[3][3];
Multiply3x3(mForwardMatrix, whiteDiagInv, rgb2xyzD50);
// Through chromatic adaptation convert XYZ D50 to XYZ camera white
double whiteMatrix[3][3];
MapWhiteMatrix(white_d50, white_xyz, whiteMatrix);
double rgb2xyz[3][3];
Multiply3x3(rgb2xyzD50, whiteMatrix, rgb2xyz);
// Now we have CameraRGB -> XYZ, invert so we get XYZ -> CameraRGB (ColorMatrix format)
double dngColorMatrix[3][3];
Invert3x3(rgb2xyz, dngColorMatrix);
// now we can run the ordinary ColorMatrix conversion
ConvertDNGMatrix2XYZCAM(dngColorMatrix, mXYZCAM);
}
void DCPProfile::Apply(Imagefloat *pImg, int preferredIlluminant, Glib::ustring workingSpace, ColorTemp &wb, float rawWhiteFac, bool useToneCurve) const {
TMatrix mWork = iccStore->workingSpaceInverseMatrix (workingSpace);
double mXYZCAM[3][3];
MakeXYZCAM(wb, preferredIlluminant, mXYZCAM);
HSBModify *deleteTableHandle;
const HSBModify *deltaBase = MakeHueSatMap(wb, preferredIlluminant, &deleteTableHandle);
useToneCurve&=toneCurve;
if (deltaBase == NULL && aLookTable == NULL && !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++)
for (int k=0; k<3; k++)
mat[i][j] += mWork[i][k] * mXYZCAM[k][j];
// Apply the matrix part
#pragma omp parallel for
for (int y=0; y<pImg->height; y++) {
float newr, newg, newb;
for (int x=0; x<pImg->width; x++) {
newr = mat[0][0]*pImg->r(y,x) + mat[0][1]*pImg->g(y,x) + mat[0][2]*pImg->b(y,x);
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);
pImg->r(y,x) = newr; pImg->g(y,x) = newg; pImg->b(y,x) = newb;
}
}
}
else {
//===== LUT available- Calculate matrix for conversion raw>ProPhoto
double m2ProPhoto[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++)
for (int k=0; k<3; k++)
m2ProPhoto[i][j] += prophoto_xyz[i][k] * mXYZCAM[k][j];
double m2Work[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++)
for (int k=0; k<3; k++)
m2Work[i][j] += mWork[i][k] * xyz_prophoto[k][j];
bool useRawWhite=fabs(rawWhiteFac)>0.001;
// Convert to prophoto and apply LUT
#pragma omp parallel for
for (int y=0; y<pImg->height; y++) {
float newr, newg, newb, h,s,v,hs,ss,vs;
for (int x=0; x<pImg->width; x++) {
newr = m2ProPhoto[0][0]*pImg->r(y,x) + m2ProPhoto[0][1]*pImg->g(y,x) + m2ProPhoto[0][2]*pImg->b(y,x);
newg = m2ProPhoto[1][0]*pImg->r(y,x) + m2ProPhoto[1][1]*pImg->g(y,x) + m2ProPhoto[1][2]*pImg->b(y,x);
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 ((deltaBase || aLookTable) && 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;
}
if (deltaBase) {
HSDApply(DeltaInfo, deltaBase, hs, ss, vs, h, s, v);
}
if (aLookTable) {
HSDApply(LookInfo, aLookTable, hs, ss, vs, h, s, v);
}
// RT range correction // RT range correction
if (h < 0.0f) h += 6.0f; if (h < 0.0f) h += 6.0f;
@@ -453,14 +822,16 @@ void DCPProfile::Apply(Imagefloat *pImg, DCPLightType preferredProfile, Glib::us
} }
} }
} }
if (deleteTableHandle) delete deleteTableHandle;
} }
// Integer variant is legacy, only used for thumbs. Simply take the matrix here // Integer variant is legacy, only used for thumbs. Simply take the matrix here
void DCPProfile::Apply(Image16 *pImg, DCPLightType preferredProfile, Glib::ustring workingSpace, bool useToneCurve) const { void DCPProfile::Apply(Image16 *pImg, int preferredIlluminant, Glib::ustring workingSpace, ColorTemp &wb, bool useToneCurve) const {
TMatrix mWork = iccStore->workingSpaceInverseMatrix (workingSpace); TMatrix mWork = iccStore->workingSpaceInverseMatrix (workingSpace);
double mXYZCAM[3][3]; double mXYZCAM[3][3];
// unused //const HSBModify* tableBase=GetBestProfile(preferredProfile,mXYZCAM); MakeXYZCAM(wb, preferredIlluminant, mXYZCAM);
useToneCurve&=toneCurve; useToneCurve&=toneCurve;

View File

@@ -22,15 +22,13 @@
#include "imagefloat.h" #include "imagefloat.h"
#include "curves.h" #include "curves.h"
#include "colortemp.h"
#include "../rtgui/threadutils.h" #include "../rtgui/threadutils.h"
#include <glibmm.h> #include <glibmm.h>
#include <map> #include <map>
#include <string> #include <string>
namespace rtengine { namespace rtengine {
enum DCPLightType {
Daylight=1, Tungsten=2, Fluorescent=3, Flash=4
};
class DCPProfile { class DCPProfile {
struct HSBModify struct HSBModify
@@ -39,30 +37,42 @@ namespace rtengine {
float fSatScale; float fSatScale;
float fValScale; float fValScale;
}; };
struct HSDTableInfo
{
int iHueDivisions, iSatDivisions, iValDivisions;
int iHueStep, iValStep, iArrayCount;
struct
{
float hScale, sScale, vScale;
int maxHueIndex0, maxSatIndex0, maxValIndex0;
int hueStep, valStep;
} pc;
};
double mColorMatrix1[3][3],mColorMatrix2[3][3]; double mColorMatrix1[3][3],mColorMatrix2[3][3];
double mXYZCAM1[3][3],mXYZCAM2[3][3]; // compatible to RTs xyz_cam bool hasColorMatrix1, hasColorMatrix2, hasForwardMatrix1, hasForwardMatrix2, hasToneCurve, willInterpolate;
HSBModify *aDeltas1,*aDeltas2; double mForwardMatrix1[3][3],mForwardMatrix2[3][3];
double temperature1, temperature2;
HSBModify *aDeltas1,*aDeltas2,*aLookTable;
HSDTableInfo DeltaInfo,LookInfo;
short iLightSource1,iLightSource2; short iLightSource1,iLightSource2;
int iHueDivisions, iSatDivisions, iValDivisions;
int iHueStep, iValStep, iArrayCount;
AdobeToneCurve toneCurve; AdobeToneCurve toneCurve;
void ConvertDNGMatrix2XYZCAM(const double (*mColorMatrix)[3], double (*mXYZCAM)[3]); void MakeXYZCAM(ColorTemp &wb, int preferredIlluminant, double (*mXYZCAM)[3]) const;
const HSBModify* MakeHueSatMap(ColorTemp &wb, int preferredIlluminant, HSBModify **deleteHandle) const;
const HSBModify* GetBestProfile(DCPLightType preferredProfile, double (*mXYZCAM)[3]) const; void ConvertDNGMatrix2XYZCAM(const double (*mColorMatrix)[3], double (*mXYZCAM)[3]) const;
void ConvertDNGForwardMatrix2XYZCAM(const double (*mForwardMatrix)[3], double (*mXYZCAM)[3], ColorTemp &wb) const;
DCPLightType GetLightType(short iLightSource) const; void HSDApply(const HSDTableInfo &ti, const HSBModify *tableBase, const float hs, const float ss, const float vs, float &h, float &s, float &v) const;
public: public:
DCPProfile(Glib::ustring fname, bool isRTProfile); DCPProfile(Glib::ustring fname, bool isRTProfile);
~DCPProfile(); ~DCPProfile();
void Apply(Imagefloat *pImg, DCPLightType preferredProfile, Glib::ustring workingSpace, float rawWhiteFac=1, bool useToneCurve=false) const; bool getHasToneCurve() { return hasToneCurve; }
void Apply(Image16 *pImg, DCPLightType preferredProfile, Glib::ustring workingSpace, bool useToneCurve) const; void getIlluminants(int &i1, double &temp1, int &i2, double &temp2, bool &willInterpolate_) { i1 = iLightSource1; i2 = iLightSource2; temp1 = temperature1, temp2 = temperature2; willInterpolate_ = willInterpolate; };
void Apply(Imagefloat *pImg, int preferredIlluminant, Glib::ustring workingSpace, ColorTemp &wb, float rawWhiteFac=1, bool useToneCurve=false) const;
void Apply(Image16 *pImg, int preferredIlluminant, Glib::ustring workingSpace, ColorTemp &wb, bool useToneCurve) const;
}; };
class DCPStore { class DCPStore {

View File

@@ -70,7 +70,7 @@ enum ProcEvent {
EvCDNEnabled=45, // obsolete EvCDNEnabled=45, // obsolete
EvBlendCMSMatrix=46, EvBlendCMSMatrix=46,
EvDCPToneCurve=47, EvDCPToneCurve=47,
EvPrefProfile=48, EvDCPIlluminant=48,
EvSHEnabled=49, EvSHEnabled=49,
EvSHHighlights=50, EvSHHighlights=50,
EvSHShadows=51, EvSHShadows=51,

View File

@@ -408,7 +408,7 @@ void ProcParams::setDefaults () {
icm.input = "(cameraICC)"; icm.input = "(cameraICC)";
icm.blendCMSMatrix = false; icm.blendCMSMatrix = false;
icm.toneCurve = false; icm.toneCurve = false;
icm.preferredProfile = (short)rtengine::Daylight; icm.dcpIlluminant = 0;
icm.working = "sRGB"; icm.working = "sRGB";
icm.output = "sRGB"; icm.output = "sRGB";
icm.gamma = "default"; icm.gamma = "default";
@@ -966,7 +966,7 @@ int ProcParams::save (Glib::ustring fname, Glib::ustring fname2, bool fnameAbsol
if (!pedited || pedited->icm.input) keyFile.set_string ("Color Management", "InputProfile", relativePathIfInside(fname, fnameAbsolute, icm.input)); if (!pedited || pedited->icm.input) keyFile.set_string ("Color Management", "InputProfile", relativePathIfInside(fname, fnameAbsolute, icm.input));
if (!pedited || pedited->icm.toneCurve) keyFile.set_boolean ("Color Management", "ToneCurve", icm.toneCurve); 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.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.dcpIlluminant) keyFile.set_integer ("Color Management", "DCPIlluminant", icm.dcpIlluminant);
if (!pedited || pedited->icm.working) keyFile.set_string ("Color Management", "WorkingProfile", icm.working); if (!pedited || pedited->icm.working) keyFile.set_string ("Color Management", "WorkingProfile", icm.working);
if (!pedited || pedited->icm.output) keyFile.set_string ("Color Management", "OutputProfile", icm.output); if (!pedited || pedited->icm.output) keyFile.set_string ("Color Management", "OutputProfile", icm.output);
if (!pedited || pedited->icm.gamma) keyFile.set_string ("Color Management", "Gammafree", icm.gamma); if (!pedited || pedited->icm.gamma) keyFile.set_string ("Color Management", "Gammafree", icm.gamma);
@@ -1573,7 +1573,7 @@ if (keyFile.has_group ("Color Management")) {
if (keyFile.has_key ("Color Management", "InputProfile")) { icm.input = expandRelativePath(fname, "file:", keyFile.get_string ("Color Management", "InputProfile")); if (pedited) pedited->icm.input = true; } if (keyFile.has_key ("Color Management", "InputProfile")) { icm.input = expandRelativePath(fname, "file:", 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", "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", "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", "DCPIlluminant")) { icm.dcpIlluminant = keyFile.get_integer ("Color Management", "DCPIlluminant"); if (pedited) pedited->icm.dcpIlluminant = true; }
if (keyFile.has_key ("Color Management", "WorkingProfile")) { icm.working = keyFile.get_string ("Color Management", "WorkingProfile"); if (pedited) pedited->icm.working = true; } if (keyFile.has_key ("Color Management", "WorkingProfile")) { icm.working = keyFile.get_string ("Color Management", "WorkingProfile"); if (pedited) pedited->icm.working = true; }
if (keyFile.has_key ("Color Management", "OutputProfile")) { icm.output = keyFile.get_string ("Color Management", "OutputProfile"); if (pedited) pedited->icm.output = true; } if (keyFile.has_key ("Color Management", "OutputProfile")) { icm.output = keyFile.get_string ("Color Management", "OutputProfile"); if (pedited) pedited->icm.output = true; }
if (keyFile.has_key ("Color Management", "Gammafree")) { icm.gamma = keyFile.get_string ("Color Management", "Gammafree"); if (pedited) pedited->icm.gamfree = true; } if (keyFile.has_key ("Color Management", "Gammafree")) { icm.gamma = keyFile.get_string ("Color Management", "Gammafree"); if (pedited) pedited->icm.gamfree = true; }
@@ -1941,7 +1941,7 @@ bool ProcParams::operator== (const ProcParams& other) {
&& icm.input == other.icm.input && icm.input == other.icm.input
&& icm.toneCurve == other.icm.toneCurve && icm.toneCurve == other.icm.toneCurve
&& icm.blendCMSMatrix == other.icm.blendCMSMatrix && icm.blendCMSMatrix == other.icm.blendCMSMatrix
&& icm.preferredProfile == other.icm.preferredProfile && icm.dcpIlluminant == other.icm.dcpIlluminant
&& icm.working == other.icm.working && icm.working == other.icm.working
&& icm.output == other.icm.output && icm.output == other.icm.output
&& icm.gamma == other.icm.gamma && icm.gamma == other.icm.gamma

View File

@@ -706,7 +706,7 @@ class ColorManagementParams {
Glib::ustring input; Glib::ustring input;
bool toneCurve; bool toneCurve;
bool blendCMSMatrix; bool blendCMSMatrix;
short preferredProfile; int dcpIlluminant;
Glib::ustring working; Glib::ustring working;
Glib::ustring output; Glib::ustring output;
static const Glib::ustring NoICMString; static const Glib::ustring NoICMString;

View File

@@ -426,7 +426,7 @@ void RawImageSource::getImage (ColorTemp ctemp, int tran, Imagefloat* image, Pre
} }
void RawImageSource::convertColorSpace(Imagefloat* image, ColorManagementParams cmp, RAWParams raw) { void RawImageSource::convertColorSpace(Imagefloat* image, ColorManagementParams cmp, RAWParams raw) {
colorSpaceConversion (image, cmp, raw, embProfile, camProfile, imatrices.xyz_cam, (static_cast<const ImageData*>(getMetaData()))->getCamera()); colorSpaceConversion (image, cmp, wb, raw, embProfile, camProfile, imatrices.xyz_cam, (static_cast<const ImageData*>(getMetaData()))->getCamera());
} }
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1647,7 +1647,7 @@ void RawImageSource::getProfilePreprocParams(cmsHPROFILE in, float& gammaFac, fl
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Converts raw image including ICC input profile to working space - floating point version // Converts raw image including ICC input profile to working space - floating point version
void RawImageSource::colorSpaceConversion (Imagefloat* im, ColorManagementParams &cmp, float rawWhitePoint, cmsHPROFILE embedded, cmsHPROFILE camprofile, double camMatrix[3][3], const std::string &camName) { void RawImageSource::colorSpaceConversion (Imagefloat* im, ColorManagementParams &cmp, ColorTemp &wb, float rawWhitePoint, cmsHPROFILE embedded, cmsHPROFILE camprofile, double camMatrix[3][3], const std::string &camName) {
//MyTime t1, t2, t3; //MyTime t1, t2, t3;
//t1.set (); //t1.set ();
@@ -1657,7 +1657,7 @@ void RawImageSource::colorSpaceConversion (Imagefloat* im, ColorManagementParams
if (!findInputProfile(cmp.input, embedded, camName, &dcpProf, in)) return; if (!findInputProfile(cmp.input, embedded, camName, &dcpProf, in)) return;
if (dcpProf!=NULL) { if (dcpProf!=NULL) {
dcpProf->Apply(im, (DCPLightType)cmp.preferredProfile, cmp.working, rawWhitePoint, cmp.toneCurve); dcpProf->Apply(im, cmp.dcpIlluminant, cmp.working, wb, rawWhitePoint, cmp.toneCurve);
} else { } else {
// Calculate matrix for direct conversion raw>working space // Calculate matrix for direct conversion raw>working space
TMatrix work = iccStore->workingSpaceInverseMatrix (cmp.working); TMatrix work = iccStore->workingSpaceInverseMatrix (cmp.working);

View File

@@ -66,7 +66,7 @@ class RawImageSource : public ImageSource {
static LUTf invGrad; // for fast_demosaic static LUTf invGrad; // for fast_demosaic
static LUTf initInvGrad (); static LUTf initInvGrad ();
static bool findInputProfile(Glib::ustring inProfile, cmsHPROFILE embedded, std::string camName, DCPProfile **dcpProf, cmsHPROFILE& in); static bool findInputProfile(Glib::ustring inProfile, cmsHPROFILE embedded, std::string camName, DCPProfile **dcpProf, cmsHPROFILE& in);
static void colorSpaceConversion (Imagefloat* im, ColorManagementParams &cmp, float rawWhitePoint, cmsHPROFILE embedded, cmsHPROFILE camprofile, double cam[3][3], const std::string &camName); static void colorSpaceConversion (Imagefloat* im, ColorManagementParams &cmp, ColorTemp &wb, float rawWhitePoint, cmsHPROFILE embedded, cmsHPROFILE camprofile, double cam[3][3], const std::string &camName);
protected: protected:
MyMutex getImageMutex; // locks getImage MyMutex getImageMutex; // locks getImage
@@ -176,11 +176,11 @@ class RawImageSource : public ImageSource {
void convertColorSpace(Imagefloat* image, ColorManagementParams cmp, RAWParams raw); void convertColorSpace(Imagefloat* image, ColorManagementParams cmp, RAWParams raw);
//static void colorSpaceConversion16 (Image16* im, ColorManagementParams cmp, cmsHPROFILE embedded, cmsHPROFILE camprofile, double cam[3][3], std::string camName); //static void colorSpaceConversion16 (Image16* im, ColorManagementParams cmp, cmsHPROFILE embedded, cmsHPROFILE camprofile, double cam[3][3], std::string camName);
static void colorSpaceConversion (Imagefloat* im, ColorManagementParams cmp, cmsHPROFILE embedded, cmsHPROFILE camprofile, double cam[3][3], std::string camName) { static void colorSpaceConversion (Imagefloat* im, ColorManagementParams cmp, ColorTemp &wb, cmsHPROFILE embedded, cmsHPROFILE camprofile, double cam[3][3], std::string camName) {
colorSpaceConversion (im, cmp, 0.0f, embedded, camprofile, cam, camName); colorSpaceConversion (im, cmp, wb, 0.0f, embedded, camprofile, cam, camName);
} }
static void colorSpaceConversion (Imagefloat* im, ColorManagementParams cmp, RAWParams raw, cmsHPROFILE embedded, cmsHPROFILE camprofile, double cam[3][3], std::string camName) { static void colorSpaceConversion (Imagefloat* im, ColorManagementParams cmp, ColorTemp &wb, RAWParams raw, cmsHPROFILE embedded, cmsHPROFILE camprofile, double cam[3][3], std::string camName) {
colorSpaceConversion (im, cmp, float(raw.expos), embedded, camprofile, cam, camName); colorSpaceConversion (im, cmp, wb, float(raw.expos), embedded, camprofile, cam, camName);
} }
static void inverse33 (const double (*coeff)[3], double (*icoeff)[3]); static void inverse33 (const double (*coeff)[3], double (*icoeff)[3]);

View File

@@ -68,7 +68,7 @@ RGBCURVE, // EvToneCurveMode2,
0, // EvCDNEnabled:obsolete, 0, // EvCDNEnabled:obsolete,
ALL, // EvBlendCMSMatrix, ALL, // EvBlendCMSMatrix,
ALL, // EvDCPToneCurve, ALL, // EvDCPToneCurve,
ALL, // EvPrefProfile, ALL, // EvDCPIlluminant,
RETINEX, // EvSHEnabled, RETINEX, // EvSHEnabled,
RGBCURVE, // EvSHHighlights, RGBCURVE, // EvSHHighlights,
RGBCURVE, // EvSHShadows, RGBCURVE, // EvSHShadows,

View File

@@ -420,6 +420,7 @@ namespace rtengine {
* @param img is the result of the last ProcessingJob * @param img is the result of the last ProcessingJob
* @return the next ProcessingJob to process */ * @return the next ProcessingJob to process */
virtual ProcessingJob* imageReady (IImage16* img) =0; virtual ProcessingJob* imageReady (IImage16* img) =0;
virtual void error(Glib::ustring message) =0;
}; };
/** This function performs all the image processinf steps corresponding to the given ProcessingJob. It runs in the background, thus it returns immediately, /** This function performs all the image processinf steps corresponding to the given ProcessingJob. It runs in the background, thus it returns immediately,
* When it finishes, it calls the BatchProcessingListener with the resulting image and asks for the next job. It the listener gives a new job, it goes on * When it finishes, it calls the BatchProcessingListener with the resulting image and asks for the next job. It the listener gives a new job, it goes on

View File

@@ -679,7 +679,7 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, int rhei
// perform color space transformation // perform color space transformation
if (isRaw) if (isRaw)
RawImageSource::colorSpaceConversion (baseImg, params.icm, embProfile, camProfile, cam2xyz, camName ); RawImageSource::colorSpaceConversion (baseImg, params.icm, currWB, embProfile, camProfile, cam2xyz, camName );
else else
StdImageSource::colorSpaceConversion (baseImg, params.icm, embProfile, thumbImg->getSampleFormat()); StdImageSource::colorSpaceConversion (baseImg, params.icm, embProfile, thumbImg->getSampleFormat());

View File

@@ -681,9 +681,17 @@ void batchProcessingThread (ProcessingJob* job, BatchProcessingListener* bpl, bo
while (currentJob) { while (currentJob) {
int errorCode; int errorCode;
IImage16* img = processImage (currentJob, errorCode, bpl, tunnelMetaData); IImage16* img = processImage (currentJob, errorCode, bpl, tunnelMetaData);
if (errorCode) if (errorCode) {
bpl->error ("Can not load input image."); bpl->error ("Can not load input image.");
currentJob = NULL;
} else {
try {
currentJob = bpl->imageReady (img); currentJob = bpl->imageReady (img);
} catch (Glib::Exception& ex) {
bpl->error (ex.what());
currentJob = NULL;
}
}
} }
} }

View File

@@ -594,7 +594,7 @@ rtengine::ProcessingJob* BatchQueue::imageReady (rtengine::IImage16* img) {
err = img->saveAsJPEG (fname, saveFormat.jpegQuality, saveFormat.jpegSubSamp); err = img->saveAsJPEG (fname, saveFormat.jpegQuality, saveFormat.jpegSubSamp);
img->free (); img->free ();
if (err) throw "Unable to save output file"; if (err) throw Glib::FileError(Glib::FileError::FAILED, "Unable to save output file");
if (saveFormat.saveParams) { if (saveFormat.saveParams) {
// We keep the extension to avoid overwriting the profile when we have // We keep the extension to avoid overwriting the profile when we have
@@ -848,12 +848,14 @@ struct NLParams {
BatchQueueListener* listener; BatchQueueListener* listener;
int qsize; int qsize;
bool queueEmptied; bool queueEmptied;
bool queueError;
Glib::ustring queueErrorMessage;
}; };
int bqnotifylistenerUI (void* data) { int bqnotifylistenerUI (void* data) {
GThreadLock lock; // All GUI acces from idle_add callbacks or separate thread HAVE to be protected GThreadLock lock; // All GUI acces from idle_add callbacks or separate thread HAVE to be protected
NLParams* params = static_cast<NLParams*>(data); NLParams* params = static_cast<NLParams*>(data);
params->listener->queueSizeChanged (params->qsize, params->queueEmptied); params->listener->queueSizeChanged (params->qsize, params->queueEmptied, params->queueError, params->queueErrorMessage);
delete params; delete params;
return 0; return 0;
} }
@@ -871,6 +873,7 @@ void BatchQueue::notifyListener (bool queueEmptied) {
params->qsize = fd.size(); params->qsize = fd.size();
} }
params->queueEmptied = queueEmptied; params->queueEmptied = queueEmptied;
params->queueError = false;
g_idle_add (bqnotifylistenerUI, params); g_idle_add (bqnotifylistenerUI, params);
} }
} }
@@ -879,3 +882,15 @@ void BatchQueue::redrawNeeded (LWButton* button) {
GThreadLock lock; GThreadLock lock;
queue_draw (); queue_draw ();
} }
void BatchQueue::error (Glib::ustring msg) {
if (listener) {
NLParams* params = new NLParams;
params->listener = listener;
params->queueEmptied = false;
params->queueError = true;
params->queueErrorMessage = msg;
g_idle_add (bqnotifylistenerUI, params);
}
}

3
rtgui/batchqueue.h Normal file → Executable file
View File

@@ -30,7 +30,7 @@ class BatchQueueListener {
public: public:
virtual ~BatchQueueListener () {} virtual ~BatchQueueListener () {}
virtual void queueSizeChanged (int qsize, bool queueEmptied) =0; virtual void queueSizeChanged (int qsize, bool queueEmptied, bool queueError, Glib::ustring queueErrorMessage) =0;
virtual bool canStartNext () =0; virtual bool canStartNext () =0;
}; };
@@ -86,6 +86,7 @@ class BatchQueue : public ThumbBrowserBase,
} }
rtengine::ProcessingJob* imageReady (rtengine::IImage16* img); rtengine::ProcessingJob* imageReady (rtengine::IImage16* img);
void error (Glib::ustring msg);
void setProgress (double p); void setProgress (double p);
void rightClicked (ThumbBrowserEntryBase* entry); void rightClicked (ThumbBrowserEntryBase* entry);
bool keyPressed (GdkEventKey* event); bool keyPressed (GdkEventKey* event);

View File

@@ -223,17 +223,21 @@ void BatchQueuePanel::updateTab (int qsize)
} }
} }
void BatchQueuePanel::queueSizeChanged (int qsize, bool queueEmptied) void BatchQueuePanel::queueSizeChanged (int qsize, bool queueEmptied, bool queueError, Glib::ustring queueErrorMessage)
{ {
updateTab ( qsize); updateTab ( qsize);
if (queueEmptied) { if (queueEmptied || queueError) {
stopBatchProc (); stopBatchProc ();
fdir->set_sensitive (true); fdir->set_sensitive (true);
fformat->set_sensitive (true); fformat->set_sensitive (true);
SoundManager::playSoundAsync(options.sndBatchQueueDone); SoundManager::playSoundAsync(options.sndBatchQueueDone);
} }
if (queueError) {
Gtk::MessageDialog msgd (queueErrorMessage, true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
msgd.run ();
}
} }
void BatchQueuePanel::startBatchProc () { void BatchQueuePanel::startBatchProc () {

View File

@@ -59,7 +59,7 @@ class BatchQueuePanel : public Gtk::VBox,
void addBatchQueueJobs (std::vector<BatchQueueEntry*> &entries , bool head=false); void addBatchQueueJobs (std::vector<BatchQueueEntry*> &entries , bool head=false);
// batchqueuelistener interface // batchqueuelistener interface
void queueSizeChanged (int qsize, bool queueEmptied); void queueSizeChanged (int qsize, bool queueEmptied, bool queueError, Glib::ustring queueErrorMessage);
bool canStartNext (); bool canStartNext ();
void startBatchProc (); void startBatchProc ();

View File

@@ -82,16 +82,20 @@ ICMPanel::ICMPanel () : Gtk::VBox(), FoldableToolPanel(this), iunchanged(NULL),
Gtk::HBox* hb = Gtk::manage (new Gtk::HBox ()); Gtk::HBox* hb = Gtk::manage (new Gtk::HBox ());
hb->show (); hb->show ();
Gtk::Label* ppl = Gtk::manage (new Gtk::Label (M("TP_ICM_PREFERREDPROFILE")+":")); dcpIllLabel = Gtk::manage (new Gtk::Label ("DCP " + M("TP_ICM_DCPILLUMINANT")+":"));
ppl->show (); dcpIllLabel->set_tooltip_text (M("TP_ICM_DCPILLUMINANT_TOOLTIP"));
prefprof = Gtk::manage (new MyComboBoxText ()); dcpIllLabel->show ();
prefprof->append_text (M("TP_ICM_PREFERREDPROFILE_1")); dcpIll = Gtk::manage (new MyComboBoxText ());
prefprof->append_text (M("TP_ICM_PREFERREDPROFILE_2")); dcpIll->set_tooltip_text (M("TP_ICM_DCPILLUMINANT_TOOLTIP"));
prefprof->append_text (M("TP_ICM_PREFERREDPROFILE_3")); dcpIll->append_text (M("TP_ICM_DCPILLUMINANT_INTERPOLATED"));
prefprof->append_text (M("TP_ICM_PREFERREDPROFILE_4")); dcpIll->append_text (M("TP_ICM_DCPILLUMINANT") + " 1");
prefprof->show (); dcpIll->append_text (M("TP_ICM_DCPILLUMINANT") + " 2");
hb->pack_start(*ppl, Gtk::PACK_SHRINK, 4); dcpIll->show ();
hb->pack_start(*prefprof); dcpTemperatures[0] = 0;
dcpTemperatures[1] = 0;
ignoreDcpSignal = true;
hb->pack_start(*dcpIllLabel, Gtk::PACK_SHRINK, 4);
hb->pack_start(*dcpIll);
iVBox->pack_start (*hb, Gtk::PACK_SHRINK, 2); iVBox->pack_start (*hb, Gtk::PACK_SHRINK, 2);
ckbToneCurve = Gtk::manage (new Gtk::CheckButton (M("TP_ICM_TONECURVE"))); ckbToneCurve = Gtk::manage (new Gtk::CheckButton (M("TP_ICM_TONECURVE")));
@@ -229,7 +233,7 @@ ICMPanel::ICMPanel () : Gtk::VBox(), FoldableToolPanel(this), iunchanged(NULL),
wnames->signal_changed().connect( sigc::mem_fun(*this, &ICMPanel::wpChanged) ); wnames->signal_changed().connect( sigc::mem_fun(*this, &ICMPanel::wpChanged) );
onames->signal_changed().connect( sigc::mem_fun(*this, &ICMPanel::opChanged) ); onames->signal_changed().connect( sigc::mem_fun(*this, &ICMPanel::opChanged) );
wgamma->signal_changed().connect( sigc::mem_fun(*this, &ICMPanel::gpChanged) ); wgamma->signal_changed().connect( sigc::mem_fun(*this, &ICMPanel::gpChanged) );
prefprof->signal_changed().connect( sigc::mem_fun(*this, &ICMPanel::prefProfChanged) ); dcpIll->signal_changed().connect( sigc::mem_fun(*this, &ICMPanel::dcpIlluminantChanged) );
gamcsconn = freegamma->signal_toggled().connect ( sigc::mem_fun(*this, &ICMPanel::GamChanged)); gamcsconn = freegamma->signal_toggled().connect ( sigc::mem_fun(*this, &ICMPanel::GamChanged));
tcurveconn = ckbToneCurve->signal_toggled().connect ( sigc::mem_fun(*this, &ICMPanel::toneCurveChanged)); tcurveconn = ckbToneCurve->signal_toggled().connect ( sigc::mem_fun(*this, &ICMPanel::toneCurveChanged));
@@ -246,6 +250,100 @@ ICMPanel::ICMPanel () : Gtk::VBox(), FoldableToolPanel(this), iunchanged(NULL),
show_all (); show_all ();
} }
void ICMPanel::updateDCP (int dcpIlluminant, Glib::ustring dcp_name) {
if (isBatchMode) {
ckbToneCurve->set_sensitive (true);
dcpIllLabel->set_sensitive (true);
dcpIll->set_sensitive (true);
if (dcpTemperatures[0] != 0 || dcpTemperatures[1] != 0) {
int curr_active = dcpIll->get_active_row_number();
ignoreDcpSignal = true;
dcpIll->clear_items ();
dcpIll->append_text (M("TP_ICM_DCPILLUMINANT_INTERPOLATED"));
dcpIll->append_text (M("TP_ICM_DCPILLUMINANT") + " 1");
dcpIll->append_text (M("TP_ICM_DCPILLUMINANT") + " 2");
dcpIll->append_text (M("GENERAL_UNCHANGED"));
dcpTemperatures[0] = 0;
dcpTemperatures[1] = 0;
dcpIll->set_active (curr_active);
ignoreDcpSignal = false;
}
if (dcpIll->get_active_row_number() == -1 && dcpIlluminant == -1) {
dcpIll->set_active(0);
} else if (dcpIlluminant >= 0 && dcpIlluminant != dcpIll->get_active_row_number()) {
dcpIll->set_active(dcpIlluminant);
}
dcpIll->set_sensitive (true);
dcpIllLabel->set_sensitive (true);
return;
}
ckbToneCurve->set_sensitive (false);
dcpIllLabel->set_sensitive (false);
dcpIll->set_sensitive (false);
if (ifromfile->get_active() && dcpStore->isValidDCPFileName(dcp_name)) {
DCPProfile* dcp = dcpStore->getProfile(dcp_name, false);
if (dcp) {
if (dcp->getHasToneCurve()) {
ckbToneCurve->set_sensitive (true);
} else {
ckbToneCurve->set_active (false);
}
int i1, i2;
double temp1, temp2;
bool willInterpolate;
dcp->getIlluminants(i1, temp1, i2, temp2, willInterpolate);
if (willInterpolate) {
if (dcpTemperatures[0] != temp1 || dcpTemperatures[1] != temp2) {
char tempstr1[64], tempstr2[64];
sprintf(tempstr1, "%.0fK", temp1);
sprintf(tempstr2, "%.0fK", temp2);
int curr_active = dcpIll->get_active_row_number();
ignoreDcpSignal = true;
dcpIll->clear_items ();
dcpIll->append_text (M("TP_ICM_DCPILLUMINANT_INTERPOLATED"));
dcpIll->append_text (tempstr1);
dcpIll->append_text (tempstr2);
dcpTemperatures[0] = temp1;
dcpTemperatures[1] = temp2;
dcpIll->set_active (curr_active);
ignoreDcpSignal = false;
}
if (dcpIlluminant > 2) {
dcpIlluminant = 0;
}
if (dcpIll->get_active_row_number() == -1 && dcpIlluminant == -1) {
dcpIll->set_active(0);
} else if (dcpIlluminant >= 0 && dcpIlluminant != dcpIll->get_active_row_number()) {
dcpIll->set_active(dcpIlluminant);
}
dcpIll->set_sensitive (true);
dcpIllLabel->set_sensitive (true);
} else {
if (dcpIll->get_active_row_number() != -1) {
dcpIll->set_active(-1);
}
}
}
}
if (!dcpIllLabel->get_sensitive() && dcpIll->get_active_row_number() != 0) {
if (dcpTemperatures[0] != 0 || dcpTemperatures[1] != 0) {
int curr_active = dcpIll->get_active_row_number();
ignoreDcpSignal = true;
dcpIll->clear_items ();
dcpIll->append_text (M("TP_ICM_DCPILLUMINANT_INTERPOLATED"));
dcpIll->append_text (M("TP_ICM_DCPILLUMINANT") + " 1");
dcpIll->append_text (M("TP_ICM_DCPILLUMINANT") + " 2");
if (isBatchMode)
dcpIll->append_text (M("GENERAL_UNCHANGED"));
dcpTemperatures[0] = 0;
dcpTemperatures[1] = 0;
dcpIll->set_active (curr_active);
ignoreDcpSignal = false;
}
}
}
void ICMPanel::read (const ProcParams* pp, const ParamsEdited* pedited) { void ICMPanel::read (const ProcParams* pp, const ParamsEdited* pedited) {
disableListener (); disableListener ();
@@ -256,33 +354,38 @@ void ICMPanel::read (const ProcParams* pp, const ParamsEdited* pedited) {
blendcmsconn.block(true); blendcmsconn.block(true);
if (pp->icm.input == "(none)" && icamera->get_state()!=Gtk::STATE_INSENSITIVE) { if (pp->icm.input == "(none)" && icamera->get_state()!=Gtk::STATE_INSENSITIVE) {
inone->set_active (true); prefprof->set_sensitive (false); ckbToneCurve->set_sensitive (false); inone->set_active (true);
ckbBlendCMSMatrix->set_sensitive (false); ckbBlendCMSMatrix->set_sensitive (false);
updateDCP(pp->icm.dcpIlluminant, "");
} }
else if (pp->icm.input == "(embedded)" || ((pp->icm.input == "(camera)" || pp->icm.input=="") && icamera->get_state()==Gtk::STATE_INSENSITIVE)) { 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); ckbToneCurve->set_sensitive (false); iembedded->set_active (true);
ckbBlendCMSMatrix->set_sensitive (false); ckbBlendCMSMatrix->set_sensitive (false);
updateDCP(pp->icm.dcpIlluminant, "");
} }
else if ((pp->icm.input == "(cameraICC)") && icameraICC->get_state()!=Gtk::STATE_INSENSITIVE) { else if ((pp->icm.input == "(cameraICC)") && icameraICC->get_state()!=Gtk::STATE_INSENSITIVE) {
icameraICC->set_active (true); prefprof->set_sensitive (true); ckbToneCurve->set_sensitive (true); icameraICC->set_active (true);
ckbBlendCMSMatrix->set_sensitive (true); ckbBlendCMSMatrix->set_sensitive (true);
updateDCP(pp->icm.dcpIlluminant, "");
} }
else if ((pp->icm.input == "(cameraICC)") && icameraICC->get_state()==Gtk::STATE_INSENSITIVE) { 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 // 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 // therefore falling back UI to explicitly reflect the (camera) option
icamera->set_active (true); icamera->set_active (true);
prefprof->set_sensitive (false); ckbToneCurve->set_sensitive (false); // RT's own are always single-illuminant and tone curve disabled
ckbBlendCMSMatrix->set_sensitive (false); ckbBlendCMSMatrix->set_sensitive (false);
updateDCP(pp->icm.dcpIlluminant, "");
} }
else if ((pp->icm.input == "(camera)" || pp->icm.input=="") && icamera->get_state()!=Gtk::STATE_INSENSITIVE) { else if ((pp->icm.input == "(camera)" || pp->icm.input=="") && icamera->get_state()!=Gtk::STATE_INSENSITIVE) {
icamera->set_active (true); icamera->set_active (true);
ckbBlendCMSMatrix->set_sensitive (false); prefprof->set_sensitive (false); ckbToneCurve->set_sensitive (false); ckbBlendCMSMatrix->set_sensitive (false);
updateDCP(pp->icm.dcpIlluminant, "");
} }
else { else {
ifromfile->set_active (true); ifromfile->set_active (true);
oldip = pp->icm.input.substr(5); // cut of "file:" oldip = pp->icm.input.substr(5); // cut of "file:"
ipDialog->set_filename (pp->icm.input.substr(5)); ipDialog->set_filename (pp->icm.input.substr(5));
ckbBlendCMSMatrix->set_sensitive (true); prefprof->set_sensitive (true); ckbToneCurve->set_sensitive (true); ckbBlendCMSMatrix->set_sensitive (true);
updateDCP(pp->icm.dcpIlluminant, pp->icm.input.substr(5));
} }
wnames->set_active_text (pp->icm.working); wnames->set_active_text (pp->icm.working);
@@ -296,8 +399,6 @@ void ICMPanel::read (const ProcParams* pp, const ParamsEdited* pedited) {
if (onames->get_active_row_number()==-1) if (onames->get_active_row_number()==-1)
onames->set_active_text (M("TP_ICM_NOICM")); onames->set_active_text (M("TP_ICM_NOICM"));
prefprof->set_active(pp->icm.preferredProfile-1);
ckbToneCurve->set_active (pp->icm.toneCurve); ckbToneCurve->set_active (pp->icm.toneCurve);
lastToneCurve = pp->icm.toneCurve; lastToneCurve = pp->icm.toneCurve;
@@ -321,8 +422,8 @@ void ICMPanel::read (const ProcParams* pp, const ParamsEdited* pedited) {
wnames->set_active_text(M("GENERAL_UNCHANGED")); wnames->set_active_text(M("GENERAL_UNCHANGED"));
if (!pedited->icm.output) if (!pedited->icm.output)
onames->set_active_text(M("GENERAL_UNCHANGED")); onames->set_active_text(M("GENERAL_UNCHANGED"));
if (!pedited->icm.preferredProfile) if (!pedited->icm.dcpIlluminant)
prefprof->set_active_text(M("GENERAL_UNCHANGED")); dcpIll->set_active_text(M("GENERAL_UNCHANGED"));
if (!pedited->icm.gamma){ if (!pedited->icm.gamma){
wgamma->set_active_text(M("GENERAL_UNCHANGED")); wgamma->set_active_text(M("GENERAL_UNCHANGED"));
wgamma->set_active_text(M("GENERAL_UNCHANGED")); wgamma->set_active_text(M("GENERAL_UNCHANGED"));
@@ -361,7 +462,9 @@ void ICMPanel::write (ProcParams* pp, ParamsEdited* pedited) {
pp->icm.working = wnames->get_active_text (); pp->icm.working = wnames->get_active_text ();
pp->icm.gamma = wgamma->get_active_text (); pp->icm.gamma = wgamma->get_active_text ();
pp->icm.preferredProfile = prefprof->get_active_row_number()+1; pp->icm.dcpIlluminant = dcpIll->get_active_row_number();
if (pp->icm.dcpIlluminant < 0)
pp->icm.dcpIlluminant = 0;
if (onames->get_active_text()==M("TP_ICM_NOICM")) if (onames->get_active_text()==M("TP_ICM_NOICM"))
pp->icm.output = ColorManagementParams::NoICMString; pp->icm.output = ColorManagementParams::NoICMString;
@@ -377,7 +480,7 @@ void ICMPanel::write (ProcParams* pp, ParamsEdited* pedited) {
pedited->icm.input = !iunchanged->get_active (); pedited->icm.input = !iunchanged->get_active ();
pedited->icm.working = wnames->get_active_text()!=M("GENERAL_UNCHANGED"); pedited->icm.working = wnames->get_active_text()!=M("GENERAL_UNCHANGED");
pedited->icm.output = onames->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.dcpIlluminant = dcpIll->get_active_text()!=M("GENERAL_UNCHANGED");
pedited->icm.toneCurve = !ckbToneCurve->get_inconsistent (); pedited->icm.toneCurve = !ckbToneCurve->get_inconsistent ();
pedited->icm.blendCMSMatrix = !ckbBlendCMSMatrix->get_inconsistent (); pedited->icm.blendCMSMatrix = !ckbBlendCMSMatrix->get_inconsistent ();
pedited->icm.gamma = wgamma->get_active_text()!=M("GENERAL_UNCHANGED"); pedited->icm.gamma = wgamma->get_active_text()!=M("GENERAL_UNCHANGED");
@@ -433,9 +536,10 @@ void ICMPanel::gpChanged () {
} }
} }
void ICMPanel::prefProfChanged() { void ICMPanel::dcpIlluminantChanged() {
if (listener) if (listener && !ignoreDcpSignal) {
listener->panelChanged (EvPrefProfile, prefprof->get_active_text ()); listener->panelChanged (EvDCPIlluminant, dcpIll->get_active_text ());
}
} }
void ICMPanel::toneCurveChanged() { void ICMPanel::toneCurveChanged() {
@@ -458,27 +562,28 @@ void ICMPanel::toneCurveChanged() {
void ICMPanel::ipChanged () { void ICMPanel::ipChanged () {
std::string profname; Glib::ustring profname;
if (inone->get_active()) { if (inone->get_active()) {
profname = "(none)"; profname = "(none)";
ckbBlendCMSMatrix->set_sensitive(false); prefprof->set_sensitive (false); ckbToneCurve->set_sensitive (false); ckbBlendCMSMatrix->set_sensitive(false);
} }
else if (iembedded->get_active ()) { else if (iembedded->get_active ()) {
profname = "(embedded)"; profname = "(embedded)";
ckbBlendCMSMatrix->set_sensitive(false); prefprof->set_sensitive (false); ckbToneCurve->set_sensitive (false); ckbBlendCMSMatrix->set_sensitive(false);
} }
else if (icamera->get_active ()) { else if (icamera->get_active ()) {
profname = "(camera)"; profname = "(camera)";
ckbBlendCMSMatrix->set_sensitive(false); prefprof->set_sensitive (false); ckbToneCurve->set_sensitive (false); ckbBlendCMSMatrix->set_sensitive(false);
} }
else if (icameraICC->get_active ()) { else if (icameraICC->get_active ()) {
profname = "(cameraICC)"; profname = "(cameraICC)";
ckbBlendCMSMatrix->set_sensitive(true); prefprof->set_sensitive (false); ckbToneCurve->set_sensitive (false); ckbBlendCMSMatrix->set_sensitive(true);
} }
else { else {
profname = ipDialog->get_filename (); profname = ipDialog->get_filename ();
ckbBlendCMSMatrix->set_sensitive(true); prefprof->set_sensitive (true); ckbToneCurve->set_sensitive (true); ckbBlendCMSMatrix->set_sensitive(true);
} }
updateDCP(-1, profname);
if (listener && profname!=oldip) if (listener && profname!=oldip)
listener->panelChanged (EvIProfile, profname); listener->panelChanged (EvIProfile, profname);
@@ -604,6 +709,8 @@ void ICMPanel::saveReferencePressed () {
void ICMPanel::setBatchMode (bool batchMode) { void ICMPanel::setBatchMode (bool batchMode) {
isBatchMode = true;
ignoreDcpSignal = false;
ToolPanel::setBatchMode (batchMode); ToolPanel::setBatchMode (batchMode);
iunchanged = Gtk::manage (new Gtk::RadioButton (M("GENERAL_UNCHANGED"))); iunchanged = Gtk::manage (new Gtk::RadioButton (M("GENERAL_UNCHANGED")));
iunchanged->set_group (opts); iunchanged->set_group (opts);
@@ -613,9 +720,8 @@ void ICMPanel::setBatchMode (bool batchMode) {
onames->append_text (M("GENERAL_UNCHANGED")); onames->append_text (M("GENERAL_UNCHANGED"));
wnames->append_text (M("GENERAL_UNCHANGED")); wnames->append_text (M("GENERAL_UNCHANGED"));
wgamma->append_text (M("GENERAL_UNCHANGED")); wgamma->append_text (M("GENERAL_UNCHANGED"));
prefprof->append_text (M("GENERAL_UNCHANGED")); dcpIll->append_text (M("GENERAL_UNCHANGED"));
gampos->showEditedCB (); gampos->showEditedCB ();
slpos->showEditedCB (); slpos->showEditedCB ();
} }

10
rtgui/icmpanel.h Normal file → Executable file
View File

@@ -46,6 +46,7 @@ class ICMPanel : public Gtk::VBox, public AdjusterListener, public FoldableToolP
bool lastToneCurve; bool lastToneCurve;
sigc::connection tcurveconn; sigc::connection tcurveconn;
bool lastBlendCMSMatrix; bool lastBlendCMSMatrix;
bool isBatchMode;
sigc::connection blendcmsconn; sigc::connection blendcmsconn;
private: private:
@@ -58,7 +59,8 @@ class ICMPanel : public Gtk::VBox, public AdjusterListener, public FoldableToolP
Gtk::RadioButton* icamera; Gtk::RadioButton* icamera;
Gtk::RadioButton* icameraICC; Gtk::RadioButton* icameraICC;
Gtk::RadioButton* ifromfile; Gtk::RadioButton* ifromfile;
MyComboBoxText* prefprof; Gtk::Label* dcpIllLabel;
MyComboBoxText* dcpIll;
Gtk::CheckButton* ckbToneCurve; Gtk::CheckButton* ckbToneCurve;
Gtk::CheckButton* ckbBlendCMSMatrix; Gtk::CheckButton* ckbBlendCMSMatrix;
MyComboBoxText* wnames; MyComboBoxText* wnames;
@@ -76,9 +78,11 @@ class ICMPanel : public Gtk::VBox, public AdjusterListener, public FoldableToolP
Glib::ustring oldip; Glib::ustring oldip;
ICMPanelListener* icmplistener; ICMPanelListener* icmplistener;
bool ignoreDcpSignal;
double dcpTemperatures[2];
bool enableLastICCWorkDirChange; bool enableLastICCWorkDirChange;
Glib::ustring lastRefFilename; Glib::ustring lastRefFilename;
void updateDCP(int dcpIlluminant, Glib::ustring dcp_name);
public: public:
ICMPanel (); ICMPanel ();
@@ -96,7 +100,7 @@ class ICMPanel : public Gtk::VBox, public AdjusterListener, public FoldableToolP
void GamChanged (); void GamChanged ();
void ipSelectionChanged (); void ipSelectionChanged ();
void blendCMSMatrixChanged(); void blendCMSMatrixChanged();
void prefProfChanged(); void dcpIlluminantChanged();
void toneCurveChanged(); void toneCurveChanged();
void setRawMeta (bool raw, const rtengine::ImageData* pMeta); void setRawMeta (bool raw, const rtengine::ImageData* pMeta);

View File

@@ -251,7 +251,7 @@ void ParamsEdited::set (bool v) {
icm.input = v; icm.input = v;
icm.toneCurve = v; icm.toneCurve = v;
icm.blendCMSMatrix = v; icm.blendCMSMatrix = v;
icm.preferredProfile = v; icm.dcpIlluminant = v;
icm.working = v; icm.working = v;
icm.output = v; icm.output = v;
icm.gamma = v; icm.gamma = v;
@@ -530,7 +530,7 @@ void ParamsEdited::initFrom (const std::vector<rtengine::procparams::ProcParams>
icm.input = icm.input && p.icm.input == other.icm.input; icm.input = icm.input && p.icm.input == other.icm.input;
icm.toneCurve = icm.toneCurve && p.icm.toneCurve == other.icm.toneCurve; icm.toneCurve = icm.toneCurve && p.icm.toneCurve == other.icm.toneCurve;
icm.blendCMSMatrix = icm.blendCMSMatrix && p.icm.blendCMSMatrix == other.icm.blendCMSMatrix; icm.blendCMSMatrix = icm.blendCMSMatrix && p.icm.blendCMSMatrix == other.icm.blendCMSMatrix;
icm.preferredProfile = icm.preferredProfile && p.icm.preferredProfile == other.icm.preferredProfile; icm.dcpIlluminant = icm.dcpIlluminant && p.icm.dcpIlluminant == other.icm.dcpIlluminant;
icm.working = icm.working && p.icm.working == other.icm.working; icm.working = icm.working && p.icm.working == other.icm.working;
icm.output = icm.output && p.icm.output == other.icm.output; icm.output = icm.output && p.icm.output == other.icm.output;
icm.gamma = icm.gamma && p.icm.gamma == other.icm.gamma; icm.gamma = icm.gamma && p.icm.gamma == other.icm.gamma;
@@ -825,7 +825,7 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten
if (icm.input) toEdit.icm.input = mods.icm.input; if (icm.input) toEdit.icm.input = mods.icm.input;
if (icm.toneCurve) toEdit.icm.toneCurve = mods.icm.toneCurve; if (icm.toneCurve) toEdit.icm.toneCurve = mods.icm.toneCurve;
if (icm.blendCMSMatrix) toEdit.icm.blendCMSMatrix = mods.icm.blendCMSMatrix; if (icm.blendCMSMatrix) toEdit.icm.blendCMSMatrix = mods.icm.blendCMSMatrix;
if (icm.preferredProfile) toEdit.icm.preferredProfile = mods.icm.preferredProfile; if (icm.dcpIlluminant) toEdit.icm.dcpIlluminant = mods.icm.dcpIlluminant;
if (icm.working) toEdit.icm.working = mods.icm.working; if (icm.working) toEdit.icm.working = mods.icm.working;
if (icm.output) toEdit.icm.output = mods.icm.output; if (icm.output) toEdit.icm.output = mods.icm.output;
//if (icm.gampos) toEdit.icm.gampos = mods.icm.gampos; //if (icm.gampos) toEdit.icm.gampos = mods.icm.gampos;

View File

@@ -412,7 +412,7 @@ class ColorManagementParamsEdited {
bool input; bool input;
bool toneCurve; bool toneCurve;
bool blendCMSMatrix; bool blendCMSMatrix;
bool preferredProfile; bool dcpIlluminant;
bool working; bool working;
bool output; bool output;
bool gamma; bool gamma;