diff --git a/AUTHORS.txt b/AUTHORS.txt
index 3dc820555..0ad8438eb 100644
--- a/AUTHORS.txt
+++ b/AUTHORS.txt
@@ -46,6 +46,7 @@ Other contributors (profiles, ideas, mockups, testing, forum activity, translati
André Gauthier
Sébastien Guyader
M. Dávid Gyurkó
+ JK Han (pinholecam)
Arturs Jekabsons
Marián Kyral
Oscar de Lama
@@ -56,6 +57,8 @@ Other contributors (profiles, ideas, mockups, testing, forum activity, translati
Wim ter Meer
Alberto Righetto
Kostia (Kildor) Romanov
+ Kalle Söderman
Johan Thor
+ Vitalis Tiknius
TooWaBoo
Colin Walker
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 22c71c269..31a04b569 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -228,6 +228,7 @@ pkg_check_modules (GTK REQUIRED gtk+-3.0>=3.16)
pkg_check_modules (GLIB2 REQUIRED glib-2.0>=2.44)
pkg_check_modules (GLIBMM REQUIRED glibmm-2.4>=2.44)
pkg_check_modules (GTKMM REQUIRED gtkmm-3.0>=3.16)
+pkg_check_modules (CAIROMM REQUIRED cairomm-1.0)
pkg_check_modules (GIO REQUIRED gio-2.0>=2.44)
pkg_check_modules (GIOMM REQUIRED giomm-2.4>=2.44)
pkg_check_modules (GTHREAD REQUIRED gthread-2.0>=2.44)
diff --git a/rtdata/languages/default b/rtdata/languages/default
index 35a10b6d7..14838b1ea 100644
--- a/rtdata/languages/default
+++ b/rtdata/languages/default
@@ -690,6 +690,36 @@ HISTORY_MSG_441;Retinex - Gain transmission
HISTORY_MSG_442;Retinex - Scale
HISTORY_MSG_443;Output Black Point Compensation
HISTORY_MSG_444;WB - Temp bias
+HISTORY_MSG_445;Raw Sub-Image
+HISTORY_MSG_446;EvPixelShiftMotion
+HISTORY_MSG_447;EvPixelShiftMotionCorrection
+HISTORY_MSG_448;EvPixelShiftStddevFactorGreen
+HISTORY_MSG_449;PS ISO adaption
+HISTORY_MSG_450;EvPixelShiftNreadIso
+HISTORY_MSG_451;EvPixelShiftPrnu
+HISTORY_MSG_452;PS Show motion
+HISTORY_MSG_453;PS Show mask only
+HISTORY_MSG_454;EvPixelShiftAutomatic
+HISTORY_MSG_455;EvPixelShiftNonGreenHorizontal
+HISTORY_MSG_456;EvPixelShiftNonGreenVertical
+HISTORY_MSG_457;PS Check red/blue
+HISTORY_MSG_458;EvPixelShiftStddevFactorRed
+HISTORY_MSG_459;EvPixelShiftStddevFactorBlue
+HISTORY_MSG_460;EvPixelShiftGreenAmaze
+HISTORY_MSG_461;EvPixelShiftNonGreenAmaze
+HISTORY_MSG_462;PS Check green
+HISTORY_MSG_463;EvPixelShiftRedBlueWeight
+HISTORY_MSG_464;PS Blur motion mask
+HISTORY_MSG_465;PS Blur radius
+HISTORY_MSG_466;EvPixelShiftSum
+HISTORY_MSG_467;EvPixelShiftExp0
+HISTORY_MSG_468;PS Fill holes
+HISTORY_MSG_469;PS Median
+HISTORY_MSG_470;EvPixelShiftMedian3
+HISTORY_MSG_471;PS Motion correction
+HISTORY_MSG_472;PS Smooth transitions
+HISTORY_MSG_473;PS Use lmmse
+HISTORY_MSG_474;PS Equalize
HISTORY_NEWSNAPSHOT;Add
HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s
HISTORY_SNAPSHOT;Snapshot
@@ -880,6 +910,7 @@ PARTIALPASTE_RAW_DCBENHANCE;DCB enhancement
PARTIALPASTE_RAW_DCBITERATIONS;DCB iterations
PARTIALPASTE_RAW_DMETHOD;Demosaic method
PARTIALPASTE_RAW_FALSECOLOR;False color suppression
+PARTIALPASTE_RAW_IMAGENUM;Sub-image
PARTIALPASTE_RAW_LMMSEITERATIONS;LMMSE enhancement steps
PARTIALPASTE_RESIZE;Resize
PARTIALPASTE_RETINEX;Retinex
@@ -1665,13 +1696,60 @@ TP_RAW_DCBITERATIONS;Number of DCB iterations
TP_RAW_DMETHOD;Method
TP_RAW_DMETHOD_PROGRESSBAR;%1 demosaicing...
TP_RAW_DMETHOD_PROGRESSBAR_REFINE;Demosaicing refinement...
-TP_RAW_DMETHOD_TOOLTIP;Note: IGV and LMMSE are dedicated to high ISO images to aid in noise reduction without leading to maze patterns, posterization or a washed-out look.
+TP_RAW_DMETHOD_TOOLTIP;Note: IGV and LMMSE are dedicated to high ISO images to aid in noise reduction without leading to maze patterns, posterization or a washed-out look.\nPixelshift is for Pentax pixelshift files. It falls back to Amaze for non pixelshift files.
TP_RAW_FALSECOLOR;False color suppression steps
TP_RAW_HD;Threshold
TP_RAW_HD_TOOLTIP;Lower values make hot/dead pixel detection more aggressive, but false positives may lead to artifacts. If you notice any artifacts appearing when enabling the Hot/Dead Pixel Filters, gradually increase the threshold value until they disappear.
+TP_RAW_IMAGENUM;Sub-image
+TP_RAW_IMAGENUM_TOOLTIP;Some raw files might embed several sub-images (HDR, Pixel-Shift, Dual Sensitivity). Use this button to select the sub-image.\n\nThe last sub-image will be used if you select a value beyond the real sub-image count.
TP_RAW_LABEL;Demosaicing
TP_RAW_LMMSEITERATIONS;LMMSE enhancement steps
TP_RAW_LMMSE_TOOLTIP;Adds gamma (step 1), median (steps 2-4) and refinement (steps 5-6) to reduce artifacts and improve the signal-to-noise ratio.
+TP_RAW_PIXELSHIFTADAPTIVE;Adaptive detection
+TP_RAW_PIXELSHIFTNONGREENHORIZONTAL;Check red/blue horizontal
+TP_RAW_PIXELSHIFTNONGREENVERTICAL;Check red/blue vertical
+TP_RAW_PIXELSHIFTMEDIAN;Median
+TP_RAW_PIXELSHIFTMEDIAN3;Exclude selected frame from median
+TP_RAW_PIXELSHIFTHOLEFILL;Fill holes in motion mask
+TP_RAW_PIXELSHIFTBLUR;Blur motion mask
+TP_RAW_PIXELSHIFTSIGMA_TOOLTIP;Default radius of 1.0 usually fits good for base ISO. Increase value for high ISO shots,\n5.0 is a good starting point for high ISO shots.\nWatch motion mask while changing the value.
+TP_RAW_PIXELSHIFTSMOOTH;Smooth transitions
+TP_RAW_PIXELSHIFTLMMSE_TOOLTIP;Use lmmse instead of amaze for motion areas.\nUseful for High ISO images.
+TP_RAW_PIXELSHIFTLMMSE;Use lmmse for motion parts
+TP_RAW_PIXELSHIFTEQUALBRIGHT;Equalize brightness of frames
+TP_RAW_PIXELSHIFTEQUALBRIGHT_TOOLTIP;Equalize the brightness of the frames to the brightness of the selected frame.\nIf there are overexposed areas in the frames select the brightest frame to avoid magenta colour cast in overexposed areas or enable motion correction.
+TP_RAW_PIXELSHIFTEXP0;Experimental
+TP_RAW_PIXELSHIFTGREEN;Check green channel for motion
+TP_RAW_PIXELSHIFTNONGREENCROSS;Check red/blue channels for motion
+TP_RAW_PIXELSHIFTNONGREENCROSS2;Check green amaze
+TP_RAW_PIXELSHIFTNONGREENAMAZE;Check red/blue amaze
+TP_RAW_PIXELSHIFTMOTION;Motion detection level (deprecated)
+TP_RAW_PIXELSHIFTMOTION_TOOLTIP;0 means no motion detection\n1 - 99 means motion will be detected according to this value. Increase value to increase detection rate\n100 means the Amaze demosaiced frame will be used
+TP_RAW_PIXELSHIFTHOLEFILL_TOOLTIP;Fill holes in motion mask
+TP_RAW_PIXELSHIFTBLUR_TOOLTIP;Blur motion mask
+TP_RAW_PIXELSHIFTSMOOTH_TOOLTIP;Smooth transitions between areas with and without motion.\nSet to 0 to disable smooth transitions\nSet to 1 to get Amaze/lmmse or Median
+TP_RAW_PIXELSHIFTMEDIAN_TOOLTIP;Use median of all frames instead of selected frame for regions with motion.\nRemoves objects which are at different places in all frames.\nGives motion effect on slow moving (overlapping) objects.
+TP_RAW_PIXELSHIFTMEDIAN3_TOOLTIP;Excludes selected frame from median.\nUseful if moving objects overlap in frame 2 and 3
+TP_RAW_PIXELSHIFTSHOWMOTION_TOOLTIP;Overlays the image with a mask showing the regions with motion
+TP_RAW_PIXELSHIFTSHOWMOTIONMASKONLY_TOOLTIP;Shows the motion mask without the image
+TP_RAW_PIXELSHIFTMOTIONCORRECTION;Green motion correction size
+TP_RAW_PIXELSHIFTMOTIONCORRECTION_TOOLTIP;1 = 2 pixels\n3 = 3x3 grid\n5 = 5x5 grid
+TP_RAW_PIXELSHIFTMOTIONMETHOD;Motion Correction
+TP_RAW_PIXELSHIFTMM_OFF;Off
+TP_RAW_PIXELSHIFTMM_AUTO;Automatic
+TP_RAW_PIXELSHIFTMM_CUSTOM;Custom
+TP_RAW_PIXELSHIFTSHOWMOTION;Show motion
+TP_RAW_PIXELSHIFTSHOWMOTIONMASKONLY;Show mask only
+TP_RAW_PIXELSHIFTSTDDEVFACTORGREEN;StdDev factor Green
+TP_RAW_PIXELSHIFTSTDDEVFACTORRED;StdDev factor Red
+TP_RAW_PIXELSHIFTSTDDEVFACTORBLUE;StdDev factor Blue
+TP_RAW_PIXELSHIFTEPERISO;ISO adaption
+TP_RAW_PIXELSHIFTEPERISO_TOOLTIP;The default value (0.0) should work fine for base ISO.\nIncrease the value to improve motion detection for higher ISO.\nIncrease in small steps and watch the motion mask while increasing.
+TP_RAW_PIXELSHIFTNREADISO;nRead
+TP_RAW_PIXELSHIFTPRNU;PRNU (%)
+TP_RAW_PIXELSHIFTSIGMA;Blur radius
+TP_RAW_PIXELSHIFTMASKTHRESHOLD;3x3 new threshold
+TP_RAW_PIXELSHIFTREDBLUEWEIGHT;Red&Blue weight
TP_RAW_SENSOR_BAYER_LABEL;Sensor with Bayer Matrix
TP_RAW_SENSOR_XTRANS_DMETHOD_TOOLTIP;3-pass gives best results (recommended for low ISO images).\n1-pass is almost undistinguishable from 3-pass for high ISO images and is faster.
TP_RAW_SENSOR_XTRANS_LABEL;Sensor with X-Trans Matrix
diff --git a/rtdata/themes/RawTherapee-GTK3-20_.css b/rtdata/themes/RawTherapee-GTK3-20_.css
index 27d2b5f13..c1768f913 100644
--- a/rtdata/themes/RawTherapee-GTK3-20_.css
+++ b/rtdata/themes/RawTherapee-GTK3-20_.css
@@ -243,6 +243,13 @@ scale trough {
margin: 6px 6px; /* have to be half of "scale slider / min-width min-height" */
background-color: #2A2A2A;
}
+scale.fine-tune trough {
+ margin: 5px;
+ padding: 1px;
+}
+scale.fine-tune trough highlight {
+ margin: -2px;
+}
scale:disabled trough {
background-color: #444;
border-color: #282828;
diff --git a/rtengine/CA_correct_RT.cc b/rtengine/CA_correct_RT.cc
index 7c28801ae..fc9967619 100644
--- a/rtengine/CA_correct_RT.cc
+++ b/rtengine/CA_correct_RT.cc
@@ -112,7 +112,7 @@ bool LinEqSolve(int nDim, double* pfMatr, double* pfVect, double* pfSolution)
using namespace std;
using namespace rtengine;
-void RawImageSource::CA_correct_RT(const double cared, const double cablue, const double caautostrength)
+void RawImageSource::CA_correct_RT(const bool autoCA, const double cared, const double cablue, const double caautostrength, array2D &rawData)
{
// multithreaded and partly vectorized by Ingo Weyrich
constexpr int ts = 128;
@@ -134,7 +134,6 @@ void RawImageSource::CA_correct_RT(const double cared, const double cablue, cons
plistener->setProgress (progress);
}
- const bool autoCA = (cared == 0 && cablue == 0);
// local variables
const int width = W, height = H;
//temporary array to store simple interpolation of G
@@ -695,7 +694,7 @@ void RawImageSource::CA_correct_RT(const double cared, const double cablue, cons
//fitparams[polyord*i+j] gives the coefficients of (vblock^i hblock^j) in a polynomial fit for i,j<=4
}
//end of initialization for CA correction pass
- //only executed if cared and cablue are zero
+ //only executed if autoCA is true
}
// Main algorithm: Tile loop
diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt
index dbab1dc2d..479534bec 100644
--- a/rtengine/CMakeLists.txt
+++ b/rtengine/CMakeLists.txt
@@ -13,7 +13,7 @@ set (RTENGINESOURCEFILES colortemp.cc curves.cc flatcurves.cc diagonalcurves.cc
fast_demo.cc amaze_demosaic_RT.cc CA_correct_RT.cc cfa_linedn_RT.cc green_equil_RT.cc hilite_recon.cc expo_before_b.cc
stdimagesource.cc myfile.cc iccjpeg.cc improccoordinator.cc pipettebuffer.cc coord.cc
processingjob.cc rtthumbnail.cc utils.cc labimage.cc slicer.cc cieimage.cc
- iplab2rgb.cc ipsharpen.cc iptransform.cc ipresize.cc ipvibrance.cc
+ iplab2rgb.cc ipsharpen.cc iptransform.cc ipresize.cc ipvibrance.cc icons.cc
imagedimensions.cc jpeg_ijg/jpeg_memsrc.cc jdatasrc.cc iimage.cc
EdgePreservingDecomposition.cc cplx_wavelet_dec.cc FTblockDN.cc
PF_correct_RT.cc previewimage.cc ipwavelet.cc
@@ -24,6 +24,7 @@ set (RTENGINESOURCEFILES colortemp.cc curves.cc flatcurves.cc diagonalcurves.cc
klt/storeFeatures.cc klt/trackFeatures.cc klt/writeFeatures.cc
clutstore.cc
ciecam02.cc
+ pixelshift.cc
)
include_directories (BEFORE "${CMAKE_CURRENT_BINARY_DIR}")
diff --git a/rtengine/FTblockDN.cc b/rtengine/FTblockDN.cc
index 1ad34f2d9..d075ec5bc 100644
--- a/rtengine/FTblockDN.cc
+++ b/rtengine/FTblockDN.cc
@@ -497,7 +497,7 @@ SSEFUNCTION void ImProcFunctions::RGB_denoise(int kall, Imagefloat * src, Imagef
if (useNoiseLCurve || useNoiseCCurve) {
int hei = calclum->getHeight();
int wid = calclum->getWidth();
- TMatrix wprofi = iccStore->workingSpaceMatrix (params->icm.working);
+ TMatrix wprofi = ICCStore::getInstance()->workingSpaceMatrix (params->icm.working);
const float wpi[3][3] = {
{static_cast(wprofi[0][0]), static_cast(wprofi[0][1]), static_cast(wprofi[0][2])},
@@ -764,7 +764,7 @@ SSEFUNCTION void ImProcFunctions::RGB_denoise(int kall, Imagefloat * src, Imagef
}
}
- TMatrix wiprof = iccStore->workingSpaceInverseMatrix (params->icm.working);
+ TMatrix wiprof = ICCStore::getInstance()->workingSpaceInverseMatrix (params->icm.working);
//inverse matrix user select
const float wip[3][3] = {
{static_cast(wiprof[0][0]), static_cast(wiprof[0][1]), static_cast(wiprof[0][2])},
@@ -772,7 +772,7 @@ SSEFUNCTION void ImProcFunctions::RGB_denoise(int kall, Imagefloat * src, Imagef
{static_cast(wiprof[2][0]), static_cast(wiprof[2][1]), static_cast(wiprof[2][2])}
};
- TMatrix wprof = iccStore->workingSpaceMatrix (params->icm.working);
+ TMatrix wprof = ICCStore::getInstance()->workingSpaceMatrix (params->icm.working);
const float wp[3][3] = {
{static_cast(wprof[0][0]), static_cast(wprof[0][1]), static_cast(wprof[0][2])},
@@ -3117,7 +3117,7 @@ SSEFUNCTION void ImProcFunctions::RGB_denoise_info(Imagefloat * src, Imagefloat
float** bcalc;
hei = provicalc->getHeight();
wid = provicalc->getWidth();
- TMatrix wprofi = iccStore->workingSpaceMatrix (params->icm.working);
+ TMatrix wprofi = ICCStore::getInstance()->workingSpaceMatrix (params->icm.working);
const float wpi[3][3] = {
{static_cast(wprofi[0][0]), static_cast(wprofi[0][1]), static_cast(wprofi[0][2])},
@@ -3192,7 +3192,7 @@ SSEFUNCTION void ImProcFunctions::RGB_denoise_info(Imagefloat * src, Imagefloat
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
- TMatrix wprof = iccStore->workingSpaceMatrix (params->icm.working);
+ TMatrix wprof = ICCStore::getInstance()->workingSpaceMatrix (params->icm.working);
const float wp[3][3] = {
{static_cast(wprof[0][0]), static_cast(wprof[0][1]), static_cast(wprof[0][2])},
{static_cast(wprof[1][0]), static_cast(wprof[1][1]), static_cast(wprof[1][2])},
diff --git a/rtengine/amaze_demosaic_RT.cc b/rtengine/amaze_demosaic_RT.cc
index c5535493c..3cb0ee8de 100644
--- a/rtengine/amaze_demosaic_RT.cc
+++ b/rtengine/amaze_demosaic_RT.cc
@@ -38,7 +38,7 @@
namespace rtengine
{
-SSEFUNCTION void RawImageSource::amaze_demosaic_RT(int winx, int winy, int winw, int winh)
+SSEFUNCTION void RawImageSource::amaze_demosaic_RT(int winx, int winy, int winw, int winh, array2D &rawData, array2D &red, array2D &green, array2D &blue)
{
BENCHFUN
diff --git a/rtengine/camconst.json b/rtengine/camconst.json
index f506a190e..679c7a134 100644
--- a/rtengine/camconst.json
+++ b/rtengine/camconst.json
@@ -1768,6 +1768,11 @@ Camera constants:
"ranges": { "white": 4080 } // nominal at ISO200 4094
},
+ { // Quality C, only Raw crop
+ "make_model": [ "RICOH PENTAX KP", "PENTAX KP" ],
+ "raw_crop": [ 52, 28, 6032, 4028 ]
+ },
+
{ // Quality B, Intemediate ISO samples missing, Pentax_DNG WLtags are after BL sutraction and not valid
"make_model": [ "RICOH PENTAX K-70", "PENTAX K-70" ],
//"dcraw_matrix": [ 8050,-2061,-1264,-4359,12953,1515,-1096,1965,6075 ], // PENTAX DNG D65
diff --git a/rtengine/clutstore.cc b/rtengine/clutstore.cc
index 9e9958418..7a5f6fd19 100644
--- a/rtengine/clutstore.cc
+++ b/rtengine/clutstore.cc
@@ -2,10 +2,12 @@
#include "clutstore.h"
+#include "iccstore.h"
+#include "imagefloat.h"
#include "opthelper.h"
#include "rt_math.h"
-#include "imagefloat.h"
#include "stdimagesource.h"
+
#include "../rtgui/options.h"
namespace
@@ -284,7 +286,7 @@ void rtengine::HaldCLUT::splitClutFilename(
profile_name = "sRGB";
if (!name.empty()) {
- for (const auto& working_profile : rtengine::getWorkingProfiles()) {
+ for (const auto& working_profile : rtengine::ICCStore::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()
diff --git a/rtengine/curves.cc b/rtengine/curves.cc
index cf2fd0d04..8f2aa03d3 100644
--- a/rtengine/curves.cc
+++ b/rtengine/curves.cc
@@ -2140,7 +2140,7 @@ void PerceptualToneCurve::initApplyState(PerceptualToneCurveState & state, Glib:
state.isProphoto = true;
} else {
state.isProphoto = false;
- TMatrix Work = iccStore->workingSpaceMatrix(workingSpace);
+ TMatrix Work = ICCStore::getInstance()->workingSpaceMatrix(workingSpace);
memset(state.Working2Prophoto, 0, sizeof(state.Working2Prophoto));
for (int i = 0; i < 3; i++)
@@ -2149,7 +2149,7 @@ void PerceptualToneCurve::initApplyState(PerceptualToneCurveState & state, Glib:
state.Working2Prophoto[i][j] += prophoto_xyz[i][k] * Work[k][j];
}
- Work = iccStore->workingSpaceInverseMatrix (workingSpace);
+ Work = ICCStore::getInstance()->workingSpaceInverseMatrix (workingSpace);
memset(state.Prophoto2Working, 0, sizeof(state.Prophoto2Working));
for (int i = 0; i < 3; i++)
diff --git a/rtengine/dcp.cc b/rtengine/dcp.cc
index 58524d94c..57e25691f 100644
--- a/rtengine/dcp.cc
+++ b/rtengine/dcp.cc
@@ -406,6 +406,7 @@ DCPProfile::DCPProfile(const Glib::ustring& filename) :
has_tone_curve(false),
has_baseline_exposure_offset(false),
will_interpolate(false),
+ valid(false),
baseline_exposure_offset(0.0)
{
constexpr int tiff_float_size = 4;
@@ -691,6 +692,11 @@ DCPProfile::DCPProfile(const Glib::ustring& filename) :
FILE* const file = g_fopen(filename.c_str(), "rb");
+ if (file == nullptr) {
+ printf ("Unable to load DCP profile '%s' !", filename.c_str());
+ return;
+ }
+
std::unique_ptr tagDir(ExifManager::parseTIFF(file, false));
Tag* tag = tagDir->getTag(toUnderlying(TagKey::CALIBRATION_ILLUMINANT_1));
@@ -738,6 +744,7 @@ DCPProfile::DCPProfile(const Glib::ustring& filename) :
if (!tag) {
std::cerr << "DCP '" << filename << "' is missing 'ColorMatrix1'. Skipped." << std::endl;
+ fclose(file);
return;
}
@@ -935,6 +942,8 @@ DCPProfile::DCPProfile(const Glib::ustring& filename) :
if (file) {
fclose(file);
}
+
+ valid = true;
}
DCPProfile::~DCPProfile()
@@ -988,7 +997,7 @@ void DCPProfile::apply(
) const
{
- const TMatrix work_matrix = iccStore->workingSpaceInverseMatrix(working_space);
+ const TMatrix work_matrix = ICCStore::getInstance()->workingSpaceInverseMatrix(working_space);
const Matrix xyz_cam = makeXyzCam(white_balance, pre_mul, cam_wb_matrix, preferred_illuminant); // Camera RGB to XYZ D50 matrix
@@ -1110,7 +1119,7 @@ void DCPProfile::setStep2ApplyState(const Glib::ustring& working_space, bool use
as_out.data->already_pro_photo = false;
TMatrix mWork;
- mWork = iccStore->workingSpaceMatrix (working_space);
+ mWork = ICCStore::getInstance()->workingSpaceMatrix (working_space);
memset(as_out.data->pro_photo, 0, sizeof(as_out.data->pro_photo));
for (int i = 0; i < 3; i++)
@@ -1119,7 +1128,7 @@ void DCPProfile::setStep2ApplyState(const Glib::ustring& working_space, bool use
as_out.data->pro_photo[i][j] += prophoto_xyz[i][k] * mWork[k][j];
}
- mWork = iccStore->workingSpaceInverseMatrix (working_space);
+ mWork = ICCStore::getInstance()->workingSpaceInverseMatrix (working_space);
memset(as_out.data->work, 0, sizeof(as_out.data->work));
for (int i = 0; i < 3; i++)
@@ -1683,18 +1692,28 @@ void DCPProfile::hsdApply(const HsdTableInfo& table_info, const std::vector dirs = {
rt_profile_dir
@@ -1717,10 +1736,8 @@ void DCPStore::init(const Glib::ustring& rt_profile_dir)
return;
}
- dirname += '/';
-
for (const Glib::ustring& sname : *dir) {
- const Glib::ustring fname = dirname + sname;
+ const Glib::ustring fname = Glib::build_filename(dirname, sname);
if (!Glib::file_test(fname, Glib::FILE_TEST_IS_DIR)) {
// File
@@ -1770,9 +1787,12 @@ DCPProfile* DCPStore::getProfile(const Glib::ustring& filename) const
DCPProfile* const res = new DCPProfile(filename);
- if (*res) {
+ if (res->isValid()) {
// Add profile
profile_cache[filename] = res;
+ if (options.rtSettings.verbose) {
+ printf("DCP profile '%s' loaded from disk\n", filename.c_str());
+ }
return res;
}
@@ -1780,15 +1800,25 @@ DCPProfile* DCPStore::getProfile(const Glib::ustring& filename) const
return nullptr;
}
-DCPProfile* DCPStore::getStdProfile(const Glib::ustring& cam_short_name) const
+DCPProfile* DCPStore::getStdProfile(const Glib::ustring& requested_cam_short_name) const
{
- const Glib::ustring name = cam_short_name.uppercase();
+ const Glib::ustring name = requested_cam_short_name.uppercase();
// Warning: do NOT use map.find(), since it does not seem to work reliably here
- for (const auto& file_std_profile : file_std_profiles)
+ for (const auto& file_std_profile : file_std_profiles) {
if (file_std_profile.first == name) {
return getProfile(file_std_profile.second);
}
+ }
+
+ // profile not found, looking if we're in loadAll=false mode
+ if (!profileDir.empty()) {
+ const Glib::ustring fname = Glib::build_filename(profileDir, requested_cam_short_name + Glib::ustring(".dcp"));
+
+ if (Glib::file_test(fname, Glib::FILE_TEST_EXISTS)) {
+ return getProfile(fname);
+ }
+ }
return nullptr;
}
diff --git a/rtengine/dcp.h b/rtengine/dcp.h
index 8b781ce6b..5a6858099 100644
--- a/rtengine/dcp.h
+++ b/rtengine/dcp.h
@@ -75,6 +75,7 @@ public:
bool getHasBaselineExposureOffset() const;
Illuminants getIlluminants() const;
+ bool isValid();
void apply(
Imagefloat* img,
@@ -130,6 +131,7 @@ private:
bool has_tone_curve;
bool has_baseline_exposure_offset;
bool will_interpolate;
+ bool valid;
Matrix forward_matrix_1;
Matrix forward_matrix_2;
double temperature_1;
@@ -152,7 +154,7 @@ class DCPStore final :
public:
static DCPStore* getInstance();
- void init(const Glib::ustring& rt_profile_dir);
+ void init(const Glib::ustring& rt_profile_dir, bool loadAll = true);
bool isValidDCPFileName(const Glib::ustring& filename) const;
@@ -163,6 +165,7 @@ private:
DCPStore() = default;
mutable MyMutex mutex;
+ Glib::ustring profileDir;
// these contain standard profiles from RT. keys are all in uppercase, file path is value
std::map file_std_profiles;
diff --git a/rtengine/dcraw.cc b/rtengine/dcraw.cc
index 286574e0f..c1d2e670a 100644
--- a/rtengine/dcraw.cc
+++ b/rtengine/dcraw.cc
@@ -8578,7 +8578,7 @@ void CLASS identify()
parse_fuji (i);
}
load_raw = &CLASS unpacked_load_raw;
- fseek (ifp, 100+28*(shot_select > 0), SEEK_SET);
+ fseek (ifp, 100+28*(shot_select > 0 && shot_select < is_raw), SEEK_SET);
parse_tiff (data_offset = get4());
parse_tiff (thumb_offset+12);
/*RT*/ exif_base = thumb_offset+12;
@@ -9492,7 +9492,7 @@ dng_skip:
adobe_coeff (make, model);
if(!strncmp(make, "Samsung", 7) && !strncmp(model, "NX1",3))
adobe_coeff (make, model);
- if(!strncmp(make, "Pentax", 6) && (!strncmp(model, "K10D",4) || !strncmp(model, "K-70",4)))
+ if(!strncmp(make, "Pentax", 6) && (!strncmp(model, "K10D",4) || !strncmp(model, "K-70",4) || !strncmp(model, "K-1",3)))
adobe_coeff (make, model);
if(!strncmp(make, "Leica", 5) && !strncmp(model, "Q",1))
adobe_coeff (make, model);
diff --git a/rtengine/dcraw.patch b/rtengine/dcraw.patch
index 22c53d831..43f9e12eb 100644
--- a/rtengine/dcraw.patch
+++ b/rtengine/dcraw.patch
@@ -1,5 +1,5 @@
---- dcraw.c 2016-10-28 13:45:27 +0000
-+++ dcraw.cc 2016-10-31 13:35:15 +0000
+--- dcraw.c 2016-11-01 01:07:55 +0000
++++ dcraw.cc 2016-11-01 14:54:02 +0000
@@ -1,3 +1,16 @@
+/*RT*/#include
+/*RT*/#include
@@ -2892,8 +2892,12 @@
parse_ciff (hlen, flen-hlen, 0);
load_raw = &CLASS canon_load_raw;
} else if (parse_tiff(0)) apply_tiff();
-@@ -8494,6 +8575,7 @@
- fseek (ifp, 100+28*(shot_select > 0), SEEK_SET);
+@@ -8491,9 +8572,10 @@
+ parse_fuji (i);
+ }
+ load_raw = &CLASS unpacked_load_raw;
+- fseek (ifp, 100+28*(shot_select > 0), SEEK_SET);
++ fseek (ifp, 100+28*(shot_select > 0 && shot_select < is_raw), SEEK_SET);
parse_tiff (data_offset = get4());
parse_tiff (thumb_offset+12);
+/*RT*/ exif_base = thumb_offset+12;
diff --git a/rtengine/demosaic_algos.cc b/rtengine/demosaic_algos.cc
index 60abe8998..877df35a4 100644
--- a/rtengine/demosaic_algos.cc
+++ b/rtengine/demosaic_algos.cc
@@ -1316,7 +1316,7 @@ void RawImageSource::jdl_interpolate_omp() // from "Lassus"
// Adapted to RawTherapee by Jacques Desmis 3/2013
// Improved speed and reduced memory consumption by Ingo Weyrich 2/2015
//TODO Tiles to reduce memory consumption
-SSEFUNCTION void RawImageSource::lmmse_interpolate_omp(int winw, int winh, int iterations)
+SSEFUNCTION void RawImageSource::lmmse_interpolate_omp(int winw, int winh, array2D &rawData, array2D &red, array2D &green, array2D &blue, int iterations)
{
const int width = winw, height = winh;
const int ba = 10;
diff --git a/rtengine/dfmanager.cc b/rtengine/dfmanager.cc
index 728d40d53..c71d7f98b 100644
--- a/rtengine/dfmanager.cc
+++ b/rtengine/dfmanager.cc
@@ -144,7 +144,7 @@ void dfInfo::updateRawImage()
} else {
int H = ri->get_height();
int W = ri->get_width();
- ri->compress_image();
+ ri->compress_image(0);
int rSize = W * ((ri->getSensorType() == ST_BAYER || ri->getSensorType() == ST_FUJI_XTRANS) ? 1 : 3);
acc_t **acc = new acc_t*[H];
@@ -164,7 +164,7 @@ void dfInfo::updateRawImage()
RawImage* temp = new RawImage(*iName);
if( !temp->loadRaw(true)) {
- temp->compress_image(); //\ TODO would be better working on original, because is temporary
+ temp->compress_image(0); //\ TODO would be better working on original, because is temporary
nFiles++;
if( ri->getSensorType() == ST_BAYER || ri->getSensorType() == ST_FUJI_XTRANS ) {
@@ -204,7 +204,7 @@ void dfInfo::updateRawImage()
delete ri;
ri = nullptr;
} else {
- ri->compress_image();
+ ri->compress_image(0);
}
}
}
diff --git a/rtengine/expo_before_b.cc b/rtengine/expo_before_b.cc
index 0854647c5..98fed04f7 100644
--- a/rtengine/expo_before_b.cc
+++ b/rtengine/expo_before_b.cc
@@ -44,7 +44,7 @@ namespace rtengine
extern const Settings* settings;
-void RawImageSource::processRawWhitepoint(float expos, float preser)
+void RawImageSource::processRawWhitepoint(float expos, float preser, array2D &rawData)
{
MyTime t1e, t2e;
diff --git a/rtengine/ffmanager.cc b/rtengine/ffmanager.cc
index 069bbf563..87ae98905 100644
--- a/rtengine/ffmanager.cc
+++ b/rtengine/ffmanager.cc
@@ -130,14 +130,13 @@ void ffInfo::updateRawImage()
if( !pathNames.empty() ) {
std::list::iterator iName = pathNames.begin();
ri = new RawImage(*iName); // First file used also for extra pixels informations (width,height, shutter, filters etc.. )
-
if( ri->loadRaw(true)) {
delete ri;
ri = nullptr;
} else {
int H = ri->get_height();
int W = ri->get_width();
- ri->compress_image();
+ ri->compress_image(0);
int rSize = W * ((ri->getSensorType() == ST_BAYER || ri->getSensorType() == ST_FUJI_XTRANS) ? 1 : 3);
acc_t **acc = new acc_t*[H];
@@ -157,7 +156,7 @@ void ffInfo::updateRawImage()
RawImage* temp = new RawImage(*iName);
if( !temp->loadRaw(true)) {
- temp->compress_image(); //\ TODO would be better working on original, because is temporary
+ temp->compress_image(0); //\ TODO would be better working on original, because is temporary
nFiles++;
if( ri->getSensorType() == ST_BAYER || ri->getSensorType() == ST_FUJI_XTRANS ) {
@@ -192,12 +191,11 @@ void ffInfo::updateRawImage()
}
} else {
ri = new RawImage(pathname);
-
if( ri->loadRaw(true)) {
delete ri;
ri = nullptr;
} else {
- ri->compress_image();
+ ri->compress_image(0);
}
}
diff --git a/rtengine/green_equil_RT.cc b/rtengine/green_equil_RT.cc
index a9183509d..8b1136359 100644
--- a/rtengine/green_equil_RT.cc
+++ b/rtengine/green_equil_RT.cc
@@ -30,12 +30,11 @@
#include "rt_math.h"
#include "rawimagesource.h"
-
namespace rtengine
{
//void green_equilibrate()//for dcraw implementation
-void RawImageSource::green_equilibrate(float thresh)
+void RawImageSource::green_equilibrate(float thresh, array2D &rawData)
{
// thresh = threshold for performing green equilibration; max percentage difference of G1 vs G2
// G1-G2 differences larger than this will be assumed to be Nyquist texture, and left untouched
@@ -79,7 +78,7 @@ void RawImageSource::green_equilibrate(float thresh)
*/
//now smooth the cfa data
#ifdef _OPENMP
- #pragma omp parallel for
+ #pragma omp parallel for schedule(dynamic,16)
#endif
for (int rr = 4; rr < height - 4; rr++)
@@ -99,8 +98,8 @@ void RawImageSource::green_equilibrate(float thresh)
float d1 = (o1_1 + o1_2 + o1_3 + o1_4) * 0.25f;
float d2 = (o2_1 + o2_2 + o2_3 + o2_4) * 0.25f;
- float c1 = (fabs(o1_1 - o1_2) + fabs(o1_1 - o1_3) + fabs(o1_1 - o1_4) + fabs(o1_2 - o1_3) + fabs(o1_3 - o1_4) + fabs(o1_2 - o1_4)) / 6.0;
- float c2 = (fabs(o2_1 - o2_2) + fabs(o2_1 - o2_3) + fabs(o2_1 - o2_4) + fabs(o2_2 - o2_3) + fabs(o2_3 - o2_4) + fabs(o2_2 - o2_4)) / 6.0;
+ float c1 = (fabs(o1_1 - o1_2) + fabs(o1_1 - o1_3) + fabs(o1_1 - o1_4) + fabs(o1_2 - o1_3) + fabs(o1_3 - o1_4) + fabs(o1_2 - o1_4)) / 6.f;
+ float c2 = (fabs(o2_1 - o2_2) + fabs(o2_1 - o2_3) + fabs(o2_1 - o2_4) + fabs(o2_2 - o2_3) + fabs(o2_3 - o2_4) + fabs(o2_2 - o2_4)) / 6.f;
//%%%%%%%%%%%%%%%%%%%%%%
//vote1=(checker[rr-2][cc]+checker[rr][cc-2]+checker[rr][cc+2]+checker[rr+2][cc]);
@@ -111,10 +110,10 @@ void RawImageSource::green_equilibrate(float thresh)
//pixel interpolation
float gin = cfa[rr][cc];
- float gse = (cfa[rr + 1][cc + 1]) + 0.5 * (cfa[rr][cc] - cfa[rr + 2][cc + 2]);
- float gnw = (cfa[rr - 1][cc - 1]) + 0.5 * (cfa[rr][cc] - cfa[rr - 2][cc - 2]);
- float gne = (cfa[rr - 1][cc + 1]) + 0.5 * (cfa[rr][cc] - cfa[rr - 2][cc + 2]);
- float gsw = (cfa[rr + 1][cc - 1]) + 0.5 * (cfa[rr][cc] - cfa[rr + 2][cc - 2]);
+ float gse = (cfa[rr + 1][cc + 1]) + 0.5f * (cfa[rr][cc] - cfa[rr + 2][cc + 2]);
+ float gnw = (cfa[rr - 1][cc - 1]) + 0.5f * (cfa[rr][cc] - cfa[rr - 2][cc - 2]);
+ float gne = (cfa[rr - 1][cc + 1]) + 0.5f * (cfa[rr][cc] - cfa[rr - 2][cc + 2]);
+ float gsw = (cfa[rr + 1][cc - 1]) + 0.5f * (cfa[rr][cc] - cfa[rr + 2][cc - 2]);
diff --git a/rtengine/iccstore.cc b/rtengine/iccstore.cc
index 52dd25573..4c749e3c4 100644
--- a/rtengine/iccstore.cc
+++ b/rtengine/iccstore.cc
@@ -16,94 +16,161 @@
* You should have received a copy of the GNU General Public License
* along with RawTherapee. If not, see .
*/
-#include "iccstore.h"
-
#include
+#include
+#include
+
#ifdef WIN32
#include
#else
#include
#endif
-#include
+#include "iccstore.h"
#include "iccmatrices.h"
+#include "procparams.h"
#include "../rtgui/options.h"
+#include "../rtgui/threadutils.h"
namespace rtengine
{
+
extern const Settings* settings;
-void loadProfiles (const Glib::ustring& dirName,
- std::map* profiles,
- std::map* profileContents,
- std::map* profileNames,
- bool nameUpper)
+}
+
+namespace
{
- if (dirName.empty ()) {
+
+// Not recursive
+void loadProfiles(
+ const Glib::ustring& dirName,
+ std::map* profiles,
+ std::map* profileContents,
+ std::map* profileNames,
+ bool nameUpper
+)
+{
+ if (dirName.empty()) {
return;
}
try {
+ Glib::Dir dir(dirName);
- Glib::Dir dir (dirName);
-
- for (Glib::DirIterator entry = dir.begin (); entry != dir.end (); ++entry) {
-
+ for (Glib::DirIterator entry = dir.begin(); entry != dir.end(); ++entry) {
const Glib::ustring fileName = *entry;
- if (fileName.size () < 4) {
+ if (fileName.size() < 4) {
continue;
}
- const Glib::ustring extension = fileName.substr (fileName.size () - 4).casefold ();
+ const Glib::ustring extension = rtengine::getFileExtension(fileName);
- if (extension.compare (".icc") != 0 && extension.compare (".icm") != 0) {
+ if (extension != "icc" && extension != "icm") {
continue;
}
- const Glib::ustring filePath = Glib::build_filename (dirName, fileName);
+ const Glib::ustring filePath = Glib::build_filename(dirName, fileName);
- if (!Glib::file_test (filePath, Glib::FILE_TEST_IS_REGULAR)) {
+ if (!Glib::file_test(filePath, Glib::FILE_TEST_IS_REGULAR)) {
continue;
}
- Glib::ustring name = fileName.substr (0, fileName.size() - 4);
+ Glib::ustring name = fileName.substr(0, fileName.size() - 4);
if (nameUpper) {
- name = name.uppercase ();
+ name = name.uppercase();
}
if (profiles) {
- const ProfileContent content (filePath);
- const cmsHPROFILE profile = content.toProfile ();
+ const rtengine::ProfileContent content(filePath);
+ const cmsHPROFILE profile = content.toProfile();
if (profile) {
- profiles->insert (std::make_pair (name, profile));
+ profiles->emplace(name, profile);
if (profileContents) {
- profileContents->insert (std::make_pair (name, content));
+ profileContents->emplace(name, content);
}
}
}
if (profileNames) {
- profileNames->insert (std::make_pair (name, filePath));
+ profileNames->emplace(name, filePath);
}
}
- } catch (Glib::Exception&) {}
+ } catch (Glib::Exception&) {
+ }
}
-inline void getSupportedIntent (cmsHPROFILE profile, cmsUInt32Number intent, cmsUInt32Number direction, uint8_t& result)
+// Version dedicated to single profile load when loadAll==false (cli version "-q" mode)
+bool loadProfile(
+ const Glib::ustring& profile,
+ const Glib::ustring& dirName,
+ std::map* profiles,
+ std::map* profileContents
+)
{
- if (cmsIsIntentSupported (profile, intent, direction)) {
+ if (dirName.empty() || profiles == nullptr) {
+ return false;
+ }
+
+ try {
+ Glib::Dir dir(dirName);
+
+ for (Glib::DirIterator entry = dir.begin(); entry != dir.end(); ++entry) {
+ const Glib::ustring fileName = *entry;
+
+ if (fileName.size() < 4) {
+ continue;
+ }
+
+ const Glib::ustring extension = rtengine::getFileExtension(fileName);
+
+ if (extension != "icc" && extension != "icm") {
+ continue;
+ }
+
+ const Glib::ustring filePath = Glib::build_filename(dirName, fileName);
+
+ if (!Glib::file_test(filePath, Glib::FILE_TEST_IS_REGULAR)) {
+ continue;
+ }
+
+ const Glib::ustring name = fileName.substr(0, fileName.size() - 4);
+
+ if (name == profile) {
+ const rtengine::ProfileContent content(filePath);
+ const cmsHPROFILE profile = content.toProfile();
+
+ if (profile) {
+ profiles->emplace(name, profile);
+
+ if (profileContents) {
+ profileContents->emplace(name, content);
+ }
+ return true;
+ }
+ }
+ }
+ } catch (Glib::Exception&) {
+ }
+
+ return false;
+}
+
+void getSupportedIntent(cmsHPROFILE profile, cmsUInt32Number intent, cmsUInt32Number direction, uint8_t& result)
+{
+ if (cmsIsIntentSupported(profile, intent, direction)) {
result |= 1 << intent;
}
}
-inline uint8_t getSupportedIntents (cmsHPROFILE profile, cmsUInt32Number direction)
+uint8_t getSupportedIntents(cmsHPROFILE profile, cmsUInt32Number direction)
{
if (!profile) {
return 0;
@@ -111,22 +178,22 @@ inline uint8_t getSupportedIntents (cmsHPROFILE profile, cmsUInt32Number directi
uint8_t result = 0;
- getSupportedIntent (profile, INTENT_PERCEPTUAL, direction, result);
- getSupportedIntent (profile, INTENT_RELATIVE_COLORIMETRIC, direction, result);
- getSupportedIntent (profile, INTENT_SATURATION, direction, result);
- getSupportedIntent (profile, INTENT_ABSOLUTE_COLORIMETRIC, direction, result);
+ getSupportedIntent(profile, INTENT_PERCEPTUAL, direction, result);
+ getSupportedIntent(profile, INTENT_RELATIVE_COLORIMETRIC, direction, result);
+ getSupportedIntent(profile, INTENT_SATURATION, direction, result);
+ getSupportedIntent(profile, INTENT_ABSOLUTE_COLORIMETRIC, direction, result);
return result;
}
-inline cmsHPROFILE createXYZProfile ()
+cmsHPROFILE createXYZProfile()
{
double mat[3][3] = { {1.0, 0, 0}, {0, 1.0, 0}, {0, 0, 1.0} };
- return ICCStore::createFromMatrix (mat, false, "XYZ");
+ return rtengine::ICCStore::createFromMatrix(mat, false, "XYZ");
}
-const double (*wprofiles[])[3] = {xyz_sRGB, xyz_adobe, xyz_prophoto, xyz_widegamut, xyz_bruce, xyz_beta, xyz_best, xyz_rec2020};
-const double (*iwprofiles[])[3] = {sRGB_xyz, adobe_xyz, prophoto_xyz, widegamut_xyz, bruce_xyz, beta_xyz, best_xyz, rec2020_xyz};
+const double(*wprofiles[])[3] = {xyz_sRGB, xyz_adobe, xyz_prophoto, xyz_widegamut, xyz_bruce, xyz_beta, xyz_best, xyz_rec2020};
+const double(*iwprofiles[])[3] = {sRGB_xyz, adobe_xyz, prophoto_xyz, widegamut_xyz, bruce_xyz, beta_xyz, best_xyz, rec2020_xyz};
const char* wpnames[] = {"sRGB", "Adobe RGB", "ProPhoto", "WideGamut", "BruceRGB", "Beta RGB", "BestRGB", "Rec2020"};
const char* wpgamma[] = {"default", "BT709_g2.2_s4.5", "sRGB_g2.4_s12.92", "linear_g1.0", "standard_g2.2", "standard_g1.8", "High_g1.3_s3.35", "Low_g2.6_s6.9"}; //gamma free
//default = gamma inside profile
@@ -138,243 +205,566 @@ const char* wpgamma[] = {"default", "BT709_g2.2_s4.5", "sRGB_g2.4_s12.92", "line
}
-namespace rtengine
+rtengine::ProfileContent::ProfileContent() = default;
+
+rtengine::ProfileContent::ProfileContent(const Glib::ustring& fileName)
{
+ FILE* const f = g_fopen(fileName.c_str(), "rb");
-std::vector getGamma ()
-{
-
- std::vector res;
-
- for (unsigned int i = 0; i < sizeof (wpgamma) / sizeof (wpgamma[0]); i++) {
- res.push_back (wpgamma[i]);
+ if (!f) {
+ return;
}
- return res;
+ fseek(f, 0, SEEK_END);
+ const long length = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ char* d = new char[length + 1];
+ fread(d, length, 1, f);
+ d[length] = 0;
+ fclose(f);
+
+ data.assign(d, length);
+ delete[] d;
}
-std::vector getWorkingProfiles ()
+rtengine::ProfileContent::ProfileContent(cmsHPROFILE hProfile)
{
+ if (hProfile != nullptr) {
+ cmsUInt32Number bytesNeeded = 0;
+ cmsSaveProfileToMem(hProfile, nullptr, &bytesNeeded);
- std::vector res;
-
- for (unsigned int i = 0; i < sizeof (wpnames) / sizeof (wpnames[0]); i++) {
- res.push_back (wpnames[i]);
+ if (bytesNeeded > 0) {
+ char* d = new char[bytesNeeded + 1];
+ cmsSaveProfileToMem(hProfile, d, &bytesNeeded);
+ data.assign(d, bytesNeeded);
+ delete[] d;
+ }
}
-
- return res;
}
-std::vector ICCStore::getProfiles (const ProfileType type) const
+cmsHPROFILE rtengine::ProfileContent::toProfile() const
{
- MyMutex::MyLock lock (mutex_);
+ return
+ !data.empty()
+ ? cmsOpenProfileFromMem(data.c_str(), data.size())
+ : nullptr;
+}
- std::vector res;
+const std::string& rtengine::ProfileContent::getData() const
+{
+ return data;
+}
- for (const auto profile : fileProfiles) {
- if ( (type==ICCStore::ProfileType::MONITOR
- && cmsGetDeviceClass(profile.second) == cmsSigDisplayClass
- && cmsGetColorSpace (profile.second) == cmsSigRgbData)
- || (type==ICCStore::ProfileType::PRINTER
- && cmsGetDeviceClass(profile.second) == cmsSigOutputClass)
- || (type==ICCStore::ProfileType::OUTPUT
- && (cmsGetDeviceClass(profile.second) == cmsSigDisplayClass || cmsGetDeviceClass(profile.second) == cmsSigOutputClass)
- && cmsGetColorSpace (profile.second) == cmsSigRgbData)
- )
- {
- res.push_back (profile.first);
+class rtengine::ICCStore::Implementation
+{
+public:
+ Implementation() :
+ loadAll(true),
+ xyz(createXYZProfile()),
+ srgb(cmsCreate_sRGBProfile())
+ {
+ //cmsErrorAction(LCMS_ERROR_SHOW);
+
+ constexpr int N = sizeof(wpnames) / sizeof(wpnames[0]);
+
+ for (int i = 0; i < N; ++i) {
+ wProfiles[wpnames[i]] = createFromMatrix(wprofiles[i]);
+ wProfilesGamma[wpnames[i]] = createFromMatrix(wprofiles[i], true);
+ wMatrices[wpnames[i]] = wprofiles[i];
+ iwMatrices[wpnames[i]] = iwprofiles[i];
}
}
- return res;
-}
+ void init(const Glib::ustring& usrICCDir, const Glib::ustring& rtICCDir, bool loadAll)
+ {
+ // Reads all profiles from the given profiles dir
-std::vector ICCStore::getProfilesFromDir (const Glib::ustring& dirName) const
-{
+ MyMutex::MyLock lock(mutex);
- MyMutex::MyLock lock (mutex_);
+ this->loadAll = loadAll;
- std::vector res;
+ // RawTherapee's profiles take precedence if a user's profile of the same name exists
+ profilesDir = Glib::build_filename(rtICCDir, "output");
+ userICCDir = usrICCDir;
+ fileProfiles.clear();
+ fileProfileContents.clear();
+ if (loadAll) {
+ loadProfiles(profilesDir, &fileProfiles, &fileProfileContents, nullptr, false);
+ loadProfiles(userICCDir, &fileProfiles, &fileProfileContents, nullptr, false);
+ }
- ProfileMap profiles;
-
- loadProfiles (profilesDir, &profiles, nullptr, nullptr, false);
- loadProfiles (dirName, &profiles, nullptr, nullptr, false);
-
- for (ProfileMap::const_iterator profile = profiles.begin (); profile != profiles.end (); ++profile) {
- res.push_back (profile->first);
- }
-
- return res;
-}
-
-cmsHPROFILE ICCStore::makeStdGammaProfile (cmsHPROFILE iprof)
-{
- // forgive me for the messy code, quick hack to change gamma of an ICC profile to the RT standard gamma
- if (!iprof) {
- return nullptr;
- }
-
- cmsUInt32Number bytesNeeded = 0;
- cmsSaveProfileToMem (iprof, nullptr, &bytesNeeded);
-
- if (bytesNeeded == 0) {
- return nullptr;
- }
-
- uint8_t *data = new uint8_t[bytesNeeded + 1];
- cmsSaveProfileToMem (iprof, data, &bytesNeeded);
- const uint8_t *p = &data[128]; // skip 128 byte header
- uint32_t tag_count;
- memcpy (&tag_count, p, 4);
- p += 4;
- tag_count = ntohl (tag_count);
-
- struct icctag {
- uint32_t sig;
- uint32_t offset;
- uint32_t size;
- } tags[tag_count];
-
- const uint32_t gamma = 0x239;
- int gamma_size = (gamma == 0 || gamma == 256) ? 12 : 14;
- int data_size = (gamma_size + 3) & ~3;
-
- for (uint32_t i = 0; i < tag_count; i++) {
- memcpy (&tags[i], p, 12);
- tags[i].sig = ntohl (tags[i].sig);
- tags[i].offset = ntohl (tags[i].offset);
- tags[i].size = ntohl (tags[i].size);
- p += 12;
-
- if (tags[i].sig != 0x62545243 && // bTRC
- tags[i].sig != 0x67545243 && // gTRC
- tags[i].sig != 0x72545243 && // rTRC
- tags[i].sig != 0x6B545243) { // kTRC
- data_size += (tags[i].size + 3) & ~3;
+ // Input profiles
+ // Load these to different areas, since the short name(e.g. "NIKON D700" may overlap between system/user and RT dir)
+ stdProfilesDir = Glib::build_filename(rtICCDir, "input");
+ fileStdProfiles.clear();
+ fileStdProfilesFileNames.clear();
+ if (loadAll) {
+ loadProfiles(stdProfilesDir, nullptr, nullptr, &fileStdProfilesFileNames, true);
}
}
- uint32_t sz = 128 + 4 + tag_count * 12 + data_size;
- uint8_t *nd = new uint8_t[sz];
- memset (nd, 0, sz);
- memcpy (nd, data, 128 + 4);
- sz = htonl (sz);
- memcpy (nd, &sz, 4);
- uint32_t offset = 128 + 4 + tag_count * 12;
- uint32_t gamma_offset = 0;
+ void findDefaultMonitorProfile()
+ {
+ // Determine the first monitor default profile of operating system, if selected
- for (uint32_t i = 0; i < tag_count; i++) {
- struct icctag tag;
- tag.sig = htonl (tags[i].sig);
+ defaultMonitorProfile.clear();
- if (tags[i].sig == 0x62545243 || // bTRC
- tags[i].sig == 0x67545243 || // gTRC
- tags[i].sig == 0x72545243 || // rTRC
- tags[i].sig == 0x6B545243) { // kTRC
- if (gamma_offset == 0) {
- gamma_offset = offset;
- uint32_t pcurve[] = { htonl(0x63757276), htonl(0), htonl(gamma_size == 12 ? 0U : 1U) };
- memcpy (&nd[offset], pcurve, 12);
+ #ifdef WIN32
+ // Get current main monitor. Could be fine tuned to get the current windows monitor(multi monitor setup),
+ // but problem is that we live in RTEngine with no GUI window to query around
+ HDC hDC = GetDC(nullptr);
- if (gamma_size == 14) {
- uint16_t gm = htons (gamma);
- memcpy (&nd[offset + 12], &gm, 2);
+ if (hDC != nullptr) {
+ if (SetICMMode(hDC, ICM_ON)) {
+ char profileName[MAX_PATH + 1];
+ DWORD profileLength = MAX_PATH;
+
+ if (GetICMProfileA(hDC, &profileLength, profileName)) {
+ defaultMonitorProfile = Glib::ustring(profileName);
+ defaultMonitorProfile = Glib::path_get_basename(defaultMonitorProfile);
+ size_t pos = defaultMonitorProfile.rfind(".");
+
+ if (pos != Glib::ustring::npos) {
+ defaultMonitorProfile = defaultMonitorProfile.substr(0, pos);
+ }
}
- offset += (gamma_size + 3) & ~3;
+ // might fail if e.g. the monitor has no profile
}
- tag.offset = htonl (gamma_offset);
- tag.size = htonl (gamma_size);
- } else {
- tag.offset = htonl (offset);
- tag.size = htonl (tags[i].size);
- memcpy (&nd[offset], &data[tags[i].offset], tags[i].size);
- offset += (tags[i].size + 3) & ~3;
+ ReleaseDC(NULL, hDC);
}
- memcpy (&nd[128 + 4 + i * 12], &tag, 12);
+ #else
+ // TODO: Add other OS specific code here
+ #endif
+
+ if (options.rtSettings.verbose) {
+ printf("Default monitor profile is: %s\n", defaultMonitorProfile.c_str());
+ }
}
- cmsHPROFILE oprof = cmsOpenProfileFromMem (nd, ntohl (sz));
- delete [] nd;
- delete [] data;
- return oprof;
-}
-
-ICCStore* ICCStore::getInstance ()
-{
- static ICCStore instance_;
- return &instance_;
-}
-
-ICCStore::ICCStore () :
- xyz (createXYZProfile ()),
- srgb (cmsCreate_sRGBProfile ())
-{
- //cmsErrorAction (LCMS_ERROR_SHOW);
-
- int N = sizeof (wpnames) / sizeof (wpnames[0]);
-
- for (int i = 0; i < N; i++) {
- wProfiles[wpnames[i]] = createFromMatrix (wprofiles[i]);
- wProfilesGamma[wpnames[i]] = createFromMatrix (wprofiles[i], true);
- wMatrices[wpnames[i]] = wprofiles[i];
- iwMatrices[wpnames[i]] = iwprofiles[i];
+ cmsHPROFILE getDefaultMonitorProfile()
+ {
+ return getProfile(defaultMonitorProfile);
}
+
+ Glib::ustring getDefaultMonitorProfileName() const
+ {
+ return defaultMonitorProfile;
+ }
+
+ cmsHPROFILE workingSpace(const Glib::ustring& name) const
+ {
+ const ProfileMap::const_iterator r = wProfiles.find(name);
+
+ return
+ r != wProfiles.end()
+ ? r->second
+ : wProfiles.find("sRGB")->second;
+ }
+
+ cmsHPROFILE workingSpaceGamma(const Glib::ustring& name) const
+ {
+
+ const ProfileMap::const_iterator r = wProfilesGamma.find(name);
+
+ return
+ r != wProfilesGamma.end()
+ ? r->second
+ : wProfilesGamma.find("sRGB")->second;
+ }
+
+ TMatrix workingSpaceMatrix(const Glib::ustring& name) const
+ {
+ const MatrixMap::const_iterator r = wMatrices.find(name);
+
+ return
+ r != wMatrices.end()
+ ? r->second
+ : wMatrices.find("sRGB")->second;
+ }
+
+ TMatrix workingSpaceInverseMatrix(const Glib::ustring& name) const
+ {
+
+ const MatrixMap::const_iterator r = iwMatrices.find(name);
+
+ return
+ r != iwMatrices.end()
+ ? r->second
+ : iwMatrices.find("sRGB")->second;
+ }
+
+ bool outputProfileExist(const Glib::ustring& name) const
+ {
+ MyMutex::MyLock lock(mutex);
+ return fileProfiles.find(name) != fileProfiles.end();
+ }
+
+ cmsHPROFILE getProfile(const Glib::ustring& name)
+ {
+ MyMutex::MyLock lock(mutex);
+
+ const ProfileMap::const_iterator r = fileProfiles.find(name);
+
+ if (r != fileProfiles.end()) {
+ return r->second;
+ }
+
+ if (!name.compare(0, 5, "file:")) {
+ const ProfileContent content(name.substr(5));
+ const cmsHPROFILE profile = content.toProfile();
+
+ if (profile) {
+ fileProfiles.emplace(name, profile);
+ fileProfileContents.emplace(name, content);
+
+ return profile;
+ }
+ } else if (!loadAll) {
+ // Look for a standard profile
+ if (!loadProfile(name, profilesDir, &fileProfiles, &fileProfileContents)) {
+ loadProfile(name, userICCDir, &fileProfiles, &fileProfileContents);
+ }
+ const ProfileMap::const_iterator r = fileProfiles.find(name);
+ if (r != fileProfiles.end()) {
+ return r->second;
+ }
+ }
+
+ return nullptr;
+ }
+
+ cmsHPROFILE getStdProfile(const Glib::ustring& name)
+ {
+ const Glib::ustring nameUpper = name.uppercase();
+
+ MyMutex::MyLock lock(mutex);
+
+ const ProfileMap::const_iterator r = fileStdProfiles.find(nameUpper);
+
+ // Return profile from store
+ if (r != fileStdProfiles.end()) {
+ return r->second;
+ } else if (!loadAll) {
+ // Directory not scanned, so looking and adding now...
+ if (!loadProfile(name, profilesDir, &fileProfiles, &fileProfileContents)) {
+ loadProfile(name, userICCDir, &fileProfiles, &fileProfileContents);
+ }
+ const ProfileMap::const_iterator r = fileProfiles.find(name);
+ if (r != fileProfiles.end()) {
+ return r->second;
+ }
+ }
+
+ // Profile is not yet in store
+ const NameMap::const_iterator f = fileStdProfilesFileNames.find(nameUpper);
+
+ // Profile does not exist
+ if (f == fileStdProfilesFileNames.end()) {
+ return nullptr;
+ }
+
+ // But there exists one --> load it
+ const ProfileContent content(f->second);
+ const cmsHPROFILE profile = content.toProfile();
+
+ if (profile) {
+ fileStdProfiles.emplace(f->first, profile);
+ }
+
+ // Profile invalid or stored now --> remove entry from fileStdProfilesFileNames
+ fileStdProfilesFileNames.erase(f);
+ return profile;
+ }
+
+ ProfileContent getContent(const Glib::ustring& name) const
+ {
+ MyMutex::MyLock lock(mutex);
+
+ const ContentMap::const_iterator r = fileProfileContents.find(name);
+
+ return
+ r != fileProfileContents.end()
+ ? r->second
+ : ProfileContent();
+ }
+
+ cmsHPROFILE getXYZProfile() const
+ {
+ return xyz;
+ }
+
+ cmsHPROFILE getsRGBProfile() const
+ {
+ return srgb;
+ }
+
+ std::vector getProfiles(ProfileType type) const
+ {
+ std::vector res;
+
+ MyMutex::MyLock lock(mutex);
+
+ for (const auto profile : fileProfiles) {
+ if (
+ (
+ type==ICCStore::ProfileType::MONITOR
+ && cmsGetDeviceClass(profile.second) == cmsSigDisplayClass
+ && cmsGetColorSpace(profile.second) == cmsSigRgbData
+ )
+ ||(
+ type==ICCStore::ProfileType::PRINTER
+ && cmsGetDeviceClass(profile.second) == cmsSigOutputClass
+ )
+ ||(
+ type==ICCStore::ProfileType::OUTPUT
+ && (cmsGetDeviceClass(profile.second) == cmsSigDisplayClass || cmsGetDeviceClass(profile.second) == cmsSigOutputClass)
+ && cmsGetColorSpace(profile.second) == cmsSigRgbData
+ )
+ ) {
+ res.push_back(profile.first);
+ }
+ }
+
+ return res;
+ }
+
+ std::vector getProfilesFromDir(const Glib::ustring& dirName) const
+ {
+ std::vector res;
+ ProfileMap profiles;
+
+ MyMutex::MyLock lock(mutex);
+
+ loadProfiles(profilesDir, &profiles, nullptr, nullptr, false);
+ loadProfiles(dirName, &profiles, nullptr, nullptr, false);
+
+ for (const auto& profile : profiles) {
+ res.push_back(profile.first);
+ }
+
+ return res;
+ }
+
+ std::uint8_t getInputIntents(cmsHPROFILE profile)
+ {
+ MyMutex::MyLock lock(mutex);
+
+ return getSupportedIntents(profile, LCMS_USED_AS_INPUT);
+ }
+
+ std::uint8_t getOutputIntents(cmsHPROFILE profile)
+ {
+ MyMutex::MyLock lock(mutex);
+
+ return getSupportedIntents(profile, LCMS_USED_AS_OUTPUT);
+ }
+
+ std::uint8_t getProofIntents(cmsHPROFILE profile)
+ {
+ MyMutex::MyLock lock(mutex);
+
+ return getSupportedIntents(profile, LCMS_USED_AS_PROOF);
+ }
+
+ std::uint8_t getInputIntents(const Glib::ustring &name)
+ {
+ return getInputIntents(getProfile(name));
+ }
+
+ std::uint8_t getOutputIntents(const Glib::ustring &name)
+ {
+ return getOutputIntents(getProfile(name));
+ }
+
+ std::uint8_t getProofIntents(const Glib::ustring &name)
+ {
+ return getProofIntents(getProfile(name));
+ }
+
+private:
+ using ProfileMap = std::map;
+ using MatrixMap = std::map;
+ using ContentMap = std::map;
+ using NameMap = std::map;
+
+ ProfileMap wProfiles;
+ ProfileMap wProfilesGamma;
+ MatrixMap wMatrices;
+ MatrixMap iwMatrices;
+
+ // These contain profiles from user/system directory(supplied on init)
+ Glib::ustring profilesDir;
+ Glib::ustring userICCDir;
+ ProfileMap fileProfiles;
+ ContentMap fileProfileContents;
+
+ //These contain standard profiles from RT. Keys are all in uppercase.
+ Glib::ustring stdProfilesDir;
+ NameMap fileStdProfilesFileNames;
+ ProfileMap fileStdProfiles;
+
+ Glib::ustring defaultMonitorProfile;
+
+ bool loadAll;
+
+ const cmsHPROFILE xyz;
+ const cmsHPROFILE srgb;
+
+ mutable MyMutex mutex;
+};
+
+rtengine::ICCStore* rtengine::ICCStore::getInstance()
+{
+ static rtengine::ICCStore instance;
+ return &instance;
}
-TMatrix ICCStore::workingSpaceMatrix (const Glib::ustring& name) const
+void rtengine::ICCStore::init(const Glib::ustring& usrICCDir, const Glib::ustring& stdICCDir, bool loadAll)
+{
+ implementation->init(usrICCDir, stdICCDir, loadAll);
+}
+
+void rtengine::ICCStore::findDefaultMonitorProfile()
+{
+ implementation->findDefaultMonitorProfile();
+}
+
+cmsHPROFILE rtengine::ICCStore::getDefaultMonitorProfile() const
+{
+ return implementation->getDefaultMonitorProfile();
+}
+
+Glib::ustring rtengine::ICCStore::getDefaultMonitorProfileName() const
+{
+ return implementation->getDefaultMonitorProfileName();
+}
+
+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);
+}
+
+rtengine::TMatrix rtengine::ICCStore::workingSpaceMatrix(const Glib::ustring& name) const
+{
+ return implementation->workingSpaceMatrix(name);
+}
+
+rtengine::TMatrix rtengine::ICCStore::workingSpaceInverseMatrix(const Glib::ustring& name) const
+{
+ return implementation->workingSpaceInverseMatrix(name);
+}
+
+bool rtengine::ICCStore::outputProfileExist(const Glib::ustring& name) const
+{
+ return implementation->outputProfileExist(name);
+}
+
+cmsHPROFILE rtengine::ICCStore::getProfile(const Glib::ustring& name) const
+{
+ return implementation->getProfile(name);
+}
+
+cmsHPROFILE rtengine::ICCStore::getStdProfile(const Glib::ustring& name) const
+{
+ return implementation->getStdProfile(name);
+}
+
+rtengine::ProfileContent rtengine::ICCStore::getContent(const Glib::ustring& name) const
+{
+ return implementation->getContent(name);
+}
+
+cmsHPROFILE rtengine::ICCStore::getXYZProfile() const
+{
+ return implementation->getXYZProfile();
+}
+
+cmsHPROFILE rtengine::ICCStore::getsRGBProfile() const
+{
+ return implementation->getsRGBProfile();
+}
+
+std::vector rtengine::ICCStore::getProfiles(ProfileType type) const
+{
+ return implementation->getProfiles(type);
+}
+
+std::vector rtengine::ICCStore::getProfilesFromDir(const Glib::ustring& dirName) const
+{
+ return implementation->getProfilesFromDir(dirName);
+}
+
+std::uint8_t rtengine::ICCStore::getInputIntents(cmsHPROFILE profile) const
+{
+ return implementation->getInputIntents(profile);
+}
+
+std::uint8_t rtengine::ICCStore::getOutputIntents(cmsHPROFILE profile) const
+{
+ return implementation->getOutputIntents(profile);
+}
+
+std::uint8_t rtengine::ICCStore::getProofIntents(cmsHPROFILE profile) const
+{
+ return implementation->getProofIntents(profile);
+}
+
+std::uint8_t rtengine::ICCStore::getInputIntents(const Glib::ustring& name) const
+{
+ return implementation->getInputIntents(name);
+}
+
+std::uint8_t rtengine::ICCStore::getOutputIntents(const Glib::ustring& name) const
+{
+ return implementation->getOutputIntents(name);
+}
+
+std::uint8_t rtengine::ICCStore::getProofIntents(const Glib::ustring& name) const
+{
+ return implementation->getProofIntents(name);
+}
+
+rtengine::ICCStore::ICCStore() :
+ implementation(new Implementation)
+{
+}
+
+rtengine::ICCStore::~ICCStore() = default;
+
+std::vector rtengine::ICCStore::getWorkingProfiles()
{
- const MatrixMap::const_iterator r = wMatrices.find (name);
+ std::vector res;
- if (r != wMatrices.end()) {
- return r->second;
- } else {
- return wMatrices.find ("sRGB")->second;
+ for (unsigned int i = 0; i < sizeof(wpnames) / sizeof(wpnames[0]); i++) {
+ res.push_back(wpnames[i]);
}
+
+ return res;
}
-TMatrix ICCStore::workingSpaceInverseMatrix (const Glib::ustring& name) const
+std::vector rtengine::ICCStore::getGamma()
{
- const MatrixMap::const_iterator r = iwMatrices.find (name);
+ std::vector res;
- if (r != iwMatrices.end()) {
- return r->second;
- } else {
- return iwMatrices.find ("sRGB")->second;
+ for (unsigned int i = 0; i < sizeof(wpgamma) / sizeof(wpgamma[0]); i++) {
+ res.push_back(wpgamma[i]);
}
+
+ return res;
}
-cmsHPROFILE ICCStore::workingSpace (const Glib::ustring& name) const
-{
-
- const ProfileMap::const_iterator r = wProfiles.find (name);
-
- if (r != wProfiles.end()) {
- return r->second;
- } else {
- return wProfiles.find ("sRGB")->second;
- }
-}
-
-cmsHPROFILE ICCStore::workingSpaceGamma (const Glib::ustring& name) const
-{
-
- const ProfileMap::const_iterator r = wProfilesGamma.find (name);
-
- if (r != wProfilesGamma.end()) {
- return r->second;
- } else {
- return wProfilesGamma.find ("sRGB")->second;
- }
-}
-
-void ICCStore::getGammaArray(const procparams::ColorManagementParams &icm, GammaValues &ga)
+void rtengine::ICCStore::getGammaArray(const procparams::ColorManagementParams &icm, GammaValues &ga)
{
const double eps = 0.000000001; // not divide by zero
if (!icm.freegamma) {//if Free gamma not selected
@@ -404,19 +794,19 @@ void ICCStore::getGammaArray(const procparams::ColorManagementParams &icm, Gamma
ga[3] = 0.144928;
ga[4] = 0.076332;
} else if (icm.gamma == "standard_g2.2") {
- ga[0] = 2.2; //gamma=2.2 (as gamma of Adobe, Widegamut...)
+ ga[0] = 2.2; //gamma=2.2(as gamma of Adobe, Widegamut...)
ga[1] = 1.;
ga[2] = 0.;
ga[3] = 1. / eps;
ga[4] = 0.;
} else if (icm.gamma == "standard_g1.8") {
- ga[0] = 1.8; //gamma=1.8 (as gamma of Prophoto)
+ ga[0] = 1.8; //gamma=1.8(as gamma of Prophoto)
ga[1] = 1.;
ga[2] = 0.;
ga[3] = 1. / eps;
ga[4] = 0.;
} else /* if (icm.gamma == "linear_g1.0") */ {
- ga[0] = 1.0; //gamma=1 linear : for high dynamic images (cf : D.Coffin...)
+ ga[0] = 1.0; //gamma=1 linear : for high dynamic images(cf : D.Coffin...)
ga[1] = 1.;
ga[2] = 0.;
ga[3] = 1. / eps;
@@ -435,8 +825,8 @@ void ICCStore::getGammaArray(const procparams::ColorManagementParams &icm, Gamma
ga[4] = g_a[3] * ts;
//printf("g_a.gamma0=%f g_a.gamma1=%f g_a.gamma2=%f g_a.gamma3=%f g_a.gamma4=%f\n", g_a.gamma0,g_a.gamma1,g_a.gamma2,g_a.gamma3,g_a.gamma4);
ga[0] = icm.gampos;
- ga[1] = 1. / (1.0 + g_a[4]);
- ga[2] = g_a[4] / (1.0 + g_a[4]);
+ ga[1] = 1. /(1.0 + g_a[4]);
+ ga[2] = g_a[4] /(1.0 + g_a[4]);
ga[3] = 1. / slope;
ga[5] = 0.0;
ga[6] = 0.0;
@@ -445,7 +835,198 @@ void ICCStore::getGammaArray(const procparams::ColorManagementParams &icm, Gamma
}
// WARNING: the caller must lock lcmsMutex
-cmsHPROFILE ICCStore::createGammaProfile (const procparams::ColorManagementParams &icm, GammaValues &ga) {
+cmsHPROFILE rtengine::ICCStore::makeStdGammaProfile(cmsHPROFILE iprof)
+{
+ // forgive me for the messy code, quick hack to change gamma of an ICC profile to the RT standard gamma
+ if (!iprof) {
+ return nullptr;
+ }
+
+ cmsUInt32Number bytesNeeded = 0;
+ cmsSaveProfileToMem(iprof, nullptr, &bytesNeeded);
+
+ if (bytesNeeded == 0) {
+ return nullptr;
+ }
+
+ uint8_t *data = new uint8_t[bytesNeeded + 1];
+ cmsSaveProfileToMem(iprof, data, &bytesNeeded);
+ const uint8_t *p = &data[128]; // skip 128 byte header
+ uint32_t tag_count;
+ memcpy(&tag_count, p, 4);
+ p += 4;
+ tag_count = ntohl(tag_count);
+
+ struct icctag {
+ uint32_t sig;
+ uint32_t offset;
+ uint32_t size;
+ } tags[tag_count];
+
+ const uint32_t gamma = 0x239;
+ int gamma_size =(gamma == 0 || gamma == 256) ? 12 : 14;
+ int data_size =(gamma_size + 3) & ~3;
+
+ for (uint32_t i = 0; i < tag_count; i++) {
+ memcpy(&tags[i], p, 12);
+ tags[i].sig = ntohl(tags[i].sig);
+ tags[i].offset = ntohl(tags[i].offset);
+ tags[i].size = ntohl(tags[i].size);
+ p += 12;
+
+ if (tags[i].sig != 0x62545243 && // bTRC
+ tags[i].sig != 0x67545243 && // gTRC
+ tags[i].sig != 0x72545243 && // rTRC
+ tags[i].sig != 0x6B545243) { // kTRC
+ data_size +=(tags[i].size + 3) & ~3;
+ }
+ }
+
+ uint32_t sz = 128 + 4 + tag_count * 12 + data_size;
+ uint8_t *nd = new uint8_t[sz];
+ memset(nd, 0, sz);
+ memcpy(nd, data, 128 + 4);
+ sz = htonl(sz);
+ memcpy(nd, &sz, 4);
+ uint32_t offset = 128 + 4 + tag_count * 12;
+ uint32_t gamma_offset = 0;
+
+ for (uint32_t i = 0; i < tag_count; i++) {
+ struct icctag tag;
+ tag.sig = htonl(tags[i].sig);
+
+ if (tags[i].sig == 0x62545243 || // bTRC
+ tags[i].sig == 0x67545243 || // gTRC
+ tags[i].sig == 0x72545243 || // rTRC
+ tags[i].sig == 0x6B545243) { // kTRC
+ if (gamma_offset == 0) {
+ gamma_offset = offset;
+ uint32_t pcurve[] = { htonl(0x63757276), htonl(0), htonl(gamma_size == 12 ? 0U : 1U) };
+ memcpy(&nd[offset], pcurve, 12);
+
+ if (gamma_size == 14) {
+ uint16_t gm = htons(gamma);
+ memcpy(&nd[offset + 12], &gm, 2);
+ }
+
+ offset +=(gamma_size + 3) & ~3;
+ }
+
+ tag.offset = htonl(gamma_offset);
+ tag.size = htonl(gamma_size);
+ } else {
+ tag.offset = htonl(offset);
+ tag.size = htonl(tags[i].size);
+ memcpy(&nd[offset], &data[tags[i].offset], tags[i].size);
+ offset +=(tags[i].size + 3) & ~3;
+ }
+
+ memcpy(&nd[128 + 4 + i * 12], &tag, 12);
+ }
+
+ cmsHPROFILE oprof = cmsOpenProfileFromMem(nd, ntohl(sz));
+ delete [] nd;
+ delete [] data;
+ return oprof;
+}
+
+cmsHPROFILE rtengine::ICCStore::createFromMatrix(const double matrix[3][3], bool gamma, const Glib::ustring& name)
+{
+
+ static const unsigned phead[] = {
+ 1024, 0, 0x2100000, 0x6d6e7472, 0x52474220, 0x58595a20, 0, 0, 0,
+ 0x61637370, 0, 0, 0, 0, 0, 0, 0, 0xf6d6, 0x10000, 0xd32d
+ };
+ unsigned pbody[] = {
+ 10, 0x63707274, 0, 36, /* cprt */
+ 0x64657363, 0, 40, /* desc */
+ 0x77747074, 0, 20, /* wtpt */
+ 0x626b7074, 0, 20, /* bkpt */
+ 0x72545243, 0, 14, /* rTRC */
+ 0x67545243, 0, 14, /* gTRC */
+ 0x62545243, 0, 14, /* bTRC */
+ 0x7258595a, 0, 20, /* rXYZ */
+ 0x6758595a, 0, 20, /* gXYZ */
+ 0x6258595a, 0, 20
+ }; /* bXYZ */
+ static const unsigned pwhite[] = { 0xf351, 0x10000, 0x116cc };//D65
+ //static const unsigned pwhite[] = { 0xf6d6, 0x10000, 0xd340 };//D50
+
+ // 0x63757276 : curveType, 0 : reserved, 1 : entries(1=gamma, 0=identity), 0x1000000=1.0
+ unsigned pcurve[] = { 0x63757276, 0, 0, 0x1000000 };
+// unsigned pcurve[] = { 0x63757276, 0, 1, 0x1000000 };
+
+ if (gamma) {
+ pcurve[2] = 1;
+ // pcurve[3] = 0x1f00000;// pcurve for gamma BT709 : g=2.22 s=4.5
+ // normalize gamma in RT, default(Emil's choice = sRGB)
+ pcurve[3] = 0x2390000;//pcurve for gamma sRGB : g:2.4 s=12.92
+
+ } else {
+ // lcms2 up to 2.4 has a bug with linear gamma causing precision loss(banding)
+ // of floating point data when a normal icc encoding of linear gamma is used
+ //(i e 0 table entries), but by encoding a gamma curve which is 1.0 the
+ // floating point path is taken within lcms2 so no precision loss occurs and
+ // gamma is still 1.0.
+ pcurve[2] = 1;
+ pcurve[3] = 0x1000000; //pcurve for gamma 1
+ }
+
+ // constructing profile header
+ unsigned* oprof = new unsigned [phead[0] / sizeof(unsigned)];
+ memset(oprof, 0, phead[0]);
+ memcpy(oprof, phead, sizeof(phead));
+
+ oprof[0] = 132 + 12 * pbody[0];
+
+ // constructing tag directory(pointers inside the file), and types
+ // 0x74657874 : text
+ // 0x64657363 : description tag
+ for (unsigned int i = 0; i < pbody[0]; i++) {
+ oprof[oprof[0] / 4] = i ?(i > 1 ? 0x58595a20 : 0x64657363) : 0x74657874;
+ pbody[i * 3 + 2] = oprof[0];
+ oprof[0] +=(pbody[i * 3 + 3] + 3) & -4;
+ }
+
+ memcpy(oprof + 32, pbody, sizeof(pbody));
+
+ // wtpt
+ memcpy((char *)oprof + pbody[8] + 8, pwhite, sizeof(pwhite));
+
+ // r/g/b TRC
+ for (int i = 4; i < 7; i++) {
+ memcpy((char *)oprof + pbody[i * 3 + 2], pcurve, sizeof(pcurve));
+ }
+
+ // r/g/b XYZ
+// pseudoinverse((double(*)[3]) out_rgb[output_color-1], inverse, 3);
+ for (int i = 0; i < 3; i++)
+ for (int j = 0; j < 3; j++) {
+ oprof[pbody[j * 3 + 23] / 4 + i + 2] = matrix[i][j] * 0x10000 + 0.5;
+// for (num = k=0; k < 3; k++)
+// num += xyzd50_srgb[i][k] * inverse[j][k];
+ }
+
+ // convert to network byte order
+ for (unsigned int i = 0; i < phead[0] / 4; i++) {
+ oprof[i] = htonl(oprof[i]);
+ }
+
+ // cprt
+ strcpy((char *)oprof + pbody[2] + 8, "--rawtherapee profile--");
+
+ // desc
+ oprof[pbody[5] / 4 + 2] = name.size() + 1;
+ strcpy((char *)oprof + pbody[5] + 12, name.c_str());
+
+
+ cmsHPROFILE p = cmsOpenProfileFromMem(oprof, ntohl(oprof[0]));
+ delete [] oprof;
+ return p;
+}
+
+cmsHPROFILE rtengine::ICCStore::createGammaProfile(const procparams::ColorManagementParams &icm, GammaValues &ga)
+{
float p[6]; //primaries
ga[6] = 0.0;
@@ -530,49 +1111,50 @@ cmsHPROFILE ICCStore::createGammaProfile (const procparams::ColorManagementParam
// 7 parameters for smoother curves
cmsFloat64Number Parameters[7] = { ga[0], ga[1], ga[2], ga[3], ga[4], ga[5], ga[6] } ;
- //lcmsMutex->lock (); Mutex acquired by the caller
- cmsWhitePointFromTemp(&xyD, (double)temp);
+ //lcmsMutex->lock(); Mutex acquired by the caller
+ cmsWhitePointFromTemp(&xyD,(double)temp);
GammaTRC[0] = GammaTRC[1] = GammaTRC[2] = cmsBuildParametricToneCurve(nullptr, 5, Parameters); //5 = smoother than 4
cmsHPROFILE oprofdef = cmsCreateRGBProfile(&xyD, &Primaries, GammaTRC); //oprofdef become Outputprofile
cmsFreeToneCurve(GammaTRC[0]);
- //lcmsMutex->unlock ();
+ //lcmsMutex->unlock();
return oprofdef;
}
// WARNING: the caller must lock lcmsMutex
-cmsHPROFILE ICCStore::createCustomGammaOutputProfile (const procparams::ColorManagementParams &icm, GammaValues &ga) {
+cmsHPROFILE rtengine::ICCStore::createCustomGammaOutputProfile(const procparams::ColorManagementParams &icm, GammaValues &ga)
+{
bool pro = false;
Glib::ustring outProfile;
cmsHPROFILE outputProfile = nullptr;
if (icm.freegamma && icm.gampos < 1.35) {
pro = true; //select profil with gammaTRC modified :
- } else if (icm.gamma == "linear_g1.0" || (icm.gamma == "High_g1.3_s3.35")) {
+ } else if (icm.gamma == "linear_g1.0" ||(icm.gamma == "High_g1.3_s3.35")) {
pro = true; //pro=0 RT_sRGB || Prophoto
}
// Check that output profiles exist, otherwise use LCMS2
// Use the icc/icm profiles associated to possible working profiles, set in "options"
- if (icm.working == "ProPhoto" && iccStore->outputProfileExist(options.rtSettings.prophoto) && !pro) {
+ if (icm.working == "ProPhoto" && rtengine::ICCStore::getInstance()->outputProfileExist(options.rtSettings.prophoto) && !pro) {
outProfile = options.rtSettings.prophoto;
- } else if (icm.working == "Adobe RGB" && iccStore->outputProfileExist(options.rtSettings.adobe) ) {
+ } else if (icm.working == "Adobe RGB" && rtengine::ICCStore::getInstance()->outputProfileExist(options.rtSettings.adobe) ) {
outProfile = options.rtSettings.adobe;
- } else if (icm.working == "WideGamut" && iccStore->outputProfileExist(options.rtSettings.widegamut) ) {
+ } else if (icm.working == "WideGamut" && rtengine::ICCStore::getInstance()->outputProfileExist(options.rtSettings.widegamut) ) {
outProfile = options.rtSettings.widegamut;
- } else if (icm.working == "Beta RGB" && iccStore->outputProfileExist(options.rtSettings.beta) ) {
+ } else if (icm.working == "Beta RGB" && rtengine::ICCStore::getInstance()->outputProfileExist(options.rtSettings.beta) ) {
outProfile = options.rtSettings.beta;
- } else if (icm.working == "BestRGB" && iccStore->outputProfileExist(options.rtSettings.best) ) {
+ } else if (icm.working == "BestRGB" && rtengine::ICCStore::getInstance()->outputProfileExist(options.rtSettings.best) ) {
outProfile = options.rtSettings.best;
- } else if (icm.working == "BruceRGB" && iccStore->outputProfileExist(options.rtSettings.bruce) ) {
+ } else if (icm.working == "BruceRGB" && rtengine::ICCStore::getInstance()->outputProfileExist(options.rtSettings.bruce) ) {
outProfile = options.rtSettings.bruce;
- } else if (icm.working == "sRGB" && iccStore->outputProfileExist(options.rtSettings.srgb) && !pro) {
+ } else if (icm.working == "sRGB" && rtengine::ICCStore::getInstance()->outputProfileExist(options.rtSettings.srgb) && !pro) {
outProfile = options.rtSettings.srgb;
- } else if (icm.working == "sRGB" && iccStore->outputProfileExist(options.rtSettings.srgb10) && pro) {
+ } else if (icm.working == "sRGB" && rtengine::ICCStore::getInstance()->outputProfileExist(options.rtSettings.srgb10) && pro) {
outProfile = options.rtSettings.srgb10;
- } else if (icm.working == "ProPhoto" && iccStore->outputProfileExist(options.rtSettings.prophoto10) && pro) {
+ } else if (icm.working == "ProPhoto" && rtengine::ICCStore::getInstance()->outputProfileExist(options.rtSettings.prophoto10) && pro) {
outProfile = options.rtSettings.prophoto10;
- } else if (icm.working == "Rec2020" && iccStore->outputProfileExist(options.rtSettings.rec2020) ) {
+ } else if (icm.working == "Rec2020" && rtengine::ICCStore::getInstance()->outputProfileExist(options.rtSettings.rec2020) ) {
outProfile = options.rtSettings.rec2020;
} else {
// Should not occurs
@@ -589,7 +1171,7 @@ cmsHPROFILE ICCStore::createCustomGammaOutputProfile (const procparams::ColorMan
printf("Output Gamma - profile: \"%s\"\n", outProfile.c_str() ); //c_str()
}
- outputProfile = iccStore->getProfile(outProfile); //get output profile
+ outputProfile = ICCStore::getInstance()->getProfile(outProfile); //get output profile
if (outputProfile == nullptr) {
@@ -637,7 +1219,7 @@ cmsHPROFILE ICCStore::createCustomGammaOutputProfile (const procparams::ColorMan
// create description with gamma + slope + primaries
std::wostringstream gammaWs;
gammaWs.precision(2);
- gammaWs << "Manual GammaTRC: g=" << (float)icm.gampos << " s=" << (float)icm.slpos;
+ gammaWs << "Manual GammaTRC: g=" <<(float)icm.gampos << " s=" <<(float)icm.slpos;
cmsMLUsetWide(mlu, "en", "US", gammaWs.str().c_str());
}
@@ -656,15 +1238,15 @@ cmsHPROFILE ICCStore::createCustomGammaOutputProfile (const procparams::ColorMan
*/
- cmsMLUfree (mlu);
+ cmsMLUfree(mlu);
}
// Calculate output profile's rTRC gTRC bTRC
cmsToneCurve* GammaTRC = nullptr;
GammaTRC = cmsBuildParametricToneCurve(nullptr, 5, Parameters);
- cmsWriteTag(outputProfile, cmsSigRedTRCTag, (void*)GammaTRC );
- cmsWriteTag(outputProfile, cmsSigGreenTRCTag, (void*)GammaTRC );
- cmsWriteTag(outputProfile, cmsSigBlueTRCTag, (void*)GammaTRC );
+ cmsWriteTag(outputProfile, cmsSigRedTRCTag,(void*)GammaTRC );
+ cmsWriteTag(outputProfile, cmsSigGreenTRCTag,(void*)GammaTRC );
+ cmsWriteTag(outputProfile, cmsSigBlueTRCTag,(void*)GammaTRC );
if (GammaTRC) {
cmsFreeToneCurve(GammaTRC);
@@ -672,334 +1254,3 @@ cmsHPROFILE ICCStore::createCustomGammaOutputProfile (const procparams::ColorMan
return outputProfile;
}
-
-bool ICCStore::outputProfileExist (const Glib::ustring& name) const
-{
-
- MyMutex::MyLock lock(mutex_);
- return fileProfiles.find(name) != fileProfiles.end();
-}
-
-cmsHPROFILE ICCStore::getProfile (const Glib::ustring& name) const
-{
-
- MyMutex::MyLock lock (mutex_);
-
- const ProfileMap::const_iterator r = fileProfiles.find (name);
-
- if (r != fileProfiles.end ()) {
- return r->second;
- }
-
- if (name.compare (0, 5, "file:") == 0) {
- const ProfileContent content (name.substr (5));
- const cmsHPROFILE profile = content.toProfile ();
-
- if (profile) {
- const_cast (fileProfiles).insert (std::make_pair (name, profile));
- const_cast (fileProfileContents).insert (std::make_pair (name, content));
-
- return profile;
- }
- }
-
- return nullptr;
-}
-
-cmsHPROFILE ICCStore::getStdProfile (const Glib::ustring& name) const
-{
-
- const Glib::ustring nameUpper = name.uppercase ();
-
- MyMutex::MyLock lock (mutex_);
-
- const ProfileMap::const_iterator r = fileStdProfiles.find (nameUpper);
-
- // return profile from store
- if (r != fileStdProfiles.end ()) {
- return r->second;
- }
-
- // profile is not yet in store
- const NameMap::const_iterator f = fileStdProfilesFileNames.find (nameUpper);
-
- // profile does not exist
- if (f == fileStdProfilesFileNames.end ()) {
- return nullptr;
- }
-
- // but there exists one => load it
- const ProfileContent content (f->second);
- const cmsHPROFILE profile = content.toProfile ();
-
- if (profile) {
- const_cast (fileStdProfiles).insert (std::make_pair (f->first, profile));
- }
-
- // profile is not valid or it is now stored => remove entry from fileStdProfilesFileNames
- const_cast (fileStdProfilesFileNames).erase (f);
- return profile;
-}
-
-ProfileContent ICCStore::getContent (const Glib::ustring& name) const
-{
-
- MyMutex::MyLock lock (mutex_);
-
- const ContentMap::const_iterator r = fileProfileContents.find (name);
-
- return r != fileProfileContents.end () ? r->second : ProfileContent();
-}
-
-uint8_t ICCStore::getInputIntents (cmsHPROFILE profile) const
-{
- MyMutex::MyLock lock (mutex_);
-
- return getSupportedIntents (profile, LCMS_USED_AS_INPUT);
-}
-
-uint8_t ICCStore::getOutputIntents (cmsHPROFILE profile) const
-{
- MyMutex::MyLock lock (mutex_);
-
- return getSupportedIntents (profile, LCMS_USED_AS_OUTPUT);
-}
-
-uint8_t ICCStore::getProofIntents (cmsHPROFILE profile) const
-{
- MyMutex::MyLock lock (mutex_);
-
- return getSupportedIntents (profile, LCMS_USED_AS_PROOF);
-}
-
-// Reads all profiles from the given profiles dir
-void ICCStore::init (const Glib::ustring& usrICCDir, const Glib::ustring& rtICCDir)
-{
-
- MyMutex::MyLock lock (mutex_);
-
- // RawTherapee's profiles take precedence if a user's profile of the same name exists
- profilesDir = Glib::build_filename (rtICCDir, "output");
- fileProfiles.clear();
- fileProfileContents.clear();
- loadProfiles (profilesDir, &fileProfiles, &fileProfileContents, nullptr, false);
- loadProfiles (usrICCDir, &fileProfiles, &fileProfileContents, nullptr, false);
-
- // Input profiles
- // Load these to different areas, since the short name (e.g. "NIKON D700" may overlap between system/user and RT dir)
- stdProfilesDir = Glib::build_filename (rtICCDir, "input");
- fileStdProfiles.clear();
- fileStdProfilesFileNames.clear();
- loadProfiles (stdProfilesDir, nullptr, nullptr, &fileStdProfilesFileNames, true);
-}
-
-// Determine the first monitor default profile of operating system, if selected
-void ICCStore::findDefaultMonitorProfile ()
-{
- defaultMonitorProfile.clear ();
-
-#ifdef WIN32
- // Get current main monitor. Could be fine tuned to get the current windows monitor (multi monitor setup),
- // but problem is that we live in RTEngine with no GUI window to query around
- HDC hDC = GetDC (NULL);
-
- if (hDC != NULL) {
- if (SetICMMode (hDC, ICM_ON)) {
- char profileName[MAX_PATH + 1];
- DWORD profileLength = MAX_PATH;
-
- if (GetICMProfileA (hDC, &profileLength, profileName)) {
- defaultMonitorProfile = Glib::ustring (profileName);
- defaultMonitorProfile = Glib::path_get_basename (defaultMonitorProfile);
- size_t pos = defaultMonitorProfile.rfind (".");
-
- if (pos != Glib::ustring::npos) {
- defaultMonitorProfile = defaultMonitorProfile.substr (0, pos);
- }
- }
-
- // might fail if e.g. the monitor has no profile
- }
-
- ReleaseDC (NULL, hDC);
- }
-
-#else
-// TODO: Add other OS specific code here
-#endif
-
- if (options.rtSettings.verbose) {
- printf ("Default monitor profile is: %s\n", defaultMonitorProfile.c_str());
- }
-}
-
-ProfileContent::ProfileContent (const Glib::ustring& fileName) : data(nullptr), length(0)
-{
-
- FILE* f = g_fopen (fileName.c_str (), "rb");
-
- if (!f) {
- return;
- }
-
- fseek (f, 0, SEEK_END);
- length = ftell (f);
- fseek (f, 0, SEEK_SET);
- data = new char[length + 1];
- fread (data, length, 1, f);
- data[length] = 0;
- fclose (f);
-}
-
-ProfileContent::ProfileContent (const ProfileContent& other)
-{
-
- length = other.length;
-
- if (other.data) {
- data = new char[length + 1];
- memcpy (data, other.data, length + 1);
- } else {
- data = nullptr;
- }
-}
-
-ProfileContent::ProfileContent (cmsHPROFILE hProfile) : data(nullptr), length(0)
-{
-
- if (hProfile != nullptr) {
- cmsUInt32Number bytesNeeded = 0;
- cmsSaveProfileToMem (hProfile, nullptr, &bytesNeeded);
-
- if (bytesNeeded > 0) {
- data = new char[bytesNeeded + 1];
- cmsSaveProfileToMem (hProfile, data, &bytesNeeded);
- length = (int)bytesNeeded;
- }
- }
-}
-
-
-ProfileContent& ProfileContent::operator= (const ProfileContent& other)
-{
-
- length = other.length;
-
- delete [] data;
-
- if (other.data) {
- data = new char[length + 1];
- memcpy (data, other.data, length + 1);
- } else {
- data = nullptr;
- }
-
- return *this;
-}
-
-cmsHPROFILE ProfileContent::toProfile () const
-{
-
- if (data) {
- return cmsOpenProfileFromMem (data, length);
- } else {
- return nullptr;
- }
-}
-
-cmsHPROFILE ICCStore::createFromMatrix (const double matrix[3][3], bool gamma, const Glib::ustring& name)
-{
-
- static const unsigned phead[] = {
- 1024, 0, 0x2100000, 0x6d6e7472, 0x52474220, 0x58595a20, 0, 0, 0,
- 0x61637370, 0, 0, 0, 0, 0, 0, 0, 0xf6d6, 0x10000, 0xd32d
- };
- unsigned pbody[] = {
- 10, 0x63707274, 0, 36, /* cprt */
- 0x64657363, 0, 40, /* desc */
- 0x77747074, 0, 20, /* wtpt */
- 0x626b7074, 0, 20, /* bkpt */
- 0x72545243, 0, 14, /* rTRC */
- 0x67545243, 0, 14, /* gTRC */
- 0x62545243, 0, 14, /* bTRC */
- 0x7258595a, 0, 20, /* rXYZ */
- 0x6758595a, 0, 20, /* gXYZ */
- 0x6258595a, 0, 20
- }; /* bXYZ */
- static const unsigned pwhite[] = { 0xf351, 0x10000, 0x116cc };//D65
- //static const unsigned pwhite[] = { 0xf6d6, 0x10000, 0xd340 };//D50
-
- // 0x63757276 : curveType, 0 : reserved, 1 : entries (1=gamma, 0=identity), 0x1000000=1.0
- unsigned pcurve[] = { 0x63757276, 0, 0, 0x1000000 };
-// unsigned pcurve[] = { 0x63757276, 0, 1, 0x1000000 };
-
- if (gamma) {
- pcurve[2] = 1;
- // pcurve[3] = 0x1f00000;// pcurve for gamma BT709 : g=2.22 s=4.5
- // normalize gamma in RT, default (Emil's choice = sRGB)
- pcurve[3] = 0x2390000;//pcurve for gamma sRGB : g:2.4 s=12.92
-
- } else {
- // lcms2 up to 2.4 has a bug with linear gamma causing precision loss (banding)
- // of floating point data when a normal icc encoding of linear gamma is used
- // (i e 0 table entries), but by encoding a gamma curve which is 1.0 the
- // floating point path is taken within lcms2 so no precision loss occurs and
- // gamma is still 1.0.
- pcurve[2] = 1;
- pcurve[3] = 0x1000000; //pcurve for gamma 1
- }
-
- // constructing profile header
- unsigned* oprof = new unsigned [phead[0] / sizeof (unsigned)];
- memset (oprof, 0, phead[0]);
- memcpy (oprof, phead, sizeof (phead));
-
- oprof[0] = 132 + 12 * pbody[0];
-
- // constructing tag directory (pointers inside the file), and types
- // 0x74657874 : text
- // 0x64657363 : description tag
- for (unsigned int i = 0; i < pbody[0]; i++) {
- oprof[oprof[0] / 4] = i ? (i > 1 ? 0x58595a20 : 0x64657363) : 0x74657874;
- pbody[i * 3 + 2] = oprof[0];
- oprof[0] += (pbody[i * 3 + 3] + 3) & -4;
- }
-
- memcpy (oprof + 32, pbody, sizeof (pbody));
-
- // wtpt
- memcpy ((char *)oprof + pbody[8] + 8, pwhite, sizeof (pwhite));
-
- // r/g/b TRC
- for (int i = 4; i < 7; i++) {
- memcpy ((char *)oprof + pbody[i * 3 + 2], pcurve, sizeof (pcurve));
- }
-
- // r/g/b XYZ
-// pseudoinverse ((double (*)[3]) out_rgb[output_color-1], inverse, 3);
- for (int i = 0; i < 3; i++)
- for (int j = 0; j < 3; j++) {
- oprof[pbody[j * 3 + 23] / 4 + i + 2] = matrix[i][j] * 0x10000 + 0.5;
-// for (num = k=0; k < 3; k++)
-// num += xyzd50_srgb[i][k] * inverse[j][k];
- }
-
- // convert to network byte order
- for (unsigned int i = 0; i < phead[0] / 4; i++) {
- oprof[i] = htonl (oprof[i]);
- }
-
- // cprt
- strcpy ((char *)oprof + pbody[2] + 8, "--rawtherapee profile--");
-
- // desc
- oprof[pbody[5] / 4 + 2] = name.size() + 1;
- strcpy ((char *)oprof + pbody[5] + 12, name.c_str());
-
-
- cmsHPROFILE p = cmsOpenProfileFromMem (oprof, ntohl (oprof[0]));
- delete [] oprof;
- return p;
-}
-
-}
diff --git a/rtengine/iccstore.h b/rtengine/iccstore.h
index 58ddfdbc9..d0c5ef400 100644
--- a/rtengine/iccstore.h
+++ b/rtengine/iccstore.h
@@ -16,169 +16,103 @@
* You should have received a copy of the GNU General Public License
* along with RawTherapee. If not, see .
*/
-#ifndef __ICCSTORE__
-#define __ICCSTORE__
+#pragma once
+
+#include
+#include
+#include
+
+#include
#include
-#include
-#include