added possibility to specify extra working spaces via a json file
The JSON file is called workingspaces.json, it can be either in the global iccprofiles directory, or in the user's ICC profiles dir (set in preferences). The format is the following: {"working_spaces": [ { "name" : "ACES", "file" : "/path/to/ACES.icc" }, { "name" : "ACEScg", "matrix" : [0.7184354, 0.16578523, 0.09882643, 0.29728935, 0.66958117, 0.03571544, -0.00647622, 0.01469771, 0.66732561] } ]} if "matrix" is present, "file" is ignored. If only "file" is present, the matrix is extracted from the ICC profile. For this, we look only at the R, G, and B matrix columns and the white point set in the profile. Bradford adaptation is used to convert the matrix to D50. Anything else (LUT, TRC, ...) in the profile is ignored. It is the user's responsibility to ensure that the profile is suitable to be used as a working space.
This commit is contained in:
parent
46a556fbaa
commit
b09bf381b4
@ -286,7 +286,7 @@ void rtengine::HaldCLUT::splitClutFilename(
|
||||
profile_name = "sRGB";
|
||||
|
||||
if (!name.empty()) {
|
||||
for (const auto& working_profile : rtengine::ICCStore::getWorkingProfiles()) {
|
||||
for (const auto& working_profile : rtengine::ICCStore::getInstance()->getWorkingProfiles()) {
|
||||
if (
|
||||
!working_profile.empty() // This isn't strictly needed, but an empty wp name should be skipped anyway
|
||||
&& std::search(name.rbegin(), name.rend(), working_profile.rbegin(), working_profile.rend()) == name.rbegin()
|
||||
|
@ -37,30 +37,36 @@ namespace
|
||||
|
||||
DCPProfile::Matrix invert3x3(const DCPProfile::Matrix& a)
|
||||
{
|
||||
const double res00 = a[1][1] * a[2][2] - a[2][1] * a[1][2];
|
||||
const double res10 = a[2][0] * a[1][2] - a[1][0] * a[2][2];
|
||||
const double res20 = a[1][0] * a[2][1] - a[2][0] * a[1][1];
|
||||
|
||||
const double det = a[0][0] * res00 + a[0][1] * res10 + a[0][2] * res20;
|
||||
|
||||
if (std::fabs(det) < 1.0e-10) {
|
||||
DCPProfile::Matrix res = a;
|
||||
if (!invertMatrix(a, res)) {
|
||||
std::cerr << "DCP matrix cannot be inverted! Expect weird output." << std::endl;
|
||||
return a;
|
||||
}
|
||||
|
||||
DCPProfile::Matrix res;
|
||||
|
||||
res[0][0] = res00 / det;
|
||||
res[0][1] = (a[2][1] * a[0][2] - a[0][1] * a[2][2]) / det;
|
||||
res[0][2] = (a[0][1] * a[1][2] - a[1][1] * a[0][2]) / det;
|
||||
res[1][0] = res10 / det;
|
||||
res[1][1] = (a[0][0] * a[2][2] - a[2][0] * a[0][2]) / det;
|
||||
res[1][2] = (a[1][0] * a[0][2] - a[0][0] * a[1][2]) / det;
|
||||
res[2][0] = res20 / det;
|
||||
res[2][1] = (a[2][0] * a[0][1] - a[0][0] * a[2][1]) / det;
|
||||
res[2][2] = (a[0][0] * a[1][1] - a[1][0] * a[0][1]) / det;
|
||||
|
||||
return res;
|
||||
|
||||
// const double res00 = a[1][1] * a[2][2] - a[2][1] * a[1][2];
|
||||
// const double res10 = a[2][0] * a[1][2] - a[1][0] * a[2][2];
|
||||
// const double res20 = a[1][0] * a[2][1] - a[2][0] * a[1][1];
|
||||
|
||||
// const double det = a[0][0] * res00 + a[0][1] * res10 + a[0][2] * res20;
|
||||
|
||||
// if (std::fabs(det) < 1.0e-10) {
|
||||
// std::cerr << "DCP matrix cannot be inverted! Expect weird output." << std::endl;
|
||||
// return a;
|
||||
// }
|
||||
|
||||
// DCPProfile::Matrix res;
|
||||
|
||||
// res[0][0] = res00 / det;
|
||||
// res[0][1] = (a[2][1] * a[0][2] - a[0][1] * a[2][2]) / det;
|
||||
// res[0][2] = (a[0][1] * a[1][2] - a[1][1] * a[0][2]) / det;
|
||||
// res[1][0] = res10 / det;
|
||||
// res[1][1] = (a[0][0] * a[2][2] - a[2][0] * a[0][2]) / det;
|
||||
// res[1][2] = (a[1][0] * a[0][2] - a[0][0] * a[1][2]) / det;
|
||||
// res[2][0] = res20 / det;
|
||||
// res[2][1] = (a[2][0] * a[0][1] - a[0][0] * a[2][1]) / det;
|
||||
// res[2][2] = (a[0][0] * a[1][1] - a[1][0] * a[0][1]) / det;
|
||||
|
||||
// return res;
|
||||
}
|
||||
|
||||
DCPProfile::Matrix multiply3x3(const DCPProfile::Matrix& a, const DCPProfile::Matrix& b)
|
||||
|
@ -27,6 +27,8 @@
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "iccstore.h"
|
||||
|
||||
#include "iccmatrices.h"
|
||||
@ -35,6 +37,8 @@
|
||||
#include "../rtgui/options.h"
|
||||
#include "../rtgui/threadutils.h"
|
||||
|
||||
#include "cJSON.h"
|
||||
|
||||
namespace rtengine
|
||||
{
|
||||
|
||||
@ -274,7 +278,7 @@ public:
|
||||
|
||||
for (int i = 0; i < N; ++i) {
|
||||
wProfiles[wpnames[i]] = createFromMatrix(wprofiles[i]);
|
||||
wProfilesGamma[wpnames[i]] = createFromMatrix(wprofiles[i], true);
|
||||
// wProfilesGamma[wpnames[i]] = createFromMatrix(wprofiles[i], true);
|
||||
wMatrices[wpnames[i]] = wprofiles[i];
|
||||
iwMatrices[wpnames[i]] = iwprofiles[i];
|
||||
}
|
||||
@ -287,11 +291,11 @@ public:
|
||||
cmsCloseProfile(p.second);
|
||||
}
|
||||
}
|
||||
for (auto &p : wProfilesGamma) {
|
||||
if (p.second) {
|
||||
cmsCloseProfile(p.second);
|
||||
}
|
||||
}
|
||||
// for (auto &p : wProfilesGamma) {
|
||||
// if (p.second) {
|
||||
// cmsCloseProfile(p.second);
|
||||
// }
|
||||
// }
|
||||
for (auto &p : fileProfiles) {
|
||||
if(p.second) {
|
||||
cmsCloseProfile(p.second);
|
||||
@ -334,6 +338,9 @@ public:
|
||||
|
||||
defaultMonitorProfile = settings->monitorProfile;
|
||||
|
||||
loadWorkingSpaces(rtICCDir);
|
||||
loadWorkingSpaces(userICCDir);
|
||||
|
||||
// initialize the alarm colours for lcms gamut checking -- we use bright green
|
||||
cmsUInt16Number cms_alarm_codes[cmsMAXCHANNELS] = { 0, 65535, 65535 };
|
||||
cmsSetAlarmCodes(cms_alarm_codes);
|
||||
@ -349,16 +356,16 @@ public:
|
||||
: wProfiles.find("sRGB")->second;
|
||||
}
|
||||
|
||||
cmsHPROFILE workingSpaceGamma(const Glib::ustring& name) const
|
||||
{
|
||||
// cmsHPROFILE workingSpaceGamma(const Glib::ustring& name) const
|
||||
// {
|
||||
|
||||
const ProfileMap::const_iterator r = wProfilesGamma.find(name);
|
||||
// const ProfileMap::const_iterator r = wProfilesGamma.find(name);
|
||||
|
||||
return
|
||||
r != wProfilesGamma.end()
|
||||
? r->second
|
||||
: wProfilesGamma.find("sRGB")->second;
|
||||
}
|
||||
// return
|
||||
// r != wProfilesGamma.end()
|
||||
// ? r->second
|
||||
// : wProfilesGamma.find("sRGB")->second;
|
||||
// }
|
||||
|
||||
TMatrix workingSpaceMatrix(const Glib::ustring& name) const
|
||||
{
|
||||
@ -582,17 +589,276 @@ public:
|
||||
defaultMonitorProfile = name;
|
||||
}
|
||||
|
||||
std::vector<Glib::ustring> getWorkingProfiles()
|
||||
{
|
||||
std::vector<Glib::ustring> res;
|
||||
|
||||
// for (unsigned int i = 0; i < sizeof(wpnames) / sizeof(wpnames[0]); i++) {
|
||||
// res.push_back(wpnames[i]);
|
||||
// }
|
||||
for (const auto &p : wProfiles) {
|
||||
res.push_back(p.first);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
using CVector = std::array<double, 3>;
|
||||
using CMatrix = std::array<CVector, 3>;
|
||||
struct PMatrix {
|
||||
double matrix[3][3];
|
||||
PMatrix(): matrix{} {}
|
||||
PMatrix(const CMatrix &m)
|
||||
{
|
||||
set(m);
|
||||
}
|
||||
|
||||
CMatrix toMatrix() const
|
||||
{
|
||||
CMatrix ret;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
ret[i][j] = matrix[i][j];
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void set(const CMatrix &m)
|
||||
{
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
matrix[i][j] = m[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bool computeWorkingSpaceMatrix(const Glib::ustring &path, const Glib::ustring &filename, PMatrix &out)
|
||||
{
|
||||
Glib::ustring fullpath = filename;
|
||||
if (!Glib::path_is_absolute(fullpath)) {
|
||||
fullpath = Glib::build_filename(path, filename);
|
||||
}
|
||||
ProfileContent content(fullpath);
|
||||
cmsHPROFILE prof = content.toProfile();
|
||||
if (!prof) {
|
||||
return false;
|
||||
}
|
||||
if (cmsGetColorSpace(prof) != cmsSigRgbData) {
|
||||
cmsCloseProfile(prof);
|
||||
return false;
|
||||
}
|
||||
if (!cmsIsMatrixShaper(prof)) {
|
||||
cmsCloseProfile(prof);
|
||||
return false;
|
||||
}
|
||||
cmsCIEXYZ *white = static_cast<cmsCIEXYZ *>(cmsReadTag(prof, cmsSigMediaWhitePointTag));
|
||||
cmsCIEXYZ *red = static_cast<cmsCIEXYZ *>(cmsReadTag(prof, cmsSigRedMatrixColumnTag));
|
||||
cmsCIEXYZ *green = static_cast<cmsCIEXYZ *>(cmsReadTag(prof, cmsSigGreenMatrixColumnTag));
|
||||
cmsCIEXYZ *blue = static_cast<cmsCIEXYZ *>(cmsReadTag(prof, cmsSigBlueMatrixColumnTag));
|
||||
|
||||
if (!white || !red || !green || !blue) {
|
||||
cmsCloseProfile(prof);
|
||||
return false;
|
||||
}
|
||||
|
||||
// do the Bradford adaptation to D50
|
||||
// matrices from Bruce Lindbloom's webpage
|
||||
static constexpr CMatrix bradford_MA = {
|
||||
CVector({0.8951000, 0.2664000, -0.1614000}),
|
||||
CVector({-0.7502000, 1.7135000, 0.0367000}),
|
||||
CVector({0.0389000, -0.0685000, 1.0296000})
|
||||
};
|
||||
static constexpr CMatrix bradford_MA_inv = {
|
||||
CVector({0.9869929, -0.1470543, 0.1599627}),
|
||||
CVector({0.4323053, 0.5183603, 0.0492912}),
|
||||
CVector({-0.0085287, 0.0400428, 0.9684867})
|
||||
};
|
||||
static constexpr CVector bradford_MA_dot_D50 = {
|
||||
0.99628443, 1.02042736, 0.81864437
|
||||
};
|
||||
|
||||
CVector srcw = dotProduct(bradford_MA, CVector({ white->X, white->Y, white->Z }));
|
||||
CMatrix m = {
|
||||
CVector({ bradford_MA_dot_D50[0]/srcw[0], 0.0, 0.0 }),
|
||||
CVector({ 0.0, bradford_MA_dot_D50[1]/srcw[1], 0.0 }),
|
||||
CVector({ 0.0, 0.0, bradford_MA_dot_D50[2]/srcw[2] })
|
||||
};
|
||||
CMatrix adapt = dotProduct(dotProduct(bradford_MA_inv, m), bradford_MA);
|
||||
|
||||
m[0][0] = red->X; m[0][1] = green->X; m[0][2] = blue->X;
|
||||
m[1][0] = red->Y; m[1][1] = green->Y; m[1][2] = blue->Y;
|
||||
m[2][0] = red->Z; m[2][1] = green->Z; m[2][2] = blue->Z;
|
||||
|
||||
m = dotProduct(adapt, m);
|
||||
out.set(m);
|
||||
|
||||
cmsCloseProfile(prof);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool loadWorkingSpaces(const Glib::ustring &path)
|
||||
{
|
||||
Glib::ustring fileName = Glib::build_filename(path, "workingspaces.json");
|
||||
FILE* const f = g_fopen(fileName.c_str(), "r");
|
||||
|
||||
if (settings->verbose) {
|
||||
std::cout << "trying to load extra working spaces from " << fileName << std::flush;
|
||||
}
|
||||
|
||||
if (!f) {
|
||||
if (settings->verbose) {
|
||||
std::cout << " FAIL" << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
long length = ftell(f);
|
||||
if (length <= 0) {
|
||||
if (settings->verbose) {
|
||||
std::cout << " FAIL" << std::endl;
|
||||
}
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
char *buf = new char[length + 1];
|
||||
fseek(f, 0, SEEK_SET);
|
||||
length = fread(buf, 1, length, f);
|
||||
buf[length] = 0;
|
||||
|
||||
fclose(f);
|
||||
|
||||
cJSON_Minify(buf);
|
||||
cJSON *root = cJSON_Parse(buf);
|
||||
|
||||
if (!root) {
|
||||
if (settings->verbose) {
|
||||
std::cout << " FAIL" << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
delete[] buf;
|
||||
|
||||
cJSON *js = cJSON_GetObjectItem(root, "working_spaces");
|
||||
if (!js) {
|
||||
goto parse_error;
|
||||
}
|
||||
|
||||
for (js = js->child; js != nullptr; js = js->next) {
|
||||
cJSON *ji = cJSON_GetObjectItem(js, "name");
|
||||
std::unique_ptr<PMatrix> m(new PMatrix);
|
||||
std::string name;
|
||||
|
||||
if (!ji || ji->type != cJSON_String) {
|
||||
goto parse_error;
|
||||
}
|
||||
|
||||
name = ji->valuestring;
|
||||
|
||||
if (wProfiles.find(name) != wProfiles.end()) {
|
||||
continue; // already there -- ignore
|
||||
}
|
||||
|
||||
bool found_matrix = false;
|
||||
|
||||
ji = cJSON_GetObjectItem(js, "matrix");
|
||||
if (ji) {
|
||||
if (ji->type != cJSON_Array) {
|
||||
goto parse_error;
|
||||
}
|
||||
|
||||
ji = ji->child;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
for (int j = 0; j < 3; ++j, ji = ji->next) {
|
||||
if (!ji || ji->type != cJSON_Number) {
|
||||
goto parse_error;
|
||||
}
|
||||
m->matrix[i][j] = ji->valuedouble;
|
||||
}
|
||||
}
|
||||
|
||||
if (ji) {
|
||||
goto parse_error;
|
||||
}
|
||||
found_matrix = true;
|
||||
} else {
|
||||
ji = cJSON_GetObjectItem(js, "file");
|
||||
if (!ji || ji->type != cJSON_String) {
|
||||
goto parse_error;
|
||||
}
|
||||
found_matrix = computeWorkingSpaceMatrix(path, ji->valuestring, *m);
|
||||
}
|
||||
|
||||
if (!found_matrix) {
|
||||
if (settings->verbose) {
|
||||
std::cout << "Could not find suitable matrix for working space: " << name << std::endl;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
pMatrices.emplace_back(std::move(m));
|
||||
TMatrix w = pMatrices.back()->matrix;
|
||||
|
||||
CMatrix b = {};
|
||||
if (!rtengine::invertMatrix(pMatrices.back()->toMatrix(), b)) {
|
||||
if (settings->verbose) {
|
||||
std::cout << "Matrix for working space: " << name << " is not invertible, skipping" << std::endl;
|
||||
}
|
||||
pMatrices.pop_back();
|
||||
} else {
|
||||
wMatrices[name] = w;
|
||||
pMatrices.emplace_back(new PMatrix(b));
|
||||
TMatrix iw = pMatrices.back()->matrix;
|
||||
iwMatrices[name] = iw;
|
||||
wProfiles[name] = createFromMatrix(w);
|
||||
|
||||
if (settings->verbose) {
|
||||
std::cout << "Added working space: " << name << std::endl;
|
||||
std::cout << " matrix: [";
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
std::cout << " [";
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
std::cout << " " << w[i][j];
|
||||
}
|
||||
std::cout << "]";
|
||||
}
|
||||
std::cout << " ]" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cJSON_Delete(root);
|
||||
if (settings->verbose) {
|
||||
std::cout << " OK" << std::endl;
|
||||
}
|
||||
return true;
|
||||
|
||||
parse_error:
|
||||
if (settings->verbose) {
|
||||
std::cout << " ERROR in parsing " << fileName << std::endl;
|
||||
}
|
||||
|
||||
cJSON_Delete(root);
|
||||
return false;
|
||||
}
|
||||
|
||||
using ProfileMap = std::map<Glib::ustring, cmsHPROFILE>;
|
||||
using MatrixMap = std::map<Glib::ustring, TMatrix>;
|
||||
using ContentMap = std::map<Glib::ustring, ProfileContent>;
|
||||
using NameMap = std::map<Glib::ustring, Glib::ustring>;
|
||||
|
||||
ProfileMap wProfiles;
|
||||
ProfileMap wProfilesGamma;
|
||||
// ProfileMap wProfilesGamma;
|
||||
MatrixMap wMatrices;
|
||||
MatrixMap iwMatrices;
|
||||
|
||||
std::vector<std::unique_ptr<PMatrix>> pMatrices;
|
||||
|
||||
// These contain profiles from user/system directory(supplied on init)
|
||||
Glib::ustring profilesDir;
|
||||
Glib::ustring userICCDir;
|
||||
@ -630,10 +896,10 @@ cmsHPROFILE rtengine::ICCStore::workingSpace(const Glib::ustring& name) const
|
||||
return implementation->workingSpace(name);
|
||||
}
|
||||
|
||||
cmsHPROFILE rtengine::ICCStore::workingSpaceGamma(const Glib::ustring& name) const
|
||||
{
|
||||
return implementation->workingSpaceGamma(name);
|
||||
}
|
||||
// cmsHPROFILE rtengine::ICCStore::workingSpaceGamma(const Glib::ustring& name) const
|
||||
// {
|
||||
// return implementation->workingSpaceGamma(name);
|
||||
// }
|
||||
|
||||
rtengine::TMatrix rtengine::ICCStore::workingSpaceMatrix(const Glib::ustring& name) const
|
||||
{
|
||||
@ -736,14 +1002,7 @@ rtengine::ICCStore::~ICCStore() = default;
|
||||
|
||||
std::vector<Glib::ustring> rtengine::ICCStore::getWorkingProfiles()
|
||||
{
|
||||
|
||||
std::vector<Glib::ustring> res;
|
||||
|
||||
for (unsigned int i = 0; i < sizeof(wpnames) / sizeof(wpnames[0]); i++) {
|
||||
res.push_back(wpnames[i]);
|
||||
}
|
||||
|
||||
return res;
|
||||
return implementation->getWorkingProfiles();
|
||||
}
|
||||
|
||||
std::vector<Glib::ustring> rtengine::ICCStore::getGamma()
|
||||
|
@ -69,7 +69,7 @@ public:
|
||||
void init(const Glib::ustring& usrICCDir, const Glib::ustring& stdICCDir, bool loadAll);
|
||||
|
||||
cmsHPROFILE workingSpace(const Glib::ustring& name) const;
|
||||
cmsHPROFILE workingSpaceGamma(const Glib::ustring& name) const;
|
||||
// cmsHPROFILE workingSpaceGamma(const Glib::ustring& name) const;
|
||||
TMatrix workingSpaceMatrix(const Glib::ustring& name) const;
|
||||
TMatrix workingSpaceInverseMatrix(const Glib::ustring& name) const;
|
||||
|
||||
@ -95,7 +95,7 @@ public:
|
||||
std::uint8_t getOutputIntents(const Glib::ustring& name) const;
|
||||
std::uint8_t getProofIntents(const Glib::ustring& name) const;
|
||||
|
||||
static std::vector<Glib::ustring> getWorkingProfiles();
|
||||
/*static*/ std::vector<Glib::ustring> getWorkingProfiles();
|
||||
static std::vector<Glib::ustring> getGamma();
|
||||
|
||||
static void getGammaArray(const procparams::ColorManagementParams& icm, GammaValues& ga);
|
||||
|
@ -3997,7 +3997,7 @@ void RawImageSource::colorSpaceConversion_ (Imagefloat* im, const ColorManagemen
|
||||
|
||||
}
|
||||
} else {
|
||||
const bool working_space_is_prophoto = (cmp.working == "ProPhoto");
|
||||
bool working_space_is_prophoto = (cmp.working == "ProPhoto");
|
||||
|
||||
// use supplied input profile
|
||||
|
||||
@ -4066,6 +4066,33 @@ void RawImageSource::colorSpaceConversion_ (Imagefloat* im, const ColorManagemen
|
||||
cmsHPROFILE prophoto = ICCStore::getInstance()->workingSpace("ProPhoto"); // We always use Prophoto to apply the ICC profile to minimize problems with clipping in LUT conversion.
|
||||
bool transform_via_pcs_lab = false;
|
||||
bool separate_pcs_lab_highlights = false;
|
||||
|
||||
// check if the working space is fully contained in prophoto
|
||||
if (!working_space_is_prophoto) {
|
||||
TMatrix toxyz = ICCStore::getInstance()->workingSpaceMatrix(cmp.working);
|
||||
TMatrix torgb = ICCStore::getInstance()->workingSpaceInverseMatrix("ProPhoto");
|
||||
float rgb[3] = {0.f, 0.f, 0.f};
|
||||
for (int i = 0; i < 2 && !working_space_is_prophoto; ++i) {
|
||||
rgb[i] = 1.f;
|
||||
float x, y, z;
|
||||
|
||||
Color::rgbxyz(rgb[0], rgb[1], rgb[2], x, y, z, toxyz);
|
||||
Color::xyz2rgb(x, y, z, rgb[0], rgb[1], rgb[2], torgb);
|
||||
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
if (rgb[j] < 0.f || rgb[j] > 1.f) {
|
||||
working_space_is_prophoto = true;
|
||||
prophoto = ICCStore::getInstance()->workingSpace(cmp.working);
|
||||
if (settings->verbose) {
|
||||
std::cout << "colorSpaceConversion_: converting directly to " << cmp.working << " instead of passing through ProPhoto" << std::endl;
|
||||
}
|
||||
break;
|
||||
}
|
||||
rgb[j] = 0.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lcmsMutex->lock ();
|
||||
|
||||
switch (camera_icc_type) {
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <limits>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
namespace rtengine
|
||||
{
|
||||
@ -138,3 +139,65 @@ constexpr std::uint8_t uint16ToUint8Rounded(std::uint16_t i)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
bool invertMatrix(const std::array<std::array<T, 3>, 3> &in, std::array<std::array<T, 3>, 3> &out)
|
||||
{
|
||||
const T res00 = in[1][1] * in[2][2] - in[2][1] * in[1][2];
|
||||
const T res10 = in[2][0] * in[1][2] - in[1][0] * in[2][2];
|
||||
const T res20 = in[1][0] * in[2][1] - in[2][0] * in[1][1];
|
||||
|
||||
const T det = in[0][0] * res00 + in[0][1] * res10 + in[0][2] * res20;
|
||||
|
||||
if (std::abs(det) < 1.0e-10) {
|
||||
return false;
|
||||
}
|
||||
|
||||
out[0][0] = res00 / det;
|
||||
out[0][1] = (in[2][1] * in[0][2] - in[0][1] * in[2][2]) / det;
|
||||
out[0][2] = (in[0][1] * in[1][2] - in[1][1] * in[0][2]) / det;
|
||||
out[1][0] = res10 / det;
|
||||
out[1][1] = (in[0][0] * in[2][2] - in[2][0] * in[0][2]) / det;
|
||||
out[1][2] = (in[1][0] * in[0][2] - in[0][0] * in[1][2]) / det;
|
||||
out[2][0] = res20 / det;
|
||||
out[2][1] = (in[2][0] * in[0][1] - in[0][0] * in[2][1]) / det;
|
||||
out[2][2] = (in[0][0] * in[1][1] - in[1][0] * in[0][1]) / det;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::array<std::array<T, 3>, 3> dotProduct(const std::array<std::array<T, 3>, 3> &a, const std::array<std::array<T, 3>, 3> &b)
|
||||
{
|
||||
std::array<std::array<T, 3>, 3> res;
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
res[i][j] = 0;
|
||||
|
||||
for (int k = 0; k < 3; ++k) {
|
||||
res[i][j] += a[i][k] * b[k][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
std::array<T, 3> dotProduct(const std::array<std::array<T, 3>, 3> &a, const std::array<T, 3> &b)
|
||||
{
|
||||
std::array<T, 3> res;
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
res[i] = 0;
|
||||
for (int k = 0; k < 3; ++k) {
|
||||
res[i] += a[i][k] * b[k];
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -160,7 +160,7 @@ ICMPanel::ICMPanel () : FoldableToolPanel(this, "icm", M("TP_ICM_LABEL")), iunch
|
||||
wnames = Gtk::manage (new MyComboBoxText ());
|
||||
wVBox->pack_start (*wnames, Gtk::PACK_SHRINK);
|
||||
|
||||
std::vector<Glib::ustring> wpnames = rtengine::ICCStore::getWorkingProfiles();
|
||||
std::vector<Glib::ustring> wpnames = rtengine::ICCStore::getInstance()->getWorkingProfiles();
|
||||
|
||||
for (size_t i = 0; i < wpnames.size(); i++) {
|
||||
wnames->append (wpnames[i]);
|
||||
|
Loading…
x
Reference in New Issue
Block a user