Moved the ICC TRC patching code for RTv2 profiles from ImProcFunctions::lab2rgbOut to ICCStore
See #5026
This commit is contained in:
parent
23aa0562aa
commit
cfc947a865
@ -38,6 +38,8 @@
|
||||
#include "../rtgui/threadutils.h"
|
||||
#include "lcms2_plugin.h"
|
||||
|
||||
#include "color.h"
|
||||
|
||||
#include "cJSON.h"
|
||||
#define inkc_constant 0x696E6B43
|
||||
namespace rtengine
|
||||
@ -208,8 +210,85 @@ const char* wpnames[] = {"sRGB", "Adobe RGB", "ProPhoto", "WideGamut", "BruceRGB
|
||||
// high g=1.3 s=3.35 for high dynamic images
|
||||
//low g=2.6 s=6.9 for low contrast images
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// helper functions to fix V2 profiles TRCs, used in
|
||||
// rtengine::ProfileContent::toProfile()
|
||||
// see https://github.com/Beep6581/RawTherapee/issues/5026
|
||||
// -----------------------------------------------------------------------------
|
||||
bool is_RTv2_profile(cmsHPROFILE profile)
|
||||
{
|
||||
if (int(cmsGetProfileVersion(profile)) != 2) {
|
||||
return false;
|
||||
}
|
||||
const cmsMLU *mlu = static_cast<const cmsMLU *>(cmsReadTag(profile, cmsSigDeviceMfgDescTag));
|
||||
if (!mlu) {
|
||||
return false;
|
||||
}
|
||||
cmsUInt32Number sz = cmsMLUgetASCII(mlu, "en", "US", nullptr, 0);
|
||||
if (!sz) {
|
||||
return false;
|
||||
}
|
||||
std::vector<char> buf(sz);
|
||||
cmsMLUgetASCII(mlu, "en", "US", &buf[0], sz);
|
||||
buf.back() = 0; // sanity
|
||||
return strcmp(&buf[0], "RawTherapee") == 0;
|
||||
}
|
||||
|
||||
|
||||
bool get_RT_gamma_slope(cmsHPROFILE profile, double &gammatag, double &slopetag)
|
||||
{
|
||||
const cmsMLU *modelDescMLU = static_cast<const cmsMLU *>(cmsReadTag(profile, cmsSigDeviceModelDescTag));
|
||||
if (modelDescMLU) {
|
||||
cmsUInt32Number count = cmsMLUgetWide(modelDescMLU, "en", "US", nullptr, 0);
|
||||
if (count) {
|
||||
std::vector<wchar_t> vbuf(count);
|
||||
wchar_t *buffer = &vbuf[0];
|
||||
count = cmsMLUgetWide(modelDescMLU, "en", "US", buffer, count);
|
||||
Glib::ustring modelDesc;
|
||||
#if __SIZEOF_WCHAR_T__ == 2
|
||||
char *cModelDesc = g_utf16_to_utf8((unsigned short int*)buffer, -1, nullptr, nullptr, nullptr); // convert to utf-8 in a buffer allocated by glib
|
||||
if (cModelDesc) {
|
||||
modelDesc.assign(cModelDesc);
|
||||
g_free(cModelDesc);
|
||||
}
|
||||
#else
|
||||
modelDesc = utf32_to_utf8(buffer, count);
|
||||
#endif
|
||||
if (!modelDesc.empty()) {
|
||||
std::size_t pos = modelDesc.find("g");
|
||||
std::size_t posmid = modelDesc.find("s");
|
||||
std::size_t posend = modelDesc.find("!");
|
||||
std::string strgamma = modelDesc.substr(pos + 1, (posmid - pos));
|
||||
gammatag = std::stod(strgamma.c_str());
|
||||
std::string strslope = modelDesc.substr(posmid + 1, (posend - posmid));
|
||||
slopetag = std::stod(strslope.c_str());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Glib::ustring get_profile_description(cmsHPROFILE profile)
|
||||
{
|
||||
const cmsMLU *mlu = static_cast<const cmsMLU *>(cmsReadTag(profile, cmsSigProfileDescriptionTag));
|
||||
if (!mlu) {
|
||||
return "";
|
||||
}
|
||||
cmsUInt32Number sz = cmsMLUgetASCII(mlu, "en", "US", nullptr, 0);
|
||||
if (!sz) {
|
||||
return "";
|
||||
}
|
||||
std::vector<char> buf(sz);
|
||||
cmsMLUgetASCII(mlu, "en", "US", &buf[0], sz);
|
||||
buf.back() = 0; // sanity
|
||||
return std::string(&buf[0]);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
rtengine::ProfileContent::ProfileContent() = default;
|
||||
|
||||
rtengine::ProfileContent::ProfileContent(const Glib::ustring& fileName)
|
||||
@ -255,11 +334,52 @@ rtengine::ProfileContent::ProfileContent(cmsHPROFILE hProfile)
|
||||
|
||||
cmsHPROFILE rtengine::ProfileContent::toProfile() const
|
||||
{
|
||||
cmsHPROFILE profile = nullptr;
|
||||
if (!data.empty()) {
|
||||
profile = cmsOpenProfileFromMem(data.c_str(), data.size());
|
||||
// if this is a V2 profile generated by RawTherapee, we rebuild the
|
||||
// TRC. See https://github.com/Beep6581/RawTherapee/issues/5026 and
|
||||
// the references in there
|
||||
if (profile && is_RTv2_profile(profile)) {
|
||||
double gammatag, slopetag;
|
||||
if (get_RT_gamma_slope(profile, gammatag, slopetag)) {
|
||||
constexpr double eps = 0.000000001; // not divide by zero
|
||||
double pwr = 1.0 / gammatag;
|
||||
double ts = slopetag;
|
||||
double slope = slopetag == 0 ? eps : slopetag;
|
||||
|
||||
return
|
||||
!data.empty()
|
||||
? cmsOpenProfileFromMem(data.c_str(), data.size())
|
||||
: nullptr;
|
||||
GammaValues g_b; //gamma parameters
|
||||
Color::calcGamma(pwr, ts, 0, g_b); // call to calcGamma with selected gamma and slope : return parameters for LCMS2
|
||||
cmsFloat64Number gammaParams[7]; //gamma parameters
|
||||
gammaParams[4] = g_b[3] * ts;
|
||||
gammaParams[0] = gammatag;
|
||||
gammaParams[1] = 1. / (1.0 + g_b[4]);
|
||||
gammaParams[2] = g_b[4] / (1.0 + g_b[4]);
|
||||
gammaParams[3] = 1. / slope;
|
||||
gammaParams[5] = 0.0;
|
||||
gammaParams[6] = 0.0;
|
||||
|
||||
cmsToneCurve* GammaTRC;
|
||||
if (slopetag == 0.) {
|
||||
//printf("gammatag=%f\n", gammatag);
|
||||
GammaTRC = cmsBuildGamma(NULL, gammatag);
|
||||
} else {
|
||||
GammaTRC = cmsBuildParametricToneCurve(nullptr, 5, gammaParams); //5 = smoother than 4
|
||||
}
|
||||
cmsWriteTag(profile, cmsSigRedTRCTag, GammaTRC);
|
||||
cmsWriteTag(profile, cmsSigGreenTRCTag, GammaTRC);
|
||||
cmsWriteTag(profile, cmsSigBlueTRCTag, GammaTRC);
|
||||
cmsFreeToneCurve(GammaTRC);
|
||||
|
||||
if (settings->verbose) {
|
||||
std::cout << "ICCStore: rebuilt TRC for RTv2 profile " << get_profile_description(profile) << ": gamma=" << gammatag << ", slope=" << slopetag << std::endl;
|
||||
}
|
||||
} else if (settings->verbose) {
|
||||
std::cout << "ICCStore: no gamma/slope info found for RTv2 profile " << get_profile_description(profile) << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
return profile;
|
||||
}
|
||||
|
||||
const std::string& rtengine::ProfileContent::getData() const
|
||||
|
@ -303,82 +303,7 @@ Imagefloat* ImProcFunctions::lab2rgbOut(LabImage* lab, int cx, int cy, int cw, i
|
||||
}
|
||||
|
||||
Imagefloat* image = new Imagefloat(cw, ch);
|
||||
|
||||
cmsHPROFILE oprof = nullptr;
|
||||
|
||||
oprof = ICCStore::getInstance()->getProfile(icm.outputProfile);
|
||||
Glib::ustring outtest = icm.outputProfile;
|
||||
std::string fileis_RTv2 = outtest.substr(0, 4);
|
||||
//printf("IsRTv2=%s\n", fileis_RTv2.c_str());
|
||||
if(fileis_RTv2 == "RTv2") {//Only fot ICC v2 : read tag from desc to retrieve gamma and slope save before in generate ICC v2
|
||||
//due to bug in LCMS in CmsToneCurve
|
||||
//printf("icmout=%s \n",icm.output.c_str());
|
||||
GammaValues g_b; //gamma parameters
|
||||
const double eps = 0.000000001; // not divide by zero
|
||||
double gammatag = 2.4;
|
||||
double slopetag = 12.92310;
|
||||
cmsMLU *modelDescMLU = (cmsMLU*) (cmsReadTag(oprof, cmsSigDeviceModelDescTag));
|
||||
if (modelDescMLU) {
|
||||
cmsUInt32Number count = cmsMLUgetWide(modelDescMLU, "en", "US", nullptr, 0); // get buffer length first
|
||||
if (count) {
|
||||
wchar_t *buffer = new wchar_t[count];
|
||||
count = cmsMLUgetWide(modelDescMLU, "en", "US", buffer, count); // now put the string in the buffer
|
||||
Glib::ustring modelDesc;
|
||||
#if __SIZEOF_WCHAR_T__ == 2
|
||||
char* cModelDesc = g_utf16_to_utf8((unsigned short int*)buffer, -1, nullptr, nullptr, nullptr); // convert to utf-8 in a buffer allocated by glib
|
||||
if (cModelDesc) {
|
||||
modelDesc.assign(cModelDesc);
|
||||
g_free(cModelDesc);
|
||||
}
|
||||
#else
|
||||
modelDesc = utf32_to_utf8(buffer, count);
|
||||
#endif
|
||||
delete [] buffer;
|
||||
if (!modelDesc.empty()) {
|
||||
std::size_t pos = modelDesc.find("g");
|
||||
std::size_t posmid = modelDesc.find("s");
|
||||
std::size_t posend = modelDesc.find("!");
|
||||
std::string strgamma = modelDesc.substr(pos + 1, (posmid - pos));
|
||||
gammatag = std::stod(strgamma.c_str());
|
||||
std::string strslope = modelDesc.substr(posmid + 1, (posend - posmid));
|
||||
slopetag = std::stod(strslope.c_str());
|
||||
// printf("gam=%f slo=%f\n", gammatag, slopetag);
|
||||
}
|
||||
} else {
|
||||
printf("Error: lab2rgbOut / String length is null!\n");
|
||||
}
|
||||
} else {
|
||||
printf("Error: lab2rgbOut / cmsReadTag/cmsSigDeviceModelDescTag failed!\n");
|
||||
}
|
||||
|
||||
double pwr = 1.0 / gammatag;
|
||||
double ts = slopetag;
|
||||
double slope = slopetag == 0 ? eps : slopetag;
|
||||
|
||||
int mode = 0;
|
||||
Color::calcGamma(pwr, ts, mode, g_b); // call to calcGamma with selected gamma and slope : return parameters for LCMS2
|
||||
cmsFloat64Number gammaParams[7]; //gamma parameters
|
||||
gammaParams[4] = g_b[3] * ts;
|
||||
gammaParams[0] = gammatag;
|
||||
gammaParams[1] = 1. / (1.0 + g_b[4]);
|
||||
gammaParams[2] = g_b[4] / (1.0 + g_b[4]);
|
||||
gammaParams[3] = 1. / slope;
|
||||
gammaParams[5] = 0.0;
|
||||
gammaParams[6] = 0.0;
|
||||
|
||||
cmsToneCurve* GammaTRC[3];
|
||||
if(slopetag == 0.) {
|
||||
//printf("gammatag=%f\n", gammatag);
|
||||
GammaTRC[0] = GammaTRC[1] = GammaTRC[2] = cmsBuildGamma(NULL, gammatag);
|
||||
}
|
||||
else {
|
||||
GammaTRC[0] = GammaTRC[1] = GammaTRC[2] = cmsBuildParametricToneCurve(nullptr, 5, gammaParams); //5 = smoother than 4
|
||||
}
|
||||
cmsWriteTag(oprof, cmsSigRedTRCTag, GammaTRC[0]);
|
||||
cmsWriteTag(oprof, cmsSigGreenTRCTag, GammaTRC[1]);
|
||||
cmsWriteTag(oprof, cmsSigBlueTRCTag, GammaTRC[2]);
|
||||
cmsFreeToneCurve(GammaTRC[0]);
|
||||
}
|
||||
cmsHPROFILE oprof = ICCStore::getInstance()->getProfile(icm.outputProfile);
|
||||
|
||||
if (oprof) {
|
||||
cmsUInt32Number flags = cmsFLAGS_NOOPTIMIZE | cmsFLAGS_NOCACHE;
|
||||
|
Loading…
x
Reference in New Issue
Block a user