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 -#include -#include -#include "procparams.h" + #include "color.h" -#include "../rtgui/threadutils.h" namespace rtengine { -typedef const double (*TMatrix)[3]; +namespace procparams +{ + + class ColorManagementParams; + +} + +typedef const double(*TMatrix)[3]; class ProfileContent { - public: - char* data; - int length; + ProfileContent(); - ProfileContent (); - ~ProfileContent (); + explicit ProfileContent(const Glib::ustring& fileName); + explicit ProfileContent(cmsHPROFILE hProfile); + cmsHPROFILE toProfile() const; - ProfileContent (const ProfileContent& other); - ProfileContent& operator= (const rtengine::ProfileContent& other); + const std::string& getData() const; - explicit ProfileContent (const Glib::ustring& fileName); - explicit ProfileContent (cmsHPROFILE hProfile); - cmsHPROFILE toProfile () const; +private: + std::string data; }; class ICCStore { - typedef std::map ProfileMap; - typedef std::map MatrixMap; - typedef std::map ContentMap; - typedef std::map NameMap; - - ProfileMap wProfiles; - ProfileMap wProfilesGamma; - MatrixMap wMatrices; - MatrixMap iwMatrices; - - // these contain profiles from user/system directory (supplied on init) - Glib::ustring profilesDir; - 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; - - const cmsHPROFILE xyz; - const cmsHPROFILE srgb; - - mutable MyMutex mutex_; - - ICCStore (); - public: - enum class ProfileType { MONITOR, PRINTER, - OUTPUT // (actually correspond to the same profiles than with MONITOR) + OUTPUT //(actually correspond to the same profiles than with MONITOR) }; - static ICCStore* getInstance (); + static ICCStore* getInstance(); - void init (const Glib::ustring& usrICCDir, const Glib::ustring& stdICCDir); - - static void getGammaArray(const procparams::ColorManagementParams &icm, GammaValues &ga); - static cmsHPROFILE makeStdGammaProfile (cmsHPROFILE iprof); - static cmsHPROFILE createFromMatrix (const double matrix[3][3], bool gamma = false, const Glib::ustring& name = Glib::ustring()); - static cmsHPROFILE createGammaProfile (const procparams::ColorManagementParams &icm, GammaValues &ga); - static cmsHPROFILE createCustomGammaOutputProfile (const procparams::ColorManagementParams &icm, GammaValues &ga); + void init(const Glib::ustring& usrICCDir, const Glib::ustring& stdICCDir, bool loadAll); // Main monitors standard profile name, from OS - void findDefaultMonitorProfile (); - cmsHPROFILE getDefaultMonitorProfile () const; - Glib::ustring getDefaultMonitorProfileName () const; + void findDefaultMonitorProfile(); + cmsHPROFILE getDefaultMonitorProfile() const; + Glib::ustring getDefaultMonitorProfileName() const; - cmsHPROFILE workingSpace (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; + cmsHPROFILE workingSpace(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; - bool outputProfileExist (const Glib::ustring& name) const; - cmsHPROFILE getProfile (const Glib::ustring& name) const; - cmsHPROFILE getStdProfile (const Glib::ustring& name) const; - ProfileContent getContent (const Glib::ustring& name) const; + bool outputProfileExist(const Glib::ustring& name) const; + cmsHPROFILE getProfile(const Glib::ustring& name) const; + cmsHPROFILE getStdProfile(const Glib::ustring& name) const; + ProfileContent getContent(const Glib::ustring& name) const; - cmsHPROFILE getXYZProfile () const; - cmsHPROFILE getsRGBProfile () const; + cmsHPROFILE getXYZProfile() const; + cmsHPROFILE getsRGBProfile() const; - std::vector getProfiles (const ProfileType type = ProfileType::MONITOR) const; - std::vector getProfilesFromDir (const Glib::ustring& dirName) const; + std::vector getProfiles(ProfileType type = ProfileType::MONITOR) const; + std::vector getProfilesFromDir(const Glib::ustring& dirName) const; - uint8_t getInputIntents (cmsHPROFILE profile) const; - uint8_t getOutputIntents (cmsHPROFILE profile) const; - uint8_t getProofIntents (cmsHPROFILE profile) const; + std::uint8_t getInputIntents(cmsHPROFILE profile) const; + std::uint8_t getOutputIntents(cmsHPROFILE profile) const; + std::uint8_t getProofIntents(cmsHPROFILE profile) const; - uint8_t getInputIntents (const Glib::ustring& name) const; - uint8_t getOutputIntents (const Glib::ustring& name) const; - uint8_t getProofIntents (const Glib::ustring& name) const; + std::uint8_t getInputIntents(const Glib::ustring& name) const; + std::uint8_t getOutputIntents(const Glib::ustring& name) const; + std::uint8_t getProofIntents(const Glib::ustring& name) const; + + static std::vector getWorkingProfiles(); + static std::vector getGamma(); + + static void getGammaArray(const procparams::ColorManagementParams& icm, GammaValues& ga); + static cmsHPROFILE makeStdGammaProfile(cmsHPROFILE iprof); + static cmsHPROFILE createFromMatrix(const double matrix[3][3], bool gamma = false, const Glib::ustring& name = Glib::ustring()); + static cmsHPROFILE createGammaProfile(const procparams::ColorManagementParams& icm, GammaValues& ga); + static cmsHPROFILE createCustomGammaOutputProfile(const procparams::ColorManagementParams& icm, GammaValues& ga); + +private: + class Implementation; + + ICCStore(); + ~ICCStore(); + + const std::unique_ptr implementation; }; -#define iccStore ICCStore::getInstance() - -inline ProfileContent::ProfileContent () : - data(nullptr), - length(0) -{ } - -inline ProfileContent::~ProfileContent () -{ - delete [] data; -} - -inline cmsHPROFILE ICCStore::getDefaultMonitorProfile () const -{ - return getProfile (defaultMonitorProfile); -} - -inline Glib::ustring ICCStore::getDefaultMonitorProfileName () const -{ - return defaultMonitorProfile; -} - -inline uint8_t ICCStore::getInputIntents (const Glib::ustring &name) const -{ - return getInputIntents (getProfile (name)); -} - -inline uint8_t ICCStore::getOutputIntents (const Glib::ustring &name) const -{ - return getOutputIntents (getProfile (name)); -} - -inline uint8_t ICCStore::getProofIntents (const Glib::ustring &name) const -{ - return getProofIntents (getProfile (name)); -} - -inline cmsHPROFILE ICCStore::getXYZProfile () const -{ - return xyz; -} - -inline cmsHPROFILE ICCStore::getsRGBProfile () const -{ - return srgb; -} - -} - -#endif - diff --git a/rtengine/icons.cc b/rtengine/icons.cc new file mode 100644 index 000000000..2c5075007 --- /dev/null +++ b/rtengine/icons.cc @@ -0,0 +1,120 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2004-2010 Gabor Horvath + * Copyright (c) 2011 Jean-Christophe FRISCH + * + * RawTherapee is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RawTherapee is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RawTherapee. If not, see . + */ + +#include "icons.h" + +#include + +namespace rtengine +{ + +std::vector imagePaths; + +bool loadIconSet(const Glib::ustring& iconSet) +{ + try { + + Glib::KeyFile keyFile; + keyFile.load_from_file (iconSet); + + auto iconSetDir = keyFile.get_string ("General", "Iconset"); + + if (!iconSetDir.empty ()) { + imagePaths.push_back (Glib::build_filename (argv0, "images", iconSetDir, "actions")); + imagePaths.push_back (Glib::build_filename (argv0, "images", iconSetDir)); + imagePaths.push_back (Glib::build_filename (argv0, "images", iconSetDir, "devices")); + imagePaths.push_back (Glib::build_filename (argv0, "images", iconSetDir, "places")); + } + + iconSetDir = keyFile.get_string ("General", "FallbackIconset"); + + if (!iconSetDir.empty ()) { + imagePaths.push_back (Glib::build_filename (argv0, "images", iconSetDir, "actions")); + imagePaths.push_back (Glib::build_filename (argv0, "images", iconSetDir)); + imagePaths.push_back (Glib::build_filename (argv0, "images", iconSetDir, "devices")); + imagePaths.push_back (Glib::build_filename (argv0, "images", iconSetDir, "places")); + } + + return true; + + } catch (const Glib::Exception& exception) { + + if (options.rtSettings.verbose) { + std::cerr << "Failed to load icon set \"" << iconSet << "\": " << exception.what() << std::endl; + } + + return false; + + } +} + +Glib::ustring findIconAbsolutePath (const Glib::ustring& iconName) +{ + try { + + for (const auto& imagePath : imagePaths) { + const auto iconPath = Glib::build_filename(imagePath, iconName); + + if (Glib::file_test(iconPath, Glib::FILE_TEST_IS_REGULAR)) { + return iconPath; + } + } + + } catch(const Glib::Exception&) {} + + if (options.rtSettings.verbose) { + std::cerr << "Icon \"" << iconName << "\" could not be found!" << std::endl; + } + + return Glib::ustring(); +} + +void setPaths (const Options& options) +{ + // TODO: Forcing the Dark theme, so reading the icon set files is useless for now... + + /*Glib::ustring iconSet; + + // Either use the system icon set or the one specified in the options. + if (options.useSystemTheme) { + iconSet = Glib::build_filename (argv0, "themes", "system.iconset"); + } else { + iconSet = Glib::build_filename (argv0, "themes", options.theme + ".iconset"); + } + + imagePaths.clear (); + + if (!loadIconSet (iconSet)) { + // If the preferred icon set is unavailable, fall back to the default icon set. + loadIconSet (Glib::build_filename (argv0, "themes", "Default.iconset")); + }*/ + + imagePaths.clear (); + + imagePaths.push_back (Glib::build_filename(argv0, "images", "Dark")); + imagePaths.push_back (Glib::build_filename(argv0, "images", "Dark", "actions")); + imagePaths.push_back (Glib::build_filename(argv0, "images", "Dark", "devices")); + imagePaths.push_back (Glib::build_filename(argv0, "images", "Dark", "places")); + + // The images folder is the second fallback solution. + imagePaths.push_back (Glib::build_filename(argv0, "images")); +} + +} diff --git a/rtengine/icons.h b/rtengine/icons.h new file mode 100644 index 000000000..9f6654a9c --- /dev/null +++ b/rtengine/icons.h @@ -0,0 +1,33 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2004-2010 Gabor Horvath + * + * RawTherapee is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RawTherapee is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RawTherapee. If not, see . + */ +#ifndef _ICONS_ +#define _ICONS_ + +#include +#include "../rtgui/options.h" + +namespace rtengine +{ + +Glib::ustring findIconAbsolutePath (const Glib::ustring& iconName); +void setPaths (const Options& options); + +} + +#endif diff --git a/rtengine/imagefloat.cc b/rtengine/imagefloat.cc index aeb57c795..a0a7e293c 100644 --- a/rtengine/imagefloat.cc +++ b/rtengine/imagefloat.cc @@ -429,7 +429,7 @@ void Imagefloat::calcCroppedHistogram(const ProcParams ¶ms, float scale, LUT hist.clear(); // Set up factors to calc the lightness - TMatrix wprof = iccStore->workingSpaceMatrix (params.icm.working); + TMatrix wprof = ICCStore::getInstance()->workingSpaceMatrix (params.icm.working); float facRed = wprof[1][0]; float facGreen = wprof[1][1]; diff --git a/rtengine/imageio.cc b/rtengine/imageio.cc index e0a28d4e9..f24842b65 100644 --- a/rtengine/imageio.cc +++ b/rtengine/imageio.cc @@ -175,7 +175,7 @@ void ImageIO::setMetadata (const rtexif::TagDirectory* eroot, const rtengine::pr iptc_data_sort (iptc); } -void ImageIO::setOutputProfile (char* pdata, int plen) +void ImageIO::setOutputProfile (const char* pdata, int plen) { delete [] profileData; diff --git a/rtengine/imageio.h b/rtengine/imageio.h index 00bdbece1..1fbeb6e2d 100644 --- a/rtengine/imageio.h +++ b/rtengine/imageio.h @@ -176,7 +176,7 @@ public: void setMetadata (const rtexif::TagDirectory* eroot); void setMetadata (const rtexif::TagDirectory* eroot, const rtengine::procparams::ExifPairs& exif, const rtengine::procparams::IPTCPairs& iptcc); - void setOutputProfile (char* pdata, int plen); + void setOutputProfile (const char* pdata, int plen); MyMutex& mutex () { return imutex; diff --git a/rtengine/imagesource.h b/rtengine/imagesource.h index 59d46035b..8998fa848 100644 --- a/rtengine/imagesource.h +++ b/rtengine/imagesource.h @@ -66,7 +66,7 @@ public: embProfile(nullptr), idata(nullptr), dirpyrdenoiseExpComp(INFINITY) {} virtual ~ImageSource () {} - virtual int load (const Glib::ustring &fname, bool batch = false) = 0; + virtual int load (const Glib::ustring &fname, int imageNum = 0, bool batch = false) = 0; virtual void preprocess (const RAWParams &raw, const LensProfParams &lensProf, const CoarseTransformParams& coarse, bool prepareDenoise = true) {}; virtual void demosaic (const RAWParams &raw) {}; virtual void retinex (ColorManagementParams cmp, RetinexParams deh, ToneCurveParams Tc, LUTf & cdcurve, LUTf & mapcurve, const RetinextransmissionCurve & dehatransmissionCurve, const RetinexgaintransmissionCurve & dehagaintransmissionCurve, multi_array2D &conversionBuffer, bool dehacontlutili, bool mapcontlutili, bool useHsl, float &minCD, float &maxCD, float &mini, float &maxi, float &Tmean, float &Tsigma, float &Tmin, float &Tmax, LUTu &histLRETI) {}; @@ -80,6 +80,9 @@ public: virtual bool IsrgbSourceModified() const = 0; // tracks whether cached rgb output of demosaic has been modified + virtual void setCurrentFrame(unsigned int frameNum) = 0; + + // use right after demosaicing image, add coarse transformation and put the result in the provided Imagefloat* virtual void getImage (const ColorTemp &ctemp, int tran, Imagefloat* image, const PreviewProps &pp, const ToneCurveParams &hlp, const ColorManagementParams &cmp, const RAWParams &raw) = 0; virtual eSensorType getSensorType () @@ -157,6 +160,7 @@ public: { return this; } + virtual void getRawValues(int x, int y, int rotate, int &R, int &G, int &B) = 0; }; } #endif diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index b36a9f9a2..31a09ef22 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -185,6 +185,7 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall) // raw auto CA is bypassed if no high detail is needed, so we have to compute it when high detail is needed if ( (todo & M_PREPROC) || (!highDetailPreprocessComputed && highDetailNeeded)) { + imgsrc->setCurrentFrame(params.raw.bayersensor.imageNum); imgsrc->preprocess( rp, params.lensProf, params.coarse ); imgsrc->getRAWHistogram( histRedRaw, histGreenRaw, histBlueRaw ); @@ -214,7 +215,7 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall) if (settings->verbose) { if (imgsrc->getSensorType() == ST_BAYER) { - printf("Demosaic Bayer image using method: %s\n", rp.bayersensor.method.c_str()); + printf("Demosaic Bayer image n.%d using method: %s\n", rp.bayersensor.imageNum + 1, rp.bayersensor.method.c_str()); } else if (imgsrc->getSensorType() == ST_FUJI_XTRANS) { printf("Demosaic X-Trans image with using method: %s\n", rp.xtranssensor.method.c_str()); } @@ -457,13 +458,13 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall) opautili = false; if(params.colorToning.enabled) { - TMatrix wprof = iccStore->workingSpaceMatrix (params.icm.working); + TMatrix wprof = ICCStore::getInstance()->workingSpaceMatrix (params.icm.working); double wp[3][3] = { {wprof[0][0], wprof[0][1], wprof[0][2]}, {wprof[1][0], wprof[1][1], wprof[1][2]}, {wprof[2][0], wprof[2][1], wprof[2][2]} }; - TMatrix wiprof = iccStore->workingSpaceInverseMatrix (params.icm.working); + TMatrix wiprof = ICCStore::getInstance()->workingSpaceInverseMatrix (params.icm.working); double wip[3][3] = { {wiprof[0][0], wiprof[0][1], wiprof[0][2]}, {wiprof[1][0], wiprof[1][1], wiprof[1][2]}, diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index f756f347a..2d9deb10e 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -80,9 +80,9 @@ void ImProcFunctions::updateColorProfiles (const Glib::ustring& monitorProfile, cmsHPROFILE monitor = nullptr; if (!monitorProfile.empty()) { #if !defined(__APPLE__) // No support for monitor profiles on OS X, all data is sRGB - monitor = iccStore->getProfile (monitorProfile); + monitor = ICCStore::getInstance()->getProfile (monitorProfile); #else - monitor = iccStore->getProfile ("RT_sRGB"); + monitor = ICCStore::getInstance()->getProfile ("RT_sRGB"); #endif } @@ -97,7 +97,7 @@ void ImProcFunctions::updateColorProfiles (const Glib::ustring& monitorProfile, if (softProof) { cmsHPROFILE oprof = nullptr; if (!settings->printerProfile.empty()) { - oprof = iccStore->getProfile(settings->printerProfile); + oprof = ICCStore::getInstance()->getProfile(settings->printerProfile); } if (oprof) { @@ -137,7 +137,7 @@ void ImProcFunctions::updateColorProfiles (const Glib::ustring& monitorProfile, void ImProcFunctions::firstAnalysis (const Imagefloat* const original, const ProcParams ¶ms, LUTu & histogram) { - TMatrix wprof = iccStore->workingSpaceMatrix (params.icm.working); + TMatrix wprof = ICCStore::getInstance()->workingSpaceMatrix (params.icm.working); lumimul[0] = wprof[1][0]; lumimul[1] = wprof[1][1]; @@ -557,7 +557,7 @@ void ImProcFunctions::ciecam_02 (CieImage* ncie, double adap, int begh, int endh #endif { //matrix for current working space - TMatrix wiprof = iccStore->workingSpaceInverseMatrix (params->icm.working); + TMatrix wiprof = ICCStore::getInstance()->workingSpaceInverseMatrix (params->icm.working); double wip[3][3] = { {wiprof[0][0], wiprof[0][1], wiprof[0][2]}, {wiprof[1][0], wiprof[1][1], wiprof[1][2]}, @@ -1287,7 +1287,7 @@ void ImProcFunctions::ciecam_02 (CieImage* ncie, double adap, int begh, int endh #pragma omp parallel default(shared) firstprivate(lab,xw2,yw2,zw2,chr,yb,la2,yb2, height,width,begh, endh, nc2,f2,c2, gamu, highlight,pW) #endif { - TMatrix wiprofa = iccStore->workingSpaceInverseMatrix (params->icm.working); + TMatrix wiprofa = ICCStore::getInstance()->workingSpaceInverseMatrix (params->icm.working); double wipa[3][3] = { {wiprofa[0][0], wiprofa[0][1], wiprofa[0][2]}, {wiprofa[1][0], wiprofa[1][1], wiprofa[1][2]}, @@ -1891,7 +1891,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int //matrix for current working space - TMatrix wiprof = iccStore->workingSpaceInverseMatrix (params->icm.working); + TMatrix wiprof = ICCStore::getInstance()->workingSpaceInverseMatrix (params->icm.working); const float wip[3][3] = { {(float)wiprof[0][0], (float)wiprof[0][1], (float)wiprof[0][2]}, {(float)wiprof[1][0], (float)wiprof[1][1], (float)wiprof[1][2]}, @@ -2986,8 +2986,8 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer bool processLCE = params->sh.enabled && shmap && params->sh.localcontrast > 0; double lceamount = params->sh.localcontrast / 200.0; - TMatrix wprof = iccStore->workingSpaceMatrix (params->icm.working); - TMatrix wiprof = iccStore->workingSpaceInverseMatrix (params->icm.working); + TMatrix wprof = ICCStore::getInstance()->workingSpaceMatrix (params->icm.working); + TMatrix wiprof = ICCStore::getInstance()->workingSpaceInverseMatrix (params->icm.working); float toxyz[3][3] = { { @@ -3102,8 +3102,8 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer clutAndWorkingProfilesAreSame = hald_clut->getProfile() == params->icm.working; if ( !clutAndWorkingProfilesAreSame ) { - xyz2clut = iccStore->workingSpaceInverseMatrix( hald_clut->getProfile() ); - clut2xyz = iccStore->workingSpaceMatrix( hald_clut->getProfile() ); + xyz2clut = ICCStore::getInstance()->workingSpaceInverseMatrix( hald_clut->getProfile() ); + clut2xyz = ICCStore::getInstance()->workingSpaceMatrix( hald_clut->getProfile() ); #ifdef __SSE2__ @@ -5642,14 +5642,14 @@ SSEFUNCTION void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBu const bool gamutLch = settings->gamutLch; const float amountchroma = (float) settings->amchroma; - TMatrix wiprof = iccStore->workingSpaceInverseMatrix (params->icm.working); + TMatrix wiprof = ICCStore::getInstance()->workingSpaceInverseMatrix (params->icm.working); double wip[3][3] = { {wiprof[0][0], wiprof[0][1], wiprof[0][2]}, {wiprof[1][0], wiprof[1][1], wiprof[1][2]}, {wiprof[2][0], wiprof[2][1], wiprof[2][2]} }; - TMatrix wprof = iccStore->workingSpaceMatrix (params->icm.working); + TMatrix wprof = ICCStore::getInstance()->workingSpaceMatrix (params->icm.working); double wp[3][3] = { {wprof[0][0], wprof[0][1], wprof[0][2]}, {wprof[1][0], wprof[1][1], wprof[1][2]}, @@ -6924,7 +6924,7 @@ double ImProcFunctions::getAutoDistor (const Glib::ustring &fname, int thumb_si return 0.0; } - Thumbnail* raw = rtengine::Thumbnail::loadFromRaw (fname, ri, w_raw, h_raw, 1, 1.0, FALSE); + Thumbnail* raw = rtengine::Thumbnail::loadFromRaw (fname, ri, w_raw, h_raw, 1, 1.0, FALSE, 0); if (!raw) { delete thumb; @@ -6983,7 +6983,7 @@ double ImProcFunctions::getAutoDistor (const Glib::ustring &fname, int thumb_si void ImProcFunctions::rgb2lab(const Imagefloat &src, LabImage &dst, const Glib::ustring &workingSpace) { - TMatrix wprof = iccStore->workingSpaceMatrix( workingSpace ); + TMatrix wprof = ICCStore::getInstance()->workingSpaceMatrix( workingSpace ); 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])}, @@ -7009,7 +7009,7 @@ void ImProcFunctions::rgb2lab(const Imagefloat &src, LabImage &dst, const Glib:: SSEFUNCTION void ImProcFunctions::lab2rgb(const LabImage &src, Imagefloat &dst, const Glib::ustring &workingSpace) { - TMatrix wiprof = iccStore->workingSpaceInverseMatrix( workingSpace ); + TMatrix wiprof = ICCStore::getInstance()->workingSpaceInverseMatrix( workingSpace ); const float wip[3][3] = { {static_cast(wiprof[0][0]), static_cast(wiprof[0][1]), static_cast(wiprof[0][2])}, {static_cast(wiprof[1][0]), static_cast(wiprof[1][1]), static_cast(wiprof[1][2])}, diff --git a/rtengine/init.cc b/rtengine/init.cc index 27edd976b..8b270f1d7 100644 --- a/rtengine/init.cc +++ b/rtengine/init.cc @@ -37,15 +37,14 @@ const Settings* settings; MyMutex* lcmsMutex = nullptr; -int init (const Settings* s, Glib::ustring baseDir, Glib::ustring userSettingsDir) +int init (const Settings* s, Glib::ustring baseDir, Glib::ustring userSettingsDir, bool loadAll) { settings = s; - iccStore->init (s->iccDirectory, baseDir + "/iccprofiles"); - iccStore->findDefaultMonitorProfile(); - DCPStore::getInstance()->init (baseDir + "/dcpprofiles"); + ICCStore::getInstance()->init (s->iccDirectory, Glib::build_filename (baseDir, "iccprofiles"), loadAll); + ICCStore::getInstance()->findDefaultMonitorProfile(); + DCPStore::getInstance()->init (Glib::build_filename (baseDir, "dcpprofiles"), loadAll); CameraConstantsStore::getInstance ()->init (baseDir, userSettingsDir); - profileStore.init (); ProcParams::init (); Color::init (); PerceptualToneCurve::init (); diff --git a/rtengine/iplab2rgb.cc b/rtengine/iplab2rgb.cc index f970e3a54..dac24411d 100644 --- a/rtengine/iplab2rgb.cc +++ b/rtengine/iplab2rgb.cc @@ -158,7 +158,7 @@ Image8* ImProcFunctions::lab2rgb (LabImage* lab, int cx, int cy, int cw, int ch, standard_gamma = false; } - cmsHPROFILE oprof = iccStore->getProfile (profile); + cmsHPROFILE oprof = ICCStore::getInstance()->getProfile (profile); if (oprof) { cmsHPROFILE oprofG = oprof; @@ -216,7 +216,7 @@ Image8* ImProcFunctions::lab2rgb (LabImage* lab, int cx, int cy, int cw, int ch, } } else { - const auto xyz_rgb = iccStore->workingSpaceInverseMatrix (profile); + const auto xyz_rgb = ICCStore::getInstance()->workingSpaceInverseMatrix (profile); #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) if (multiThread) @@ -286,11 +286,11 @@ Image16* ImProcFunctions::lab2rgb16 (LabImage* lab, int cx, int cy, int cw, int cmsHPROFILE oprof = nullptr; if (ga) { lcmsMutex->lock (); - iccStore->getGammaArray(icm, *ga); - oprof = iccStore->createGammaProfile(icm, *ga); + ICCStore::getInstance()->getGammaArray(icm, *ga); + oprof = ICCStore::getInstance()->createGammaProfile(icm, *ga); lcmsMutex->unlock (); } else { - oprof = iccStore->getProfile (icm.output); + oprof = ICCStore::getInstance()->getProfile (icm.output); } if (oprof) { diff --git a/rtengine/ipvibrance.cc b/rtengine/ipvibrance.cc index 60994175b..8eb565434 100644 --- a/rtengine/ipvibrance.cc +++ b/rtengine/ipvibrance.cc @@ -170,7 +170,7 @@ void ImProcFunctions::vibrance (LabImage* lab) const bool protectskins = params->vibrance.protectskins; const bool avoidcolorshift = params->vibrance.avoidcolorshift; - TMatrix wiprof = iccStore->workingSpaceInverseMatrix (params->icm.working); + TMatrix wiprof = ICCStore::getInstance()->workingSpaceInverseMatrix (params->icm.working); //inverse matrix user select const double wip[3][3] = { {wiprof[0][0], wiprof[0][1], wiprof[0][2]}, diff --git a/rtengine/ipwavelet.cc b/rtengine/ipwavelet.cc index 532c562c5..882dd8cdc 100644 --- a/rtengine/ipwavelet.cc +++ b/rtengine/ipwavelet.cc @@ -148,7 +148,7 @@ SSEFUNCTION void ImProcFunctions::ip_wavelet(LabImage * lab, LabImage * dst, int // init variables to display Munsell corrections MunsellDebugInfo* MunsDebugInfo = new MunsellDebugInfo(); #endif - TMatrix wiprof = iccStore->workingSpaceInverseMatrix (params->icm.working); + TMatrix wiprof = ICCStore::getInstance()->workingSpaceInverseMatrix (params->icm.working); double wip[3][3] = { {wiprof[0][0], wiprof[0][1], wiprof[0][2]}, {wiprof[1][0], wiprof[1][1], wiprof[1][2]}, diff --git a/rtengine/median.h b/rtengine/median.h index 971452bc3..30adbacb3 100644 --- a/rtengine/median.h +++ b/rtengine/median.h @@ -64,6 +64,15 @@ inline vfloat median(std::array array) } #endif + +template +inline T median(std::array array) +{ + float val1 = std::max(std::min(array[0], array[1]), std::min(array[2], array[3])); + float val2 = std::min(std::max(array[0], array[1]), std::max(array[2], array[3])); + return (val1 + val2) / 2.f; +} + template inline T median(std::array array) { diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc new file mode 100644 index 000000000..0ef4f6b6b --- /dev/null +++ b/rtengine/pixelshift.cc @@ -0,0 +1,1430 @@ +//////////////////////////////////////////////////////////////// +// +// pentax pixelshift algorithm with motion detection +// +// +// If motion correction is enabled only the pixels which are not detected as motion are set +// That means for a complete image you have to demosaic one of the frames with a bayer demosaicer to fill red, green and blue +// before calling pixelshift in case motion correction is enabled. +// +// copyright (c) Ingo Weyrich 2016 +// +// +// pixelshift.cc is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +//////////////////////////////////////////////////////////////// + +#include +#include "rawimagesource.h" +#include "../rtgui/multilangmgr.h" +#include "procparams.h" +#include "gauss.h" +#include "median.h" +#define BENCHMARK +#include "StopWatch.h" + +namespace +{ + +float greenDiff(float a, float b, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, bool showMotion) +{ + // calculate the difference between two green samples +#ifdef PIXELSHIFTDEV + if(adaptive) { +#endif + float gDiff = a - b; + gDiff *= eperIso; + gDiff *= gDiff; + float avg = (a + b) * 0.5f; + avg *= eperIso; + prnu *= avg; + float stddev = stddevFactor * (avg + nreadIso + prnu * prnu); + float result = gDiff - stddev; + + if(!showMotion) { + return result; + } else if(result > 0.f) { // for the motion mask + return std::fabs(a - b) / (std::max(a, b) + 0.01f); + } else { + return 0.f; + } +#ifdef PIXELSHIFTDEV + + } else { + float gDiff = std::fabs(a - b); + // add a small epsilon to avoid division by zero + float maxVal = std::max(a, b) + 0.01f; + return gDiff / maxVal; + } +#endif +} + +#ifdef PIXELSHIFTDEV +float nonGreenDiff(float a, float b, float stddevFactor, float eperIso, float nreadIso, float prnu, bool showMotion) +{ + // calculate the difference between two nongreen samples + float gDiff = a - b; + gDiff *= eperIso; + gDiff *= gDiff; + float avg = (a + b) / 2.f; + avg *= eperIso; + prnu *= avg; + float stddev = stddevFactor * (avg + nreadIso + prnu * prnu); + float result = gDiff - stddev; + + if(!showMotion) { + return result; + } else if(result > 0.f) { // for the motion mask + return std::fabs(a - b) / (std::max(a, b) + 0.01f); + } else { + return 0.f; + } +} +#endif + +float nonGreenDiffCross(float right, float left, float top, float bottom, float centre, float clippedVal, float stddevFactor, float eperIso, float nreadIso, float prnu, bool showMotion) +{ + if(rtengine::max(right, left, top, bottom, centre) > clippedVal) { + return 0.f; + } + // check non green cross + float hDiff = (right + left) * 0.5f - centre; + hDiff *= eperIso; + hDiff *= hDiff; + float vDiff = (top + bottom) * 0.5f - centre; + vDiff *= eperIso; + vDiff *= vDiff; + float avg = ((right + left) + (top + bottom)) * 0.25f; + avg *= eperIso; + prnu *= avg; + float stddev = stddevFactor * (avg + nreadIso + prnu * prnu); + float result = std::min(hDiff, vDiff) - stddev; + + if(!showMotion) { + return result; + } else if(result > 0.f) { // for the motion mask + return std::sqrt((result / (stddev + result + 0.01f))); + } else { + return 0.f; + } +} + +void paintMotionMask(int index, bool showMotion, float gridMax, bool showOnlyMask, float *maskDest, float *nonMaskDest0, float *nonMaskDest1) +{ + if(showMotion) { + if(!showOnlyMask) { + // if showMotion is enabled colourize the pixel + maskDest[index] = 1000.f + 25000.f * gridMax; + nonMaskDest1[index] = nonMaskDest0[index] = 0.f; + } else { + maskDest[index] = nonMaskDest0[index] = nonMaskDest1[index] = 1000.f + 25000.f * gridMax; + } + } +} + +void invertMask(int xStart, int xEnd, int yStart, int yEnd, array2D &maskIn, array2D &maskOut) +{ + #pragma omp parallel for schedule(dynamic,16) + + for(int i = yStart; i < yEnd; ++i) { + #pragma omp simd + + for(int j = xStart; j < xEnd; ++j) { + maskOut[i][j] = ~maskIn[i][j]; + } + } +} + +void xorMasks(int xStart, int xEnd, int yStart, int yEnd, array2D &maskIn, array2D &maskOut) +{ + #pragma omp parallel for schedule(dynamic,16) + + for(int i = yStart; i < yEnd; ++i) { + #pragma omp simd + + for(int j = xStart; j < xEnd; ++j) { + maskOut[i][j] ^= maskIn[i][j]; + } + } +} + +void floodFill4Impl(int y, int x, int xStart, int xEnd, int yStart, int yEnd, array2D &mask, std::stack, std::vector>> &coordStack) +{ + coordStack.emplace(x, y); + + while(!coordStack.empty()) { + auto coord = coordStack.top(); + coordStack.pop(); + auto x = coord.first, y = coord.second; + + if (mask[y][x] == 255) { + auto yUp = y - 1, yDown = y + 1; + bool lastXUp = false, lastXDown = false, firstXUp = false, firstXDown = false; + mask[y][x] = 0; + + if(yUp >= yStart && mask[yUp][x] == 255) { + coordStack.emplace(x, yUp); + firstXUp = lastXUp = true; + } + + if(yDown < yEnd && mask[yDown][x] == 255) { + coordStack.emplace(x, yDown); + firstXDown = lastXDown = true; + } + + auto xr = x + 1; + + while(xr < xEnd && mask[y][xr] == 255) { + mask[y][xr] = 0; + + if(yUp >= yStart && mask[yUp][xr] == 255) { + if(!lastXUp) { + coordStack.emplace(xr, yUp); + lastXUp = true; + } + } else { + lastXUp = false; + } + + if(yDown < yEnd && mask[yDown][xr] == 255) { + if(!lastXDown) { + coordStack.emplace(xr, yDown); + lastXDown = true; + } + } else { + lastXDown = false; + } + + xr++; + } + + auto xl = x - 1; + lastXUp = firstXUp; + lastXDown = firstXDown; + + while(xl >= xStart && mask[y][xl] == 255) { + mask[y][xl] = 0; + + if(yUp >= yStart && mask[yUp][xl] == 255) { + if(!lastXUp) { + coordStack.emplace(xl, yUp); + lastXUp = true; + } + } else { + lastXUp = false; + } + + if(yDown < yEnd && mask[yDown][xl] == 255) { + if(!lastXDown) { + coordStack.emplace(xl, yDown); + lastXDown = true; + } + } else { + lastXDown = false; + } + + xl--; + } + + mask[y][x] = 0; + } + } +} + +void floodFill4(int xStart, int xEnd, int yStart, int yEnd, array2D &mask) +{ + #pragma omp parallel + { + std::stack, std::vector>> coordStack; + + #pragma omp for schedule(dynamic,128) nowait + + for(uint16_t i = yStart; i < yEnd; i++) + { + floodFill4Impl(i, xStart, xStart, xEnd, yStart, yEnd, mask, coordStack); + } + + #pragma omp for schedule(dynamic,128) nowait + + for(int16_t i = yEnd - 1; i >= 0 ; i--) + { + floodFill4Impl(i, xEnd - 1, xStart, xEnd, yStart, yEnd, mask, coordStack); + } + + #pragma omp sections nowait + { + #pragma omp section + { + uint16_t i = yStart; + + for(uint16_t j = xStart; j < xEnd; j++) + { + floodFill4Impl(i, j, xStart, xEnd, yStart, yEnd, mask, coordStack); + } + } + #pragma omp section + { + uint16_t i = yStart; + + for(uint16_t j = xEnd - 1; j >= xStart; j--) + { + floodFill4Impl(i, j, xStart, xEnd, yStart, yEnd, mask, coordStack); + } + } + #pragma omp section + { + uint16_t i = yEnd; + + for(uint16_t j = xStart; j < xEnd; j++) + { + floodFill4Impl(i, j, xStart, xEnd, yStart, yEnd, mask, coordStack); + } + } + #pragma omp section + { + uint16_t i = yEnd; + + for(uint16_t j = xEnd - 1; j >= xStart; j--) + { + floodFill4Impl(i, j, xStart, xEnd, yStart, yEnd, mask, coordStack); + } + } + } + } +} + +} + +using namespace std; +using namespace rtengine; +void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RAWParams::BayerSensor &bayerParamsIn, unsigned int frame, const std::string &model, float rawWpCorrection) +{ +#ifdef PIXELSHIFTDEV + BENCHFUN +#endif + + if(numFrames != 4) { // fallback for non pixelshift files + amaze_demosaic_RT (0, 0, winw, winh, rawData, red, green, blue); + return; + } + + RAWParams::BayerSensor bayerParams = bayerParamsIn; + +#ifndef PIXELSHIFTDEV + bayerParams.pixelShiftAutomatic = true; +#endif + + if(bayerParams.pixelShiftMotionCorrectionMethod == RAWParams::BayerSensor::Automatic) { + bool pixelShiftEqualBright = bayerParams.pixelShiftEqualBright; + bayerParams.setPixelShiftDefaults(); + bayerParams.pixelShiftEqualBright = pixelShiftEqualBright; + } else if(bayerParams.pixelShiftMotionCorrectionMethod == RAWParams::BayerSensor::Off) { + bayerParams.pixelShiftMotion = 0; + bayerParams.pixelShiftAutomatic = false; + bayerParams.pixelshiftShowMotion = false; + } + + if((bayerParams.pixelShiftMotion > 0 || bayerParams.pixelShiftAutomatic)) { + if(bayerParams.pixelShiftMedian) { // We need the amaze demosaiced frames for motion correction +#ifdef PIXELSHIFTDEV + if(!bayerParams.pixelShiftMedian3) { +#endif + if(bayerParams.pixelShiftLmmse) { + lmmse_interpolate_omp(winw, winh, *(rawDataFrames[0]), red, green, blue, bayerParams.lmmse_iterations); + } else { + amaze_demosaic_RT (0, 0, winw, winh, *(rawDataFrames[0]), red, green, blue); + } + multi_array2D redTmp(W,H); + multi_array2D greenTmp(W,H); + multi_array2D blueTmp(W,H); + for(int i=0;i<3;i++) { + if(bayerParams.pixelShiftLmmse) { + lmmse_interpolate_omp(winw, winh, *(rawDataFrames[i+1]), redTmp[i], greenTmp[i], blueTmp[i], bayerParams.lmmse_iterations); + } else { + amaze_demosaic_RT (0, 0, winw, winh, *(rawDataFrames[i+1]), redTmp[i], greenTmp[i], blueTmp[i]); + } + } + #pragma omp parallel for schedule(dynamic,16) + for(int i=border;i redTmp(W,H); + multi_array2D greenTmp(W,H); + multi_array2D blueTmp(W,H); + for(int i=0, frameIndex = 0;i<4;++i) { + if(i != currFrame) { + if(bayerParams.pixelShiftLmmse) { + lmmse_interpolate_omp(winw, winh, *(rawDataFrames[i]), redTmp[frameIndex], greenTmp[frameIndex], blueTmp[frameIndex], raw.bayersensor.lmmse_iterations); + } else { + amaze_demosaic_RT (0, 0, winw, winh, *(rawDataFrames[i]), redTmp[frameIndex], greenTmp[frameIndex], blueTmp[frameIndex]); + } + ++frameIndex; + } + } + unsigned int offsX0 = 0, offsY0 = 0; + unsigned int offsX1 = 0, offsY1 = 0; + unsigned int offsX2 = 0, offsY2 = 0; + + // We have to adjust the offsets for the selected subframe we exclude from median + switch (currFrame) { + case 0: + offsY0 = 1; + offsX0 = 0; + offsY1 = 1; + offsX1 = 1; + offsY2 = 0; + offsX2 = 1; + break; + + case 1: + offsY0 = 0; + offsX0 = 0; + offsY1 = 1; + offsX1 = 1; + offsY2 = 0; + offsX2 = 1; + break; + + case 2: + offsY0 = 0; + offsX0 = 0; + offsY1 = 1; + offsX1 = 0; + offsY2 = 0; + offsX2 = 1; + break; + + case 3: + offsY0 = 0; + offsX0 = 0; + offsY1 = 1; + offsX1 = 0; + offsY2 = 1; + offsX2 = 1; + } + + #pragma omp parallel for schedule(dynamic,16) + for(int i=border;i 0; + float stddevFactorGreen = bayerParams.pixelShiftStddevFactorGreen; + float stddevFactorRed = bayerParams.pixelShiftStddevFactorRed; + float stddevFactorBlue = bayerParams.pixelShiftStddevFactorBlue; + float nreadIso = bayerParams.pixelShiftNreadIso; + float prnu = bayerParams.pixelShiftPrnu; + const float redBlueWeight = bayerParams.pixelShiftRedBlueWeight + 1.f; +#else + float stddevFactorGreen = 5.f; + float stddevFactorRed = 5.f; + float stddevFactorBlue = 5.f; + float nreadIso = 0.f; + float prnu = 1.f; + const float redBlueWeight = 0.7f + 1.f; +#endif + float eperIso = bayerParams.pixelShiftEperIso; + const bool checkNonGreenHorizontal = bayerParams.pixelShiftNonGreenHorizontal; + const bool checkNonGreenVertical = bayerParams.pixelShiftNonGreenVertical; + const bool checkNonGreenCross = bayerParams.pixelShiftNonGreenCross; + const bool checkNonGreenAmaze = bayerParams.pixelShiftNonGreenAmaze; + const bool checkNonGreenCross2 = bayerParams.pixelShiftNonGreenCross2; + const bool checkGreen = bayerParams.pixelShiftGreen; + const float greenWeight = 2.f; + const bool blurMap = bayerParams.pixelShiftBlur; + const float sigma = bayerParams.pixelShiftSigma; +#ifdef PIXELSHIFTDEV + const float threshold = bayerParams.pixelShiftSum + 9.f; +#else + constexpr float threshold = 3.f + 9.f; +#endif + const bool experimental0 = bayerParams.pixelShiftExp0; + const bool holeFill = bayerParams.pixelShiftHoleFill; + const bool equalBrightness = bayerParams.pixelShiftEqualBright; + const bool smoothTransitions = blurMap && bayerParams.pixelShiftSmoothFactor > 0. && !showOnlyMask; + const bool automatic = bayerParams.pixelShiftMotionCorrectionMethod == RAWParams::BayerSensor::Automatic; + const float smoothFactor = 1.0 - bayerParams.pixelShiftSmoothFactor; + + static const float nReadK3II[] = { 3.4f, // ISO 100 + 3.1f, // ISO 125 + 2.5f, // ISO 160 + 2.5f, // ISO 200 + 2.5f, // ISO 250 + 2.5f, // ISO 320 + 2.3f, // ISO 400 + 2.5f, // ISO 500 + 2.3f, // ISO 640 + 2.3f, // ISO 800 + 2.4f, // ISO 1000 + 2.3f, // ISO 1250 + 1.75f, // ISO 1600 + 1.75f, // ISO 2000 + 1.75f, // ISO 2500 + 1.75f, // ISO 3200 + 1.75f, // ISO 4000 + 1.75f, // ISO 5000 + 1.75f, // ISO 6400 + 1.75f, // ISO 8000 + 1.75f, // ISO 10000 + 1.5f, // ISO 12800 + 1.5f, // ISO 16000 + 1.5f, // ISO 20000 + 1.5f, // ISO 25600 + 1.5f, // ISO 32000 + 1.5f, // ISO 40000 + 1.5f, // ISO 51200 + 1.5f // ISO > 51200 (we get a max ISO value of 65535 from dcraw) + }; + + static const float ePerIsoK3II = 0.35f; + + static const float nReadK1[] = { 3.45f, // ISO 100 + 3.15f, // ISO 125 + 3.45f, // ISO 160 + 3.0f, // ISO 200 + 3.0f, // ISO 250 + 3.0f, // ISO 320 + 2.7f, // ISO 400 + 2.7f, // ISO 500 + 2.7f, // ISO 640 + 2.5f, // ISO 800 + 2.5f, // ISO 1000 + 2.5f, // ISO 1250 + 2.4f, // ISO 1600 + 2.4f, // ISO 2000 + 2.4f, // ISO 2500 + 2.4f, // ISO 3200 + 2.4f, // ISO 4000 + 2.4f, // ISO 5000 + 2.4f, // ISO 6400 + 2.4f, // ISO 8000 + 2.4f, // ISO 10000 + 2.4f, // ISO 12800 + 2.4f, // ISO 16000 + 2.4f, // ISO 20000 + 2.4f, // ISO 25600 + 2.4f, // ISO 32000 + 2.4f, // ISO 40000 + 2.4f, // ISO 51200 + 2.4f, // ISO 64000 + 2.4f, // ISO 80000 + 2.4f, // ISO 102400 + 2.4f, // ISO 128000 + 2.4f, // ISO 160000 + 2.4f // ISO 204800 + }; + + static const float ePerIsoK1 = 0.75f; + + static const float nReadK70[] = { 4.0f, // ISO 100 + 4.0f, // ISO 125 + 4.0f, // ISO 160 + 4.0f, // ISO 200 + 4.0f, // ISO 250 + 4.0f, // ISO 320 + 4.0f, // ISO 400 + 4.0f, // ISO 500 + 4.0f, // ISO 640 + 3.0f, // ISO 800 + 3.0f, // ISO 1000 + 3.0f, // ISO 1250 + 3.0f, // ISO 1600 + 3.0f, // ISO 2000 + 3.0f, // ISO 2500 + 3.0f, // ISO 3200 + 3.0f, // ISO 4000 + 3.0f, // ISO 5000 + 3.0f, // ISO 6400 + 3.0f, // ISO 8000 + 3.0f, // ISO 10000 + 3.0f, // ISO 12800 + 3.0f, // ISO 16000 + 3.0f, // ISO 20000 + 3.0f, // ISO 25600 + 3.0f, // ISO 32000 + 3.0f, // ISO 40000 + 3.0f, // ISO 51200 + 3.0f // ISO > 51200 (we get a max ISO value of 65535 from dcraw) + }; + + static const float ePerIsoK70 = 0.5f; + + if (plistener) { + plistener->setProgressStr (Glib::ustring::compose(M("TP_RAW_DMETHOD_PROGRESSBAR"), RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift])); + plistener->setProgress(0.0); + } + + + const bool skip = (gridSize_ == RAWParams::BayerSensor::ePSMotionCorrection::Grid1x2); + int gridSize = 1; + bool nOf3x3 = false; + + switch (gridSize_) { + case RAWParams::BayerSensor::ePSMotionCorrection::Grid1x1: + case RAWParams::BayerSensor::ePSMotionCorrection::Grid1x2: + gridSize = 1; + break; + + case RAWParams::BayerSensor::ePSMotionCorrection::Grid3x3: + gridSize = 3; + break; + + case RAWParams::BayerSensor::ePSMotionCorrection::Grid5x5: + gridSize = 5; + break; + + case RAWParams::BayerSensor::ePSMotionCorrection::Grid7x7: + gridSize = 7; + break; + + case RAWParams::BayerSensor::ePSMotionCorrection::Grid3x3New: + gridSize = 1; + nOf3x3 = true; + } + + if(adaptive && blurMap && nOf3x3 && smoothFactor == 0.f && !showMotion) { + if(plistener) { + plistener->setProgress(1.0); + } + + return; + } + +#ifdef PIXELSHIFTDEV + // Lookup table for non adaptive (slider) mode + LUTf log2Lut(32768, LUT_CLIP_BELOW | LUT_CLIP_ABOVE); + + if(detectMotion && !adaptive) { + const float lutStrength = 2.f; + log2Lut[0] = 0; + + for(int i = 2; i < 65536; i += 2) { + log2Lut[i >> 1] = lutStrength * log2(i) / 100.f; + } + } +#endif + + const float scaleGreen = 1.f / scale_mul[1]; + + float nRead; + float eperIsoModel; + + int nReadIndex = static_cast(round(log2(idata->getISOSpeed() / 100.f) * 3.f)); + + if(model.find("K-3") != string::npos) { + nRead = nReadK3II[nReadIndex]; + eperIsoModel = ePerIsoK3II; + } else if(model.find("K-1") != string::npos) { + nRead = nReadK1[nReadIndex]; + eperIsoModel = ePerIsoK1; + } else { + nRead = nReadK70[nReadIndex]; + eperIsoModel = ePerIsoK70; + } + + nRead *= pow(2.f, nreadIso); + eperIsoModel *= pow(2.f, eperIso); + +#ifdef PIXELSHIFTDEV + if(adaptive && experimental0) { + eperIso = eperIsoModel * sqrtf(100.f / (rawWpCorrection * idata->getISOSpeed())); + } else { + eperIso = eperIsoModel * (100.f / (rawWpCorrection * idata->getISOSpeed())); + } +#else + eperIso = eperIsoModel * (100.f / (rawWpCorrection * idata->getISOSpeed())); +#endif + +#ifdef PIXELSHIFTDEV + std::cout << "WL: " << c_white[0] << " BL: " << c_black[0] << " ePerIso multiplicator: " << (65535.f / (c_white[0] - c_black[0])) << std::endl; +#endif + float eperIsoRed = (eperIso / scale_mul[0]) * (65535.f / (c_white[0] - c_black[0])); + float eperIsoGreen = (eperIso * scaleGreen) * (65535.f / (c_white[1] - c_black[1])); + float eperIsoBlue = (eperIso / scale_mul[2]) * (65535.f / (c_white[2] - c_black[2])); + + const float clippedRed = 65535.f / scale_mul[0]; + const float clippedBlue = 65535.f / scale_mul[2]; + + prnu /= 100.f; + stddevFactorGreen *= stddevFactorGreen; + stddevFactorRed *= stddevFactorRed; + stddevFactorBlue *= stddevFactorBlue; + + + nRead *= nRead; + + // If the values of two corresponding green pixels differ my more then motionThreshold %, the pixel will be treated as a badGreen pixel + float motionThreshold = 1.f - (motion / 100.f); + // For shades of green motion indicators + const float blendFactor = ((adaptive || motion == 0.f) ? 1.f : 1.f / (1.f - motionThreshold)); + + unsigned int offsX = 0, offsY = 0; + + if(!bayerParams.pixelShiftMedian || !adaptive) { + // We have to adjust the offsets for the selected subframe we use for areas with motion + switch (frame) { + case 0: + offsX = offsY = 0; + break; + + case 1: + offsX = 0; + offsY = 1; + break; + + case 2: + offsX = offsY = 1; + break; + + case 3: + offsX = 1; + offsY = 0; + } + } + + const float thresh = adaptive ? 0.f : motionThreshold; + array2D psRed(winw + 32, winh); // increase width to avoid cache conflicts + array2D psG1(winw + 32, winh); + array2D psG2(winw + 32, winh); + array2D psBlue(winw + 32, winh); + + // calculate average green brightness for each frame + float greenBrightness[4] = {1.f, 1.f, 1.f, 1.f}; + + if(equalBrightness) { + LUT *histo[4]; + + for(int i = 0; i < 4; ++i) { + histo[i] = new LUT(65536); + histo[i]->clear(); + } + +#ifdef _OPENMP + #pragma omp parallel +#endif + { + LUT *histoThr[4]; + + for(int i = 0; i < 4; ++i) { + histoThr[i] = new LUT(65536); + histoThr[i]->clear(); + } + +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) nowait +#endif + + for(int i = winy + 1; i < winh - 1; ++i) { + int j = winx + 1; + int c = FC(i, j); + + // offset to keep the code short. It changes its value between 0 and 1 for each iteration of the loop + unsigned int offset = c & 1; + + for(; j < winw - 1; ++j) { + float green1 = (*rawDataFrames[1 - offset])[i - offset + 1][j]; + float green2 = (*rawDataFrames[3 - offset])[i + offset][j + 1]; + (*histoThr[1 - offset])[green1]++; + (*histoThr[3 - offset])[green2]++; + offset ^= 1; // 0 => 1 or 1 => 0 + } + } + + #pragma omp critical + { + for(int i = 0; i < 4; ++i) { + (*histo[i]) += (*histoThr[i]); + delete histoThr[i]; + } + } + } + + float medians[4]; + + for(int i = 0; i < 4; ++i) { + //find median of histogram + uint32_t median = 0, count = 0; + uint32_t datalen = (winh - 2) * (winw - 2) / 2; + + while (count < datalen / 2) { + count += (*histo[i])[median]; + ++median; + } + + const float weight = (count - datalen / 2.f) / (*histo[i])[median - 1]; + medians[i] = intp(weight, (float)(median - 2), (float)(median - 1)); + delete histo[i]; + } + + for(int i = 0; i < 4; ++i) { + greenBrightness[i] = medians[frame] / medians[i]; + } + +#ifdef PIXELSHIFTDEV + std::cout << "brightness factors by median : " << greenBrightness[0] << " " << greenBrightness[1] << " " << greenBrightness[2] << " " << greenBrightness[3] << std::endl; +#endif + + } + +// fill channels psRed, psG1, psG2 and psBlue +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) +#endif + + for(int i = winy + 1; i < winh - 1; ++i) { + float *greenDest1 = psG1[i]; + float *greenDest2 = psG2[i]; + float *nonGreenDest0 = psRed[i]; + float *nonGreenDest1 = psBlue[i]; + int j = winx + 1; + int c = FC(i, j); + + if ((c + FC(i, j + 1)) == 3) { + // row with blue pixels => swap destination pointers for non green pixels + std::swap(nonGreenDest0, nonGreenDest1); + std::swap(greenDest1, greenDest2); + } + + // offset to keep the code short. It changes its value between 0 and 1 for each iteration of the loop + unsigned int offset = c & 1; + + for(; j < winw - 1; ++j) { + // store the values from the 4 frames into 4 different temporary planes + greenDest1[j] = (*rawDataFrames[1 - offset])[i - offset + 1][j] * greenBrightness[1 - offset]; + greenDest2[j] = (*rawDataFrames[3 - offset])[i + offset][j + 1] * greenBrightness[3 - offset]; + nonGreenDest0[j] = (*rawDataFrames[(offset << 1) + offset])[i][j + offset] * greenBrightness[(offset << 1) + offset]; + nonGreenDest1[j] = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1] * greenBrightness[2 - offset]; + offset ^= 1; // 0 => 1 or 1 => 0 + } + } + +// now that the temporary planes are filled for easy access we do the motion detection +#ifdef PIXELSHIFTDEV + int sum[2] = {0}; + float pixelcount = ((winh - (border + offsY) - (winy + border - offsY)) * (winw - (border + offsX) - (winx + border - offsX))) / 2.f; +#endif + + array2D psMask(winw, winh); + +#ifdef _OPENMP + #pragma omp parallel +#endif + { +#ifdef PIXELSHIFTDEV + int sumThr[2] = {0}; +#endif +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) nowait +#endif + + for(int i = winy + border - offsY; i < winh - (border + offsY); ++i) { + float *greenDest = green[i + offsY]; + float *redDest = red[i + offsY]; + float *blueDest = blue[i + offsY]; + int j = winx + border - offsX; + +#ifdef PIXELSHIFTDEV + float greenDifMax[gridSize]; // Here we store the maximum differences per Column + + // green channel motion detection checks the grid around the pixel for differences in green channels + + if(detectMotion || (adaptive && checkGreen)) { + if(gridSize == 3) { + // compute maximum of differences for first two columns of 3x3 grid + greenDifMax[0] = std::max({greenDiff(psG1[i - 1][j - 1], psG2[i - 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j - 1], psG2[ i ][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j - 1], psG2[i + 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); + greenDifMax[1] = std::max({greenDiff(psG1[i - 1][ j ], psG2[i - 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][ j ], psG2[ i ][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][ j ], psG2[i + 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); + } else if(gridSize == 5) { + // compute maximum of differences for first four columns of 5x5 grid + greenDifMax[0] = std::max({greenDiff(psG1[i - 2][j - 2], psG2[i - 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][j - 2], psG2[i - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j - 2], psG2[ i ][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j - 2], psG2[i + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][j - 2], psG2[i + 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); + greenDifMax[1] = std::max({greenDiff(psG1[i - 2][j - 1], psG2[i - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][j - 1], psG2[i - 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j - 1], psG2[ i ][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j - 1], psG2[i + 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][j - 1], psG2[i + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); + greenDifMax[2] = std::max({greenDiff(psG1[i - 2][ j ], psG2[i - 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][ j ], psG2[i - 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][ j ], psG2[ i ][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][ j ], psG2[i + 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][ j ], psG2[i + 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); + greenDifMax[3] = std::max({greenDiff(psG1[i - 2][j + 1], psG2[i - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][j + 1], psG2[i - 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j + 1], psG2[ i ][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j + 1], psG2[i + 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][j + 1], psG2[i + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); + } else if(gridSize == 7) { + // compute maximum of differences for first six columns of 7x7 grid + greenDifMax[0] = std::max({greenDiff(psG1[i - 3][j - 3], psG2[i - 3][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 2][j - 3], psG2[i - 2][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][j - 3], psG2[i - 1][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j - 3], psG2[ i ][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j - 3], psG2[i + 1][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][j - 3], psG2[i + 2][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 3][j - 3], psG2[i + 3][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); + greenDifMax[1] = std::max({greenDiff(psG1[i - 3][j - 2], psG2[i - 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 2][j - 2], psG2[i - 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][j - 2], psG2[i - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j - 2], psG2[ i ][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j - 2], psG2[i + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][j - 2], psG2[i + 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 3][j - 2], psG2[i + 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); + greenDifMax[2] = std::max({greenDiff(psG1[i - 3][j - 1], psG2[i - 3][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 2][j - 1], psG2[i - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][j - 1], psG2[i - 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j - 1], psG2[ i ][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j - 1], psG2[i + 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][j - 1], psG2[i + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 3][j - 1], psG2[i + 3][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); + greenDifMax[3] = std::max({greenDiff(psG1[i - 3][ j ], psG2[i - 3][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 2][ j ], psG2[i - 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][ j ], psG2[i - 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][ j ], psG2[ i ][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][ j ], psG2[i + 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][ j ], psG2[i + 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 3][ j ], psG2[i + 3][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); + greenDifMax[4] = std::max({greenDiff(psG1[i - 3][j + 1], psG2[i - 3][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 2][j + 1], psG2[i - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][j + 1], psG2[i - 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j + 1], psG2[ i ][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j + 1], psG2[i + 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][j + 1], psG2[i + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 3][j + 1], psG2[i + 3][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); + greenDifMax[5] = std::max({greenDiff(psG1[i - 3][j + 2], psG2[i - 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 2][j + 2], psG2[i - 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][j + 2], psG2[i - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j + 2], psG2[ i ][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j + 2], psG2[i + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][j + 2], psG2[i + 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 3][j + 2], psG2[i + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); + } + + } + + // this is the index for the last column of the grid. Obviously we have to start with gridSize - 1 + int lastIndex = gridSize - 1; + float korr = 0.f; + bool blueRow = false; +#endif + + int c = FC(i, j); + +#ifdef PIXELSHIFTDEV + if (c == 2 || ((c & 1) && FC(i, j + 1) == 2)) { + // row with blue pixels => swap destination pointers for non green pixels + blueRow = true; + } +#endif + // offset to keep the code short. It changes its value between 0 and 1 for each iteration of the loop + unsigned int offset = c & 1; + + for(; j < winw - (border + offsX); ++j) { + psMask[i][j] = 1.f; + + offset ^= 1; // 0 => 1 or 1 => 0 + +#ifdef PIXELSHIFTDEV + if(detectMotion || (adaptive && checkGreen)) { + bool skipNext = false; +#else + if(adaptive && checkGreen) { +#endif + float gridMax; + +#ifdef PIXELSHIFTDEV + + if(gridSize < 2) { + // compute difference for current pixel and skip next pixel, that's roughly the method from dcrawps +#endif + gridMax = greenDiff(psG1[i][j], psG2[i][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion); +#ifdef PIXELSHIFTDEV + + skipNext = skip; + } else if(gridSize == 3) { + // compute maximum of differences for third column of 3x3 grid and save at position lastIndex + greenDifMax[lastIndex] = std::max({greenDiff(psG1[i - 1][j + 1], psG2[i - 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j + 1], psG2[ i ][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j + 1], psG2[i + 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); + // calculate maximum of whole grid by calculating maximum of grid column max values + gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2]}); + // adjust index for next column + lastIndex ++; + lastIndex = lastIndex == gridSize ? 0 : lastIndex; + } else if(gridSize == 5) { + // compute maximum of differences for fifth column of 5x5 grid and save at position lastIndex + greenDifMax[lastIndex] = std::max({greenDiff(psG1[i - 2][j + 2], psG2[i - 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][j + 2], psG2[i - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j + 2], psG2[ i ][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j + 2], psG2[i + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][j + 2], psG2[i + 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); + // calculate maximum of whole grid by calculating maximum of grid column max values + gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4]}); + // adjust index for next column + lastIndex ++; + lastIndex = lastIndex == gridSize ? 0 : lastIndex; + } else if(gridSize == 7) { + // compute maximum of differences for 7th column of 7x7 grid and save at position lastIndex + greenDifMax[lastIndex] = std::max({greenDiff(psG1[i - 3][j + 3], psG2[i - 3][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 2][j + 3], psG2[i - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][j + 3], psG2[i - 1][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j + 3], psG2[ i ][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j + 3], psG2[i + 1][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][j + 3], psG2[i + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 3][j + 3], psG2[i + 3][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + }); + // calculate maximum of whole grid by calculating maximum of grid column max values + gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4], greenDifMax[5], greenDifMax[6]}); + // adjust index for next column + lastIndex ++; + lastIndex = lastIndex == gridSize ? 0 : lastIndex; + } + + if(!adaptive) { + // increase motion detection dependent on brightness + korr = log2Lut[((int)(psG1[i][j] * scaleGreen)) >> 1]; + } + + if (gridMax > thresh - korr) { +#else + if (gridMax > thresh) { + +#endif + +#ifdef PIXELSHIFTDEV + sumThr[offset] ++; + + if(nOf3x3) { +#endif + psMask[i][j] = greenWeight; +#ifdef PIXELSHIFTDEV + } + + else if((offset == (frame & 1)) && checkNonGreenVertical) { + if(frame > 1) { + green[i + offsY][j + offsX] = blueRow ? psG1[i][j] : psG2[i][j]; + } else { + green[i + offsY][j + offsX] = blueRow ? psG2[i][j] : psG1[i][j];; + } + + } else { + // at least one of the tested green pixels of the grid is detected as motion + paintMotionMask(j + offsX, showMotion, (gridMax - thresh + korr) * blendFactor, showOnlyMask, greenDest, redDest, blueDest); + + if(skipNext) { + // treat the horizontally next pixel also as motion + j++; + paintMotionMask(j + offsX, showMotion, (gridMax - thresh + korr) * blendFactor, showOnlyMask, greenDest, redDest, blueDest); + } + } + +#endif + // do not set the motion pixel values. They have already been set by demosaicer or showMotion + continue; + } + } + + if(adaptive) { + if(checkNonGreenCross) { + // check red cross + float redTop = psRed[i - 1][ j ]; + float redLeft = psRed[ i ][j - 1]; + float redCentre = psRed[ i ][ j ]; + float redRight = psRed[ i ][j + 1]; + float redBottom = psRed[i + 1][ j ]; + float redDiff = nonGreenDiffCross(redRight, redLeft, redTop, redBottom, redCentre, clippedRed, stddevFactorRed, eperIsoRed, nRead, prnu, showMotion); + + if(redDiff > 0.f) { +#ifdef PIXELSHIFTDEV + + if(nOf3x3) { +#endif + psMask[i][j] = redBlueWeight; +#ifdef PIXELSHIFTDEV + } else { + paintMotionMask(j + offsX, showMotion, redDiff, showOnlyMask, redDest, blueDest, greenDest); + } + +#endif + continue; + } + + // check blue cross + float blueTop = psBlue[i - 1][ j ]; + float blueLeft = psBlue[ i ][j - 1]; + float blueCentre = psBlue[ i ][ j ]; + float blueRight = psBlue[ i ][j + 1]; + float blueBottom = psBlue[i + 1][ j ]; + float blueDiff = nonGreenDiffCross(blueRight, blueLeft, blueTop, blueBottom, blueCentre, clippedBlue, stddevFactorBlue, eperIsoBlue, nRead, prnu, showMotion); + + if(blueDiff > 0.f) { +#ifdef PIXELSHIFTDEV + + if(nOf3x3) { +#endif + psMask[i][j] = redBlueWeight; +#ifdef PIXELSHIFTDEV + } else { + paintMotionMask(j + offsX, showMotion, blueDiff, showOnlyMask, blueDest, redDest, greenDest); + } + +#endif + continue; + + } + } + +#ifdef PIXELSHIFTDEV + + if(checkNonGreenHorizontal) { + float redLeft = psRed[ i ][j - 1]; + float redCentre = psRed[ i ][ j ]; + float redRight = psRed[ i ][j + 1]; + + float redDiffLeft = redLeft - redCentre; + float redDiffRight = redRight - redCentre; + + if(redDiffLeft * redDiffRight >= 0.f) { + float redAvg = (redRight + redLeft) / 2.f; + float redDiffHor = nonGreenDiff(redCentre, redAvg, stddevFactorRed, eperIsoRed, nRead, prnu, showMotion); + + if(redDiffHor > 0.f) { + if(nOf3x3) { + psMask[i][j] = redBlueWeight; + } else { + paintMotionMask(j + offsX, showMotion, redDiffHor, showOnlyMask, redDest, blueDest, greenDest); + } + + continue; + } + } + + float blueLeft = psBlue[ i ][j - 1]; + float blueCentre = psBlue[ i ][ j ]; + float blueRight = psBlue[ i ][j + 1]; + + float blueDiffLeft = blueLeft - blueCentre; + float blueDiffRight = blueRight - blueCentre; + + if(blueDiffLeft * blueDiffRight >= 0.f) { + float blueAvg = (blueRight + blueLeft) / 2.f; + float blueDiffHor = nonGreenDiff(blueCentre, blueAvg, stddevFactorBlue, eperIsoBlue, nRead, prnu, showMotion); + + if(blueDiffHor > 0.f) { + if(nOf3x3) { + psMask[i][j] = redBlueWeight; + } else { + paintMotionMask(j + offsX, showMotion, blueDiffHor, showOnlyMask, blueDest, redDest, greenDest); + } + + continue; + } + } + } + + if(checkNonGreenVertical) { + // check red vertically + float redTop = psRed[i - 1][ j ]; + float redCentre = psRed[ i ][ j ]; + float redBottom = psRed[i + 1][ j ]; + + float redDiffTop = redTop - redCentre; + float redDiffBottom = redBottom - redCentre; + + if(redDiffTop * redDiffBottom >= 0.f) { + float redAvg = (redTop + redBottom) / 2.f; + float redDiff = nonGreenDiff(redCentre, redAvg, stddevFactorRed, eperIsoRed, nRead, prnu, showMotion); + + if(redDiff > 0.f) { + if(nOf3x3) { + psMask[i][j] = redBlueWeight; + } else { + paintMotionMask(j + offsX, showMotion, redDiff, showOnlyMask, redDest, blueDest, greenDest); + } + + continue; + } + } + + // check blue vertically + float blueTop = psBlue[i - 1][ j ]; + float blueCentre = psBlue[ i ][ j ]; + float blueBottom = psBlue[i + 1][ j ]; + + float blueDiffTop = blueTop - blueCentre; + float blueDiffBottom = blueBottom - blueCentre; + + if(blueDiffTop * blueDiffBottom >= 0.f) { + float blueAvg = (blueTop + blueBottom) / 2.f; + float blueDiff = nonGreenDiff(blueCentre, blueAvg, stddevFactorBlue, eperIsoBlue, nRead, prnu, showMotion); + + if(blueDiff > 0.f) { + if(nOf3x3) { + psMask[i][j] = redBlueWeight; + } else { + paintMotionMask(j + offsX, showMotion, blueDiff, showOnlyMask, blueDest, redDest, greenDest); + } + + continue; + } + } + } + + if(checkNonGreenAmaze) { + // check current pixel against amaze + float redCentre = psRed[ i ][ j ]; + float redAmaze = red[i + offsY][j + offsX]; + + float redDiffAmaze = nonGreenDiff(redCentre, redAmaze, stddevFactorRed, eperIsoRed, nRead, prnu, showMotion); + + if(redDiffAmaze > 0.f) { + if(nOf3x3) { + psMask[i][j] = redBlueWeight; + } else { + paintMotionMask(j + offsX, showMotion, redDiffAmaze, showOnlyMask, redDest, blueDest, greenDest); + } + + continue; + } + + float blueCentre = psBlue[ i ][ j ]; + float blueAmaze = blue[i + offsY][j + offsX]; + + float blueDiffAmaze = nonGreenDiff(blueCentre, blueAmaze, stddevFactorBlue, eperIsoBlue, nRead, prnu, showMotion); + + if(blueDiffAmaze > 0.f) { + if(nOf3x3) { + psMask[i][j] = redBlueWeight; + } else { + paintMotionMask(j + offsX, showMotion, blueDiffAmaze, showOnlyMask, blueDest, redDest, greenDest); + } + + continue; + } + } + + if(checkNonGreenCross2) { // for green amaze + float greenCentre = (psG1[ i ][ j ] + psG2[ i ][ j ]) / 2.f; + float greenAmaze = green[i + offsY][j + offsX]; + float greenDiffAmaze = nonGreenDiff(greenCentre, greenAmaze, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion); + + if(greenDiffAmaze > 0.f) { + if(nOf3x3) { + psMask[i][j] = greenWeight; + } else { + paintMotionMask(j + offsX, showMotion, greenDiffAmaze, showOnlyMask, greenDest, redDest, blueDest); + } + + continue; + } + } + + if(experimental0) { // for experiments + + } + +#endif + } + + if(showOnlyMask) { // we want only motion mask => paint areas without motion in pure black + red[i + offsY][j + offsX] = green[i + offsY][j + offsX] = blue[i + offsY][j + offsX] = 0.f; + } else if(!(adaptive && nOf3x3)) { + // no motion detected, replace the a priori demosaiced values by the pixelshift combined values + red[i + offsY][j + offsX] = psRed[i][j]; + green[i + offsY][j + offsX] = (psG1[i][j] + psG2[i][j]) / 2.f; + blue[i + offsY][j + offsX] = psBlue[i][j]; + } + } + } + +#ifdef PIXELSHIFTDEV + +#ifdef _OPENMP + #pragma omp critical +#endif + { + sum[0] += sumThr[0]; + sum[1] += sumThr[1]; + } +#endif + } + + +#ifdef PIXELSHIFTDEV + float percent0 = 100.f * sum[0] / pixelcount; + float percent1 = 100.f * sum[1] / pixelcount; + + std::cout << fileName << " : Green detections at stddev " << std::setprecision( 2 ) << bayerParams.pixelShiftStddevFactorGreen << " : Frame 1/3 : " << std::setprecision( 6 ) << sum[0] << " (" << percent0 << "%)" << " Frame 2/4 : " << sum[1] << " (" << percent1 << "%)" << std::endl; +#endif + + if(adaptive && nOf3x3) { + if(blurMap) { + #pragma omp parallel + { + gaussianBlur(psMask, psMask, winw, winh, sigma); + } + } + + array2D mask(W, H, ARRAY2D_CLEAR_DATA); + array2D maskInv(W, H, ARRAY2D_CLEAR_DATA); + + #pragma omp parallel for schedule(dynamic,16) + + for(int i = winy + border - offsY; i < winh - (border + offsY); ++i) { + int j = winx + border - offsX; + float v3sum[3] = {0.f}; + + for(int v = -1; v <= 1; v++) { + for(int h = -1; h < 1; h++) { + v3sum[1 + h] += (psMask[i + v][j + h]); + } + } + + float blocksum = v3sum[0] + v3sum[1]; + + for(int voffset = 2; j < winw - (border + offsX); ++j, ++voffset) { + float colSum = psMask[i - 1][j + 1] + psMask[i][j + 1] + psMask[i + 1][j + 1]; + voffset = voffset == 3 ? 0 : voffset; // faster than voffset %= 3; + blocksum -= v3sum[voffset]; + blocksum += colSum; + v3sum[voffset] = colSum; + + if(blocksum >= threshold) { + mask[i][j] = 255; + } + } + } + + if(holeFill) { + invertMask(winx + border - offsX, winw - (border + offsX), winy + border - offsY, winh - (border + offsY), mask, maskInv); + floodFill4(winx + border - offsX, winw - (border + offsX), winy + border - offsY, winh - (border + offsY), maskInv); + xorMasks(winx + border - offsX, winw - (border + offsX), winy + border - offsY, winh - (border + offsY), maskInv, mask); + } + + + #pragma omp parallel for schedule(dynamic,16) + + for(int i = winy + border - offsY; i < winh - (border + offsY); ++i) { +#ifdef __SSE2__ + + if(smoothTransitions) { // + vfloat onev = F2V(1.f); + vfloat smoothv = F2V(smoothFactor); + int j = winx + border - offsX; + + for(; j < winw - (border + offsX) - 3; j += 4) { + vfloat blendv = vmaxf(LVFU(psMask[i][j]), onev) - onev; + blendv = pow_F(blendv, smoothv); + blendv = vself(vmaskf_eq(smoothv, ZEROV), onev, blendv); + STVFU(psMask[i][j], blendv); + } + + for(; j < winw - (border + offsX); ++j) { + psMask[i][j] = smoothFactor == 0.f ? 1.f : pow_F(std::max(psMask[i][j] - 1.f, 0.f), smoothFactor); + } + } + +#endif + float *greenDest = green[i + offsY]; + float *redDest = red[i + offsY]; + float *blueDest = blue[i + offsY]; + + for(int j = winx + border - offsX; j < winw - (border + offsX); ++j) { + if(mask[i][j] == 255) { + paintMotionMask(j + offsX, showMotion, 0.5f, showOnlyMask, greenDest, redDest, blueDest); + } else if(showOnlyMask) { // we want only motion mask => paint areas without motion in pure black + red[i + offsY][j + offsX] = green[i + offsY][j + offsX] = blue[i + offsY][j + offsX] = 0.f; + } else { + if(smoothTransitions) { +#ifdef __SSE2__ + float blend = psMask[i][j]; +#else + float blend = smoothFactor == 0.f ? 1.f : pow_F(std::max(psMask[i][j] - 1.f, 0.f), smoothFactor); +#endif + red[i + offsY][j + offsX] = intp(blend, red[i + offsY][j + offsX], psRed[i][j] ); + green[i + offsY][j + offsX] = intp(blend, green[i + offsY][j + offsX], (psG1[i][j] + psG2[i][j]) * 0.5f); + blue[i + offsY][j + offsX] = intp(blend, blue[i + offsY][j + offsX], psBlue[i][j]); + } else { + red[i + offsY][j + offsX] = psRed[i][j]; + green[i + offsY][j + offsX] = (psG1[i][j] + psG2[i][j]) * 0.5f; + blue[i + offsY][j + offsX] = psBlue[i][j]; + } + } + } + } + } + + if(plistener) { + plistener->setProgress(1.0); + } +} diff --git a/rtengine/procevents.h b/rtengine/procevents.h index bca679bdb..a1c6f58f9 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -471,6 +471,36 @@ enum ProcEvent { EvLskal = 441, EvOBPCompens = 442, EvWBtempBias = 443, + EvRawImageNum = 444, + EvPixelShiftMotion = 445, + EvPixelShiftMotionCorrection = 446, + EvPixelShiftStddevFactorGreen = 447, + EvPixelShiftEperIso = 448, + EvPixelShiftNreadIso = 449, + EvPixelShiftPrnu = 450, + EvPixelshiftShowMotion = 451, + EvPixelshiftShowMotionMaskOnly = 452, + EvPixelShiftAutomatic = 453, + EvPixelShiftNonGreenHorizontal = 454, + EvPixelShiftNonGreenVertical = 455, + EvPixelShiftNonGreenCross = 456, + EvPixelShiftStddevFactorRed = 457, + EvPixelShiftStddevFactorBlue = 458, + EvPixelShiftGreenAmaze = 459, + EvPixelShiftNonGreenAmaze = 460, + EvPixelShiftGreen = 461, + EvPixelShiftRedBlueWeight = 462, + EvPixelShiftBlur = 463, + EvPixelShiftSigma = 464, + EvPixelShiftSum = 465, + EvPixelShiftExp0 = 466, + EvPixelShiftHoleFill = 467, + EvPixelShiftMedian = 468, + EvPixelShiftMedian3 = 469, + EvPixelShiftMotionMethod = 470, + EvPixelShiftSmooth = 471, + EvPixelShiftLmmse = 472, + EvPixelShiftEqualBright = 473, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index f01fa57d2..9da09998d 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -40,7 +40,7 @@ const int br = (int) options.rtSettings.bot_right; const int tl = (int) options.rtSettings.top_left; const int bl = (int) options.rtSettings.bot_left; -const char *RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::numMethods] = {"amaze", "igv", "lmmse", "eahd", "hphd", "vng4", "dcb", "ahd", "fast", "mono", "none" }; +const char *RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::numMethods] = {"amaze", "igv", "lmmse", "eahd", "hphd", "vng4", "dcb", "ahd", "fast", "mono", "none", "pixelshift" }; const char *RAWParams::XTransSensor::methodstring[RAWParams::XTransSensor::numMethods] = {"3-pass (best)", "1-pass (medium)", "fast", "mono", "none" }; const char *RAWParams::ff_BlurTypestring[RAWParams::numFlatFileBlurTypes] = {/*"Parametric",*/ "Area Flatfield", "Vertical Flatfield", "Horizontal Flatfield", "V+H Flatfield"}; @@ -875,10 +875,41 @@ void CoarseTransformParams::setDefaults() hflip = false; vflip = false; } +void RAWParams::BayerSensor::setPixelShiftDefaults() +{ + pixelShiftMotion = 0; + pixelShiftMotionCorrection = RAWParams::BayerSensor::Grid3x3New; + pixelShiftMotionCorrectionMethod = RAWParams::BayerSensor::Automatic; + pixelShiftStddevFactorGreen = 5.0; + pixelShiftStddevFactorRed = 5.0; + pixelShiftStddevFactorBlue = 5.0; + pixelShiftEperIso = 0.0; + pixelShiftNreadIso = 0.0; + pixelShiftPrnu = 1.0; + pixelShiftSigma = 1.0; + pixelShiftSum = 3.0; + pixelShiftRedBlueWeight = 0.7; + pixelShiftAutomatic = true; + pixelShiftNonGreenHorizontal = false; + pixelShiftNonGreenVertical = false; + pixelShiftHoleFill = true; + pixelShiftMedian = false; + pixelShiftMedian3 = false; + pixelShiftGreen = true; + pixelShiftBlur = true; + pixelShiftSmoothFactor = 0.7; + pixelShiftExp0 = false; + pixelShiftLmmse = false; + pixelShiftEqualBright = false; + pixelShiftNonGreenCross = true; + pixelShiftNonGreenCross2 = false; + pixelShiftNonGreenAmaze = false; +} void RAWParams::setDefaults() { bayersensor.method = RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::amaze]; + bayersensor.imageNum = 0; bayersensor.ccSteps = 0; bayersensor.dcb_iterations = 2; bayersensor.dcb_enhance = true; @@ -913,6 +944,10 @@ void RAWParams::setDefaults() hotPixelFilter = false; deadPixelFilter = false; hotdeadpix_thresh = 100; + bayersensor.setPixelShiftDefaults(); + bayersensor.pixelshiftShowMotion = false; + bayersensor.pixelshiftShowMotionMaskOnly = false; + } void ColorManagementParams::setDefaults() @@ -3320,6 +3355,10 @@ int ProcParams::save (const Glib::ustring &fname, const Glib::ustring &fname2, b keyFile.set_string ("RAW Bayer", "Method", raw.bayersensor.method ); } + if (!pedited || pedited->raw.bayersensor.imageNum) { + keyFile.set_integer ("RAW Bayer", "ImageNum", raw.bayersensor.imageNum + 1 ); + } + if (!pedited || pedited->raw.bayersensor.ccSteps) { keyFile.set_integer ("RAW Bayer", "CcSteps", raw.bayersensor.ccSteps); } @@ -3364,7 +3403,121 @@ int ProcParams::save (const Glib::ustring &fname, const Glib::ustring &fname2, b keyFile.set_integer ("RAW Bayer", "LMMSEIterations", raw.bayersensor.lmmse_iterations ); } - //if (!pedited || pedited->raw.bayersensor.allEnhance) keyFile.set_boolean ("RAW Bayer", "ALLEnhance", raw.bayersensor.all_enhance ); + if (!pedited || pedited->raw.bayersensor.pixelShiftMotion) { + keyFile.set_integer ("RAW Bayer", "PixelShiftMotion", raw.bayersensor.pixelShiftMotion ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftMotionCorrection) { + keyFile.set_integer ("RAW Bayer", "PixelShiftMotionCorrection", raw.bayersensor.pixelShiftMotionCorrection ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftMotionCorrectionMethod) { + keyFile.set_integer ("RAW Bayer", "PixelShiftMotionCorrectionMethod", raw.bayersensor.pixelShiftMotionCorrectionMethod ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftStddevFactorGreen) { + keyFile.set_double ("RAW Bayer", "pixelShiftStddevFactorGreen", raw.bayersensor.pixelShiftStddevFactorGreen ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftStddevFactorRed) { + keyFile.set_double ("RAW Bayer", "pixelShiftStddevFactorRed", raw.bayersensor.pixelShiftStddevFactorRed ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftStddevFactorBlue) { + keyFile.set_double ("RAW Bayer", "pixelShiftStddevFactorBlue", raw.bayersensor.pixelShiftStddevFactorBlue ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftEperIso) { + keyFile.set_double ("RAW Bayer", "PixelShiftEperIso", raw.bayersensor.pixelShiftEperIso ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftNreadIso) { + keyFile.set_double ("RAW Bayer", "PixelShiftNreadIso", raw.bayersensor.pixelShiftNreadIso ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftPrnu) { + keyFile.set_double ("RAW Bayer", "PixelShiftPrnu", raw.bayersensor.pixelShiftPrnu ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftSigma) { + keyFile.set_double ("RAW Bayer", "PixelShiftSigma", raw.bayersensor.pixelShiftSigma ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftSum) { + keyFile.set_double ("RAW Bayer", "PixelShiftSum", raw.bayersensor.pixelShiftSum ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftRedBlueWeight) { + keyFile.set_double ("RAW Bayer", "PixelShiftRedBlueWeight", raw.bayersensor.pixelShiftRedBlueWeight ); + } + + if (!pedited || pedited->raw.bayersensor.pixelshiftShowMotion) { + keyFile.set_boolean ("RAW Bayer", "PixelShiftShowMotion", raw.bayersensor.pixelshiftShowMotion ); + } + + if (!pedited || pedited->raw.bayersensor.pixelshiftShowMotionMaskOnly) { + keyFile.set_boolean ("RAW Bayer", "PixelShiftShowMotionMaskOnly", raw.bayersensor.pixelshiftShowMotionMaskOnly ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftAutomatic) { + keyFile.set_boolean ("RAW Bayer", "pixelShiftAutomatic", raw.bayersensor.pixelShiftAutomatic ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftNonGreenHorizontal) { + keyFile.set_boolean ("RAW Bayer", "pixelShiftNonGreenHorizontal", raw.bayersensor.pixelShiftNonGreenHorizontal ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftNonGreenVertical) { + keyFile.set_boolean ("RAW Bayer", "pixelShiftNonGreenVertical", raw.bayersensor.pixelShiftNonGreenVertical ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftHoleFill) { + keyFile.set_boolean ("RAW Bayer", "pixelShiftHoleFill", raw.bayersensor.pixelShiftHoleFill ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftMedian) { + keyFile.set_boolean ("RAW Bayer", "pixelShiftMedian", raw.bayersensor.pixelShiftMedian ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftMedian3) { + keyFile.set_boolean ("RAW Bayer", "pixelShiftMedian3", raw.bayersensor.pixelShiftMedian3 ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftGreen) { + keyFile.set_boolean ("RAW Bayer", "pixelShiftGreen", raw.bayersensor.pixelShiftGreen ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftBlur) { + keyFile.set_boolean ("RAW Bayer", "pixelShiftBlur", raw.bayersensor.pixelShiftBlur ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftSmooth) { + keyFile.set_double ("RAW Bayer", "pixelShiftSmoothFactor", raw.bayersensor.pixelShiftSmoothFactor ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftExp0) { + keyFile.set_boolean ("RAW Bayer", "pixelShiftExp0", raw.bayersensor.pixelShiftExp0 ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftLmmse) { + keyFile.set_boolean ("RAW Bayer", "pixelShiftLmmse", raw.bayersensor.pixelShiftLmmse ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftEqualBright) { + keyFile.set_boolean ("RAW Bayer", "pixelShiftEqualBright", raw.bayersensor.pixelShiftEqualBright ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftNonGreenCross) { + keyFile.set_boolean ("RAW Bayer", "pixelShiftNonGreenCross", raw.bayersensor.pixelShiftNonGreenCross ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftNonGreenCross2) { + keyFile.set_boolean ("RAW Bayer", "pixelShiftNonGreenCross2", raw.bayersensor.pixelShiftNonGreenCross2 ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftNonGreenAmaze) { + keyFile.set_boolean ("RAW Bayer", "pixelShiftNonGreenAmaze", raw.bayersensor.pixelShiftNonGreenAmaze ); + } if (!pedited || pedited->raw.xtranssensor.method) { keyFile.set_string ("RAW X-Trans", "Method", raw.xtranssensor.method ); @@ -7334,6 +7487,14 @@ int ProcParams::load (const Glib::ustring &fname, ParamsEdited* pedited) } } + if (keyFile.has_key ("RAW Bayer", "ImageNum")) { + raw.bayersensor.imageNum = keyFile.get_integer ("RAW Bayer", "ImageNum") - 1; + + if (pedited) { + pedited->raw.bayersensor.imageNum = true; + } + } + if (keyFile.has_key ("RAW Bayer", "CcSteps")) { raw.bayersensor.ccSteps = keyFile.get_integer ("RAW Bayer", "CcSteps"); @@ -7422,7 +7583,237 @@ int ProcParams::load (const Glib::ustring &fname, ParamsEdited* pedited) } } - //if (keyFile.has_key ("RAW Bayer", "ALLEnhance")) { raw.bayersensor.all_enhance = keyFile.get_boolean("RAW Bayer", "ALLEnhance"); if (pedited) pedited->raw.bayersensor.allEnhance = true; } + if (keyFile.has_key ("RAW Bayer", "PixelShiftMotion")) { + raw.bayersensor.pixelShiftMotion = keyFile.get_integer("RAW Bayer", "PixelShiftMotion"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftMotion = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "PixelShiftMotionCorrection")) { + raw.bayersensor.pixelShiftMotionCorrection = (RAWParams::BayerSensor::ePSMotionCorrection)keyFile.get_integer("RAW Bayer", "PixelShiftMotionCorrection"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftMotionCorrection = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "PixelShiftMotionCorrectionMethod")) { + raw.bayersensor.pixelShiftMotionCorrectionMethod = (RAWParams::BayerSensor::ePSMotionCorrectionMethod)keyFile.get_integer("RAW Bayer", "PixelShiftMotionCorrectionMethod"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftMotionCorrectionMethod = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "pixelShiftStddevFactorGreen")) { + raw.bayersensor.pixelShiftStddevFactorGreen = keyFile.get_double("RAW Bayer", "pixelShiftStddevFactorGreen"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftStddevFactorGreen = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "pixelShiftStddevFactorRed")) { + raw.bayersensor.pixelShiftStddevFactorRed = keyFile.get_double("RAW Bayer", "pixelShiftStddevFactorRed"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftStddevFactorRed = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "pixelShiftStddevFactorBlue")) { + raw.bayersensor.pixelShiftStddevFactorBlue = keyFile.get_double("RAW Bayer", "pixelShiftStddevFactorBlue"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftStddevFactorBlue = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "PixelShiftEperIso")) { + raw.bayersensor.pixelShiftEperIso = keyFile.get_double("RAW Bayer", "PixelShiftEperIso"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftEperIso = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "PixelShiftNreadIso")) { + raw.bayersensor.pixelShiftNreadIso = keyFile.get_double("RAW Bayer", "PixelShiftNreadIso"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftNreadIso = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "PixelShiftPrnu")) { + raw.bayersensor.pixelShiftPrnu = keyFile.get_double("RAW Bayer", "PixelShiftPrnu"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftPrnu = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "PixelShiftSigma")) { + raw.bayersensor.pixelShiftSigma = keyFile.get_double("RAW Bayer", "PixelShiftSigma"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftSigma = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "PixelShiftSum")) { + raw.bayersensor.pixelShiftSum = keyFile.get_double("RAW Bayer", "PixelShiftSum"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftSum = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "PixelShiftRedBlueWeight")) { + raw.bayersensor.pixelShiftRedBlueWeight = keyFile.get_double("RAW Bayer", "PixelShiftRedBlueWeight"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftRedBlueWeight = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "PixelShiftShowMotion")) { + raw.bayersensor.pixelshiftShowMotion = keyFile.get_boolean("RAW Bayer", "PixelShiftShowMotion"); + + if (pedited) { + pedited->raw.bayersensor.pixelshiftShowMotion = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "PixelShiftShowMotionMaskOnly")) { + raw.bayersensor.pixelshiftShowMotionMaskOnly = keyFile.get_boolean("RAW Bayer", "PixelShiftShowMotionMaskOnly"); + + if (pedited) { + pedited->raw.bayersensor.pixelshiftShowMotionMaskOnly = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "pixelShiftAutomatic")) { + raw.bayersensor.pixelShiftAutomatic = keyFile.get_boolean("RAW Bayer", "pixelShiftAutomatic"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftAutomatic = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "pixelShiftNonGreenHorizontal")) { + raw.bayersensor.pixelShiftNonGreenHorizontal = keyFile.get_boolean("RAW Bayer", "pixelShiftNonGreenHorizontal"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftNonGreenHorizontal = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "pixelShiftNonGreenVertical")) { + raw.bayersensor.pixelShiftNonGreenVertical = keyFile.get_boolean("RAW Bayer", "pixelShiftNonGreenVertical"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftNonGreenVertical = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "pixelShiftHoleFill")) { + raw.bayersensor.pixelShiftHoleFill = keyFile.get_boolean("RAW Bayer", "pixelShiftHoleFill"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftHoleFill = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "pixelShiftMedian")) { + raw.bayersensor.pixelShiftMedian = keyFile.get_boolean("RAW Bayer", "pixelShiftMedian"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftMedian = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "pixelShiftMedian3")) { + raw.bayersensor.pixelShiftMedian3 = keyFile.get_boolean("RAW Bayer", "pixelShiftMedian3"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftMedian3 = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "pixelShiftGreen")) { + raw.bayersensor.pixelShiftGreen = keyFile.get_boolean("RAW Bayer", "pixelShiftGreen"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftGreen = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "pixelShiftBlur")) { + raw.bayersensor.pixelShiftBlur = keyFile.get_boolean("RAW Bayer", "pixelShiftBlur"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftBlur = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "pixelShiftSmoothFactor")) { + raw.bayersensor.pixelShiftSmoothFactor = keyFile.get_double("RAW Bayer", "pixelShiftSmoothFactor"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftSmooth = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "pixelShiftExp0")) { + raw.bayersensor.pixelShiftExp0 = keyFile.get_boolean("RAW Bayer", "pixelShiftExp0"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftExp0 = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "pixelShiftLmmse")) { + raw.bayersensor.pixelShiftLmmse = keyFile.get_boolean("RAW Bayer", "pixelShiftLmmse"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftLmmse = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "pixelShiftEqualBright")) { + raw.bayersensor.pixelShiftEqualBright = keyFile.get_boolean("RAW Bayer", "pixelShiftEqualBright"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftEqualBright = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "pixelShiftNonGreenCross")) { + raw.bayersensor.pixelShiftNonGreenCross = keyFile.get_boolean("RAW Bayer", "pixelShiftNonGreenCross"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftNonGreenCross = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "pixelShiftNonGreenCross2")) { + raw.bayersensor.pixelShiftNonGreenCross2 = keyFile.get_boolean("RAW Bayer", "pixelShiftNonGreenCross2"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftNonGreenCross2 = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "pixelShiftNonGreenAmaze")) { + raw.bayersensor.pixelShiftNonGreenAmaze = keyFile.get_boolean("RAW Bayer", "pixelShiftNonGreenAmaze"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftNonGreenAmaze = true; + } + } } // load X-Trans sensors' raw settings @@ -7850,6 +8241,7 @@ bool ProcParams::operator== (const ProcParams& other) && resize.width == other.resize.width && resize.height == other.resize.height && raw.bayersensor.method == other.raw.bayersensor.method + && raw.bayersensor.imageNum == other.raw.bayersensor.imageNum && raw.bayersensor.ccSteps == other.raw.bayersensor.ccSteps && raw.bayersensor.black0 == other.raw.bayersensor.black0 && raw.bayersensor.black1 == other.raw.bayersensor.black1 @@ -7858,8 +8250,38 @@ bool ProcParams::operator== (const ProcParams& other) && raw.bayersensor.twogreen == other.raw.bayersensor.twogreen && raw.bayersensor.greenthresh == other.raw.bayersensor.greenthresh && raw.bayersensor.linenoise == other.raw.bayersensor.linenoise - && raw.bayersensor.dcb_enhance == other.raw.bayersensor.dcb_enhance && raw.bayersensor.dcb_iterations == other.raw.bayersensor.dcb_iterations + && raw.bayersensor.lmmse_iterations == other.raw.bayersensor.lmmse_iterations + && raw.bayersensor.pixelShiftMotion == other.raw.bayersensor.pixelShiftMotion + && raw.bayersensor.pixelShiftMotionCorrection == other.raw.bayersensor.pixelShiftMotionCorrection + && raw.bayersensor.pixelShiftMotionCorrectionMethod == other.raw.bayersensor.pixelShiftMotionCorrectionMethod + && raw.bayersensor.pixelShiftStddevFactorGreen == other.raw.bayersensor.pixelShiftStddevFactorGreen + && raw.bayersensor.pixelShiftStddevFactorRed == other.raw.bayersensor.pixelShiftStddevFactorRed + && raw.bayersensor.pixelShiftStddevFactorBlue == other.raw.bayersensor.pixelShiftStddevFactorBlue + && raw.bayersensor.pixelShiftEperIso == other.raw.bayersensor.pixelShiftEperIso + && raw.bayersensor.pixelShiftNreadIso == other.raw.bayersensor.pixelShiftNreadIso + && raw.bayersensor.pixelShiftPrnu == other.raw.bayersensor.pixelShiftPrnu + && raw.bayersensor.pixelShiftSigma == other.raw.bayersensor.pixelShiftSigma + && raw.bayersensor.pixelShiftSum == other.raw.bayersensor.pixelShiftSum + && raw.bayersensor.pixelShiftRedBlueWeight == other.raw.bayersensor.pixelShiftRedBlueWeight + && raw.bayersensor.pixelshiftShowMotion == other.raw.bayersensor.pixelshiftShowMotion + && raw.bayersensor.pixelshiftShowMotionMaskOnly == other.raw.bayersensor.pixelshiftShowMotionMaskOnly + && raw.bayersensor.pixelShiftAutomatic == other.raw.bayersensor.pixelShiftAutomatic + && raw.bayersensor.pixelShiftNonGreenHorizontal == other.raw.bayersensor.pixelShiftNonGreenHorizontal + && raw.bayersensor.pixelShiftNonGreenVertical == other.raw.bayersensor.pixelShiftNonGreenVertical + && raw.bayersensor.pixelShiftHoleFill == other.raw.bayersensor.pixelShiftHoleFill + && raw.bayersensor.pixelShiftMedian == other.raw.bayersensor.pixelShiftMedian + && raw.bayersensor.pixelShiftMedian3 == other.raw.bayersensor.pixelShiftMedian3 + && raw.bayersensor.pixelShiftGreen == other.raw.bayersensor.pixelShiftGreen + && raw.bayersensor.pixelShiftBlur == other.raw.bayersensor.pixelShiftBlur + && raw.bayersensor.pixelShiftSmoothFactor == other.raw.bayersensor.pixelShiftSmoothFactor + && raw.bayersensor.pixelShiftExp0 == other.raw.bayersensor.pixelShiftExp0 + && raw.bayersensor.pixelShiftLmmse == other.raw.bayersensor.pixelShiftLmmse + && raw.bayersensor.pixelShiftEqualBright == other.raw.bayersensor.pixelShiftEqualBright + && raw.bayersensor.pixelShiftNonGreenCross == other.raw.bayersensor.pixelShiftNonGreenCross + && raw.bayersensor.pixelShiftNonGreenCross2 == other.raw.bayersensor.pixelShiftNonGreenCross2 + && raw.bayersensor.pixelShiftNonGreenAmaze == other.raw.bayersensor.pixelShiftNonGreenAmaze + && raw.bayersensor.dcb_enhance == other.raw.bayersensor.dcb_enhance && raw.xtranssensor.method == other.raw.xtranssensor.method && raw.xtranssensor.ccSteps == other.raw.xtranssensor.ccSteps && raw.xtranssensor.blackred == other.raw.xtranssensor.blackred diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 7cdc0894f..c83856870 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1186,12 +1186,19 @@ public: public: //enum eMethod{ eahd,hphd,vng4,dcb,amaze,ahd,IGV_noise,fast, //numMethods }; // This MUST be the last enum - enum eMethod { amaze, igv, lmmse, eahd, hphd, vng4, dcb, ahd, fast, mono, none, + enum eMethod { amaze, igv, lmmse, eahd, hphd, vng4, dcb, ahd, fast, mono, none, pixelshift, numMethods }; // This MUST be the last enum + enum ePSMotionCorrection { + Grid1x1, Grid1x2, Grid3x3, Grid5x5, Grid7x7, Grid3x3New + }; + enum ePSMotionCorrectionMethod { + Off, Automatic, Custom + }; static const char *methodstring[numMethods]; Glib::ustring method; + int imageNum; int ccSteps; double black0; double black1; @@ -1202,8 +1209,40 @@ public: int greenthresh; int dcb_iterations; int lmmse_iterations; + int pixelShiftMotion; + ePSMotionCorrection pixelShiftMotionCorrection; + ePSMotionCorrectionMethod pixelShiftMotionCorrectionMethod; + double pixelShiftStddevFactorGreen; + double pixelShiftStddevFactorRed; + double pixelShiftStddevFactorBlue; + double pixelShiftEperIso; + double pixelShiftNreadIso; + double pixelShiftPrnu; + double pixelShiftSigma; + double pixelShiftSum; + double pixelShiftRedBlueWeight; + bool pixelshiftShowMotion; + bool pixelshiftShowMotionMaskOnly; + bool pixelShiftAutomatic; + bool pixelShiftNonGreenHorizontal; + bool pixelShiftNonGreenVertical; + bool pixelShiftHoleFill; + bool pixelShiftMedian; + bool pixelShiftMedian3; + bool pixelShiftGreen; + bool pixelShiftBlur; + double pixelShiftSmoothFactor; + bool pixelShiftExp0; + bool pixelShiftLmmse; + bool pixelShiftEqualBright; + bool pixelShiftNonGreenCross; + bool pixelShiftNonGreenCross2; + bool pixelShiftNonGreenAmaze; bool dcb_enhance; //bool all_enhance; + + void setPixelShiftDefaults(); + }; /** diff --git a/rtengine/rawimage.cc b/rtengine/rawimage.cc index 88fed5473..2cd1abf5f 100644 --- a/rtengine/rawimage.cc +++ b/rtengine/rawimage.cc @@ -400,7 +400,7 @@ skip_block: } } -int RawImage::loadRaw (bool loadData, bool closeFile, ProgressListener *plistener, double progressRange) +int RawImage::loadRaw (bool loadData, unsigned int imageNum, bool closeFile, ProgressListener *plistener, double progressRange) { ifname = filename.c_str(); image = nullptr; @@ -424,7 +424,12 @@ int RawImage::loadRaw (bool loadData, bool closeFile, ProgressListener *plistene raw_image = nullptr; //***************** Read ALL raw file info - identify (); + // set the number of the frame to extract. If the number is larger then number of existing frames - 1, dcraw will handle that correctly + + shot_select = imageNum; + identify(); + // in case dcraw didn't handle the above mentioned case... + shot_select = std::min(shot_select, std::max(is_raw, 1u) - 1); if (!is_raw) { fclose(ifp); @@ -660,7 +665,7 @@ int RawImage::loadRaw (bool loadData, bool closeFile, ProgressListener *plistene return 0; } -float** RawImage::compress_image() +float** RawImage::compress_image(int frameNum) { if( !image ) { return nullptr; @@ -668,11 +673,12 @@ float** RawImage::compress_image() if (isBayer() || isXtrans()) { if (!allocation) { - allocation = new float[height * width]; + // shift the beginning of all frames but the first by 32 floats to avoid cache miss conflicts on CPUs which have <= 4-way associative L1-Cache + allocation = new float[height * width + frameNum * 32]; data = new float*[height]; for (int i = 0; i < height; i++) { - data[i] = allocation + i * width; + data[i] = allocation + i * width + frameNum * 32; } } } else if (colors == 1) { diff --git a/rtengine/rawimage.h b/rtengine/rawimage.h index 804a2a346..3f4307054 100644 --- a/rtengine/rawimage.h +++ b/rtengine/rawimage.h @@ -106,7 +106,7 @@ public: explicit RawImage( const Glib::ustring &name ); ~RawImage(); - int loadRaw (bool loadData = true, bool closeFile = true, ProgressListener *plistener = nullptr, double progressRange = 1.0); + int loadRaw (bool loadData, unsigned int imageNum = 0, bool closeFile = true, ProgressListener *plistener = nullptr, double progressRange = 1.0); void get_colorsCoeff( float* pre_mul_, float* scale_mul_, float* cblack_, bool forceAutoWB ); void set_prefilters() { @@ -119,9 +119,10 @@ public: { return image; } - float** compress_image(); // revert to compressed pixels format and release image data + float** compress_image(int frameNum); // revert to compressed pixels format and release image data float** data; // holds pixel values, data[i][j] corresponds to the ith row and jth column unsigned prefilters; // original filters saved ( used for 4 color processing ) + unsigned int getFrameCount() const { return is_raw; } protected: Glib::ustring filename; // complete filename int rotate_deg; // 0,90,180,270 degree of rotation: info taken by dcraw from exif diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 9678fcaf5..4370befbb 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -37,7 +37,7 @@ #include #endif #include "opthelper.h" -//#define BENCHMARK +#define BENCHMARK #include "StopWatch.h" #define clipretinex( val, minv, maxv ) (( val = (val < minv ? minv : val ) ) > maxv ? maxv : val ) #undef CLIPD @@ -475,8 +475,12 @@ RawImageSource::~RawImageSource () delete idata; - if (ri) { - delete ri; + for(size_t i = 0; i < numFrames; ++i) { + delete riFrames[i]; + } + + for(size_t i = 0; i < numFrames - 1; ++i) { + delete rawDataBuffer[i]; } flushRGB(); @@ -895,6 +899,9 @@ DCPProfile *RawImageSource::getDCP(const ColorManagementParams &cmp, ColorTemp & findInputProfile(cmp.input, nullptr, (static_cast(getMetaData()))->getCamera(), &dcpProf, dummy); if (dcpProf == nullptr) { + if (settings->verbose) { + printf("Can't load DCP profile '%s'!\n", cmp.input.c_str()); + } return nullptr; } @@ -913,7 +920,7 @@ void RawImageSource::convertColorSpace(Imagefloat* image, const ColorManagementP /* interpolateBadPixelsBayer: correct raw pixels looking at the bitmap * takes into consideration if there are multiple bad pixels in the neighbourhood */ -int RawImageSource::interpolateBadPixelsBayer( PixelsMap &bitmapBads ) +int RawImageSource::interpolateBadPixelsBayer( PixelsMap &bitmapBads, array2D &rawData ) { static const float eps = 1.f; int counter = 0; @@ -1494,7 +1501,7 @@ void RawImageSource::vflip (Imagefloat* image) //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -int RawImageSource::load (const Glib::ustring &fname, bool batch) +int RawImageSource::load (const Glib::ustring &fname, int imageNum, bool batch) { MyTime t1, t2; @@ -1505,15 +1512,59 @@ int RawImageSource::load (const Glib::ustring &fname, bool batch) plistener->setProgressStr ("Decoding..."); plistener->setProgress (0.0); } - ri = new RawImage(fname); - int errCode = ri->loadRaw (true, true, plistener, 0.8); + int errCode = ri->loadRaw (false, 0, false); if (errCode) { return errCode; } + numFrames = ri->getFrameCount(); - ri->compress_image(); + errCode = 0; + + if(numFrames > 1) { +#ifdef _OPENMP + #pragma omp parallel +#endif + { + int errCodeThr = 0; +#ifdef _OPENMP + #pragma omp for nowait +#endif + for(unsigned int i = 0; i < numFrames; ++i) { + if(i == 0) { + riFrames[i] = ri; + errCodeThr = riFrames[i]->loadRaw (true, i, true, plistener, 0.8); + } else { + riFrames[i] = new RawImage(fname); + errCodeThr = riFrames[i]->loadRaw (true, i); + } + } +#ifdef _OPENMP + #pragma omp critical +#endif + { + errCode = errCodeThr ? errCodeThr : errCode; + } + } + } else { + riFrames[0] = ri; + errCode = riFrames[0]->loadRaw (true, 0, true, plistener, 0.8); + } + + if(!errCode) { + for(unsigned int i = 0; i < numFrames; ++i) { + riFrames[i]->compress_image(i); + } + } else { + return errCode; + } + + if(numFrames > 1 ) { // this disables multi frame support for Fuji S5 until I found a solution to handle different dimensions + if(riFrames[0]->get_width() != riFrames[1]->get_width() || riFrames[0]->get_height() != riFrames[1]->get_height()) { + numFrames = 1; + } + } if (plistener) { plistener->setProgress (0.9); @@ -1554,7 +1605,7 @@ int RawImageSource::load (const Glib::ustring &fname, bool batch) imatrices.xyz_cam[i][j] += xyz_sRGB[i][k] * imatrices.rgb_cam[k][j]; } - camProfile = iccStore->createFromMatrix (imatrices.xyz_cam, false, "Camera"); + camProfile = ICCStore::getInstance()->createFromMatrix (imatrices.xyz_cam, false, "Camera"); inverse33 (imatrices.xyz_cam, imatrices.cam_xyz); for (int c = 0; c < 4; c++) { @@ -1626,8 +1677,10 @@ int RawImageSource::load (const Glib::ustring &fname, bool batch) initialGain = 1.0 / min(pre_mul[0], pre_mul[1], pre_mul[2]); }*/ + for(unsigned int i = 0;i < numFrames; ++i) { + riFrames[i]->set_prefilters(); + } - ri->set_prefilters(); //Load complete Exif informations RawMetaDataLocation rml; @@ -1719,7 +1772,24 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le printf( "Flat Field Correction:%s\n", rif->get_filename().c_str()); } - copyOriginalPixels(raw, ri, rid, rif); + if(numFrames == 4) { + int bufferNumber = 0; + for(int i=0; i<4; ++i) { + if(i==currFrame) { + copyOriginalPixels(raw, ri, rid, rif, rawData); + rawDataFrames[i] = &rawData; + } else { + if(!rawDataBuffer[bufferNumber]) { + rawDataBuffer[bufferNumber] = new array2D; + } + rawDataFrames[i] = rawDataBuffer[bufferNumber]; + ++bufferNumber; + copyOriginalPixels(raw, riFrames[i], rid, rif, *rawDataFrames[i]); + } + } + } else { + copyOriginalPixels(raw, ri, rid, rif, rawData); + } //FLATFIELD end @@ -1759,8 +1829,13 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le } } - - scaleColors( 0, 0, W, H, raw); //+ + raw parameters for black level(raw.blackxx) + if(numFrames == 4) { + for(int i=0; i<4; ++i) { + scaleColors( 0, 0, W, H, raw, *rawDataFrames[i]); + } + } else { + scaleColors( 0, 0, W, H, raw, rawData); //+ + raw parameters for black level(raw.blackxx) + } // Correct vignetting of lens profile if (!hasFlatField && lensProf.useVign) { @@ -1770,13 +1845,25 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le LCPMapper map(pLCPProf, max(idata->getFocalLen(), 1.0), idata->getFocalLen35mm(), idata->getFocusDist(), idata->getFNumber(), true, false, W, H, coarse, -1); if (ri->getSensorType() == ST_BAYER || ri->getSensorType() == ST_FUJI_XTRANS || ri->get_colors() == 1) { - + if(numFrames == 4) { + for(int i = 0; i < 4; ++i) { #ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) + #pragma omp parallel for schedule(dynamic,16) #endif - for (int y = 0; y < H; y++) { - map.processVignetteLine(W, y, rawData[y]); + for (int y = 0; y < H; y++) { + map.processVignetteLine(W, y, (*rawDataFrames[i])[y]); + } + } + } else { + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) +#endif + + for (int y = 0; y < H; y++) { + map.processVignetteLine(W, y, rawData[y]); + } } } else if(ri->get_colors() == 3) { #ifdef _OPENMP @@ -1854,13 +1941,25 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le plistener->setProgress (0.0); } - green_equilibrate(0.01 * (raw.bayersensor.greenthresh)); + if(numFrames == 4) { + for(int i = 0; i < 4; ++i) { + green_equilibrate(0.01 * (raw.bayersensor.greenthresh), *rawDataFrames[i]); + } + } else { + green_equilibrate(0.01 * (raw.bayersensor.greenthresh), rawData); + } } if( totBP ) { if ( ri->getSensorType() == ST_BAYER ) { - interpolateBadPixelsBayer( *bitmapBads ); + if(numFrames == 4) { + for(int i = 0; i < 4; ++i) { + interpolateBadPixelsBayer( *bitmapBads, *rawDataFrames[i] ); + } + } else { + interpolateBadPixelsBayer( *bitmapBads, rawData ); + } } else if ( ri->getSensorType() == ST_FUJI_XTRANS ) { interpolateBadPixelsXtrans( *bitmapBads ); } else { @@ -1882,12 +1981,23 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le plistener->setProgressStr ("CA Auto Correction..."); plistener->setProgress (0.0); } - - CA_correct_RT(raw.cared, raw.cablue, 10.0 - raw.caautostrength); + if(numFrames == 4) { + for(int i=0; i<4; ++i) { + CA_correct_RT(raw.ca_autocorrect, raw.cared, raw.cablue, 10.0 - raw.caautostrength, *rawDataFrames[i]); + } + } else { + CA_correct_RT(raw.ca_autocorrect, raw.cared, raw.cablue, 10.0 - raw.caautostrength, rawData); + } } if ( raw.expos != 1 ) { - processRawWhitepoint(raw.expos, raw.preser); + if(numFrames == 4) { + for(int i = 0; i < 4; ++i) { + processRawWhitepoint(raw.expos, raw.preser, *rawDataFrames[i]); + } + } else { + processRawWhitepoint(raw.expos, raw.preser, rawData); + } } if(prepareDenoise && dirpyrdenoiseExpComp == INFINITY) { @@ -1926,7 +2036,9 @@ void RawImageSource::demosaic(const RAWParams &raw) } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::ahd] ) { ahd_demosaic (0, 0, W, H); } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::amaze] ) { - amaze_demosaic_RT (0, 0, W, H); + amaze_demosaic_RT (0, 0, W, H, rawData, red, green, blue); + } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift] ) { + pixelshift(0, 0, W, H, raw.bayersensor, currFrame, ri->get_model(), raw.expos); } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::dcb] ) { dcb_demosaic(raw.bayersensor.dcb_iterations, raw.bayersensor.dcb_enhance); } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::eahd]) { @@ -1934,7 +2046,7 @@ void RawImageSource::demosaic(const RAWParams &raw) } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::igv]) { igv_interpolate(W, H); } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::lmmse]) { - lmmse_interpolate_omp(W, H, raw.bayersensor.lmmse_iterations); + lmmse_interpolate_omp(W, H, rawData, red, green, blue, raw.bayersensor.lmmse_iterations); } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::fast] ) { fast_demosaic (0, 0, W, H); } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::mono] ) { @@ -2172,7 +2284,7 @@ void RawImageSource::retinexPrepareBuffers(ColorManagementParams cmp, RetinexPar } } else { - TMatrix wprof = iccStore->workingSpaceMatrix (cmp.working); + TMatrix wprof = ICCStore::getInstance()->workingSpaceMatrix (cmp.working); 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])}, @@ -2449,7 +2561,7 @@ void RawImageSource::retinex(ColorManagementParams cmp, RetinexParams deh, ToneC } } else { - TMatrix wiprof = iccStore->workingSpaceInverseMatrix (cmp.working); + TMatrix wiprof = ICCStore::getInstance()->workingSpaceInverseMatrix (cmp.working); double wip[3][3] = { {wiprof[0][0], wiprof[0][1], wiprof[0][2]}, @@ -2973,7 +3085,7 @@ void RawImageSource::processFlatField(const RAWParams &raw, RawImage *riFlatFile /* Copy original pixel data and * subtract dark frame (if present) from current image and apply flat field correction (if present) */ -void RawImageSource::copyOriginalPixels(const RAWParams &raw, RawImage *src, RawImage *riDark, RawImage *riFlatFile ) +void RawImageSource::copyOriginalPixels(const RAWParams &raw, RawImage *src, RawImage *riDark, RawImage *riFlatFile, array2D &rawData ) { // TODO: Change type of black[] to float to avoid conversions unsigned short black[4] = { @@ -3304,7 +3416,7 @@ SSEFUNCTION void RawImageSource::cfaboxblur(RawImage *riFlatFile, float* cfablur // Scale original pixels into the range 0 65535 using black offsets and multipliers -void RawImageSource::scaleColors(int winx, int winy, int winw, int winh, const RAWParams &raw) +void RawImageSource::scaleColors(int winx, int winy, int winw, int winh, const RAWParams &raw, array2D &rawData) { chmax[0] = chmax[1] = chmax[2] = chmax[3] = 0; //channel maxima float black_lev[4] = {0.f};//black level @@ -3806,7 +3918,7 @@ void RawImageSource::colorSpaceConversion_ (Imagefloat* im, ColorManagementParam // in this case we avoid using the slllllooooooowwww lcms // Calculate matrix for direct conversion raw>working space - TMatrix work = iccStore->workingSpaceInverseMatrix (cmp.working); + TMatrix work = ICCStore::getInstance()->workingSpaceInverseMatrix (cmp.working); double mat[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; for (int i = 0; i < 3; i++) @@ -3898,7 +4010,7 @@ void RawImageSource::colorSpaceConversion_ (Imagefloat* im, ColorManagementParam // Initialize transform cmsHTRANSFORM hTransform; - cmsHPROFILE prophoto = iccStore->workingSpace("ProPhoto"); // We always use Prophoto to apply the ICC profile to minimize problems with clipping in LUT conversion. + 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; lcmsMutex->lock (); @@ -3944,8 +4056,8 @@ void RawImageSource::colorSpaceConversion_ (Imagefloat* im, ColorManagementParam TMatrix toxyz = {}, torgb = {}; if (!working_space_is_prophoto) { - toxyz = iccStore->workingSpaceMatrix ("ProPhoto"); - torgb = iccStore->workingSpaceInverseMatrix (cmp.working); //sRGB .. Adobe...Wide... + toxyz = ICCStore::getInstance()->workingSpaceMatrix ("ProPhoto"); + torgb = ICCStore::getInstance()->workingSpaceInverseMatrix (cmp.working); //sRGB .. Adobe...Wide... } #ifdef _OPENMP @@ -4158,7 +4270,7 @@ bool RawImageSource::findInputProfile(Glib::ustring inProfile, cmsHPROFILE embed *dcpProf = DCPStore::getInstance()->getStdProfile(camName); if (*dcpProf == nullptr) { - in = iccStore->getStdProfile(camName); + in = ICCStore::getInstance()->getStdProfile(camName); } } else if (inProfile != "(camera)" && inProfile != "") { Glib::ustring normalName = inProfile; @@ -4172,7 +4284,7 @@ bool RawImageSource::findInputProfile(Glib::ustring inProfile, cmsHPROFILE embed } if (*dcpProf == nullptr) { - in = iccStore->getProfile (inProfile); + in = ICCStore::getInstance()->getProfile (inProfile); } } @@ -5235,6 +5347,36 @@ void RawImageSource::init () } } +void RawImageSource::getRawValues(int x, int y, int rotate, int &R, int &G, int &B) +{ + int xnew = x + border; + int ynew = y + border; + rotate += ri->get_rotateDegree(); + rotate %= 360; + if (rotate == 90) { + std::swap(xnew,ynew); + ynew = H - 1 - ynew; + } else if (rotate == 180) { + xnew = W - 1 - xnew; + ynew = H - 1 - ynew; + } else if (rotate == 270) { + std::swap(xnew,ynew); + ynew = H - 1 - ynew; + xnew = W - 1 - xnew; + ynew = H - 1 - ynew; + } + + int c = ri->getSensorType() == ST_FUJI_XTRANS ? ri->XTRANSFC(ynew,xnew) : ri->FC(ynew,xnew); + int val = round(rawData[ynew][xnew] / scale_mul[c]); + if(c == 0) { + R = val; G = 0; B = 0; + } else if(c == 2) { + R = 0; G = 0; B = val; + } else { + R = 0; G = val; B = 0; + } +} + void RawImageSource::cleanup () { delete phaseOneIccCurve; diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index 9fafef8bb..0efa2b828 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -25,7 +25,7 @@ #include "curves.h" #include "color.h" #include "iimage.h" - +#include #define HR_SCALE 2 namespace rtengine @@ -71,6 +71,9 @@ protected: bool rgbSourceModified; RawImage* ri; // Copy of raw pixels, NOT corrected for initial gain, blackpoint etc. + RawImage* riFrames[4] = {nullptr}; + unsigned int currFrame = 0; + unsigned int numFrames = 0; // to accelerate CIELAB conversion: double lc00, lc01, lc02, lc10, lc11, lc12, lc20, lc21, lc22; @@ -78,6 +81,8 @@ protected: int threshold; array2D rawData; // holds preprocessed pixel values, rowData[i][j] corresponds to the ith row and jth column + array2D *rawDataFrames[4] = {nullptr}; + array2D *rawDataBuffer[3] = {nullptr}; // the interpolated green plane: array2D green; @@ -107,7 +112,7 @@ public: RawImageSource (); ~RawImageSource (); - int load (const Glib::ustring &fname, bool batch = false); + int load (const Glib::ustring &fname, int imageNum = 0, bool batch = false); void preprocess (const RAWParams &raw, const LensProfParams &lensProf, const CoarseTransformParams& coarse, bool prepareDenoise = true); void demosaic (const RAWParams &raw); void retinex (ColorManagementParams cmp, RetinexParams deh, ToneCurveParams Tc, LUTf & cdcurve, LUTf & mapcurve, const RetinextransmissionCurve & dehatransmissionCurve, const RetinexgaintransmissionCurve & dehagaintransmissionCurve, multi_array2D &conversionBuffer, bool dehacontlutili, bool mapcontlutili, bool useHsl, float &minCD, float &maxCD, float &mini, float &maxi, float &Tmean, float &Tsigma, float &Tmin, float &Tmax, LUTu &histLRETI); @@ -125,9 +130,9 @@ public: } void processFlatField(const RAWParams &raw, RawImage *riFlatFile, unsigned short black[4]); - void copyOriginalPixels(const RAWParams &raw, RawImage *ri, RawImage *riDark, RawImage *riFlatFile ); + void copyOriginalPixels(const RAWParams &raw, RawImage *ri, RawImage *riDark, RawImage *riFlatFile, array2D &rawData ); void cfaboxblur (RawImage *riFlatFile, float* cfablur, int boxH, int boxW); - void scaleColors (int winx, int winy, int winw, int winh, const RAWParams &raw); // raw for cblack + void scaleColors (int winx, int winy, int winw, int winh, const RAWParams &raw, array2D &rawData); // raw for cblack void getImage (const ColorTemp &ctemp, int tran, Imagefloat* image, const PreviewProps &pp, const ToneCurveParams &hrp, const ColorManagementParams &cmp, const RAWParams &raw); eSensorType getSensorType () const @@ -195,7 +200,10 @@ public: static void HLRecovery_blend (float* rin, float* gin, float* bin, int width, float maxval, float* hlmax); static void init (); static void cleanup (); - + void setCurrentFrame(unsigned int frameNum) { + currFrame = std::min(numFrames - 1, frameNum); + ri = riFrames[currFrame]; + } protected: typedef unsigned short ushort; void processFalseColorCorrection (Imagefloat* i, const int steps); @@ -208,18 +216,18 @@ protected: inline void interpolate_row_rb (float* ar, float* ab, float* pg, float* cg, float* ng, int i); inline void interpolate_row_rb_mul_pp (float* ar, float* ab, float* pg, float* cg, float* ng, int i, float r_mul, float g_mul, float b_mul, int x1, int width, int skip); - void CA_correct_RT (const double cared, const double cablue, const double caautostrength); + void CA_correct_RT (const bool autoCA, const double cared, const double cablue, const double caautostrength, array2D &rawData); void ddct8x8s(int isgn, float a[8][8]); - void processRawWhitepoint (float expos, float preser); // exposure before interpolation + void processRawWhitepoint (float expos, float preser, array2D &rawData); // exposure before interpolation - int interpolateBadPixelsBayer( PixelsMap &bitmapBads ); + int interpolateBadPixelsBayer( PixelsMap &bitmapBads, array2D &rawData ); int interpolateBadPixelsNColours( PixelsMap &bitmapBads, const int colours ); int interpolateBadPixelsXtrans( PixelsMap &bitmapBads ); int findHotDeadPixels( PixelsMap &bpMap, float thresh, bool findHotPixels, bool findDeadPixels ); void cfa_linedn (float linenoiselevel);//Emil's line denoise - void green_equilibrate (float greenthresh);//Emil's green equilibration + void green_equilibrate (float greenthresh, array2D &rawData);//Emil's green equilibration void nodemosaic(bool bw); void eahd_demosaic(); @@ -228,8 +236,8 @@ protected: void ppg_demosaic(); void jdl_interpolate_omp(); void igv_interpolate(int winw, int winh); - void lmmse_interpolate_omp(int winw, int winh, int iterations); - void amaze_demosaic_RT(int winx, int winy, int winw, int winh);//Emil's code for AMaZE + void lmmse_interpolate_omp(int winw, int winh, array2D &rawData, array2D &red, array2D &green, array2D &blue, int iterations); + void amaze_demosaic_RT(int winx, int winy, int winw, int winh, array2D &rawData, array2D &red, array2D &green, array2D &blue);//Emil's code for AMaZE void fast_demosaic(int winx, int winy, int winw, int winh );//Emil's code for fast demosaicing void dcb_demosaic(int iterations, bool dcb_enhance); void ahd_demosaic(int winx, int winy, int winw, int winh); @@ -253,8 +261,10 @@ protected: void xtransborder_interpolate (int border); void xtrans_interpolate (const int passes, const bool useCieLab); void fast_xtrans_interpolate (); + void pixelshift(int winx, int winy, int winw, int winh, const RAWParams::BayerSensor &bayerParams, unsigned int frame, const std::string &model, float rawWpCorrection); void hflip (Imagefloat* im); void vflip (Imagefloat* im); + void getRawValues(int x, int y, int rotate, int &R, int &G, int &B); }; } diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index 8e832a734..0bb188b5d 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -470,7 +470,37 @@ int refreshmap[rtengine::NUMOFEVENTS] = { RETINEX, // EvRetinexgaintransmission RETINEX, // EvLskal OUTPUTPROFILE, // EvOBPCompens - ALLNORAW // EvWBtempBias + ALLNORAW, // EvWBtempBias + DARKFRAME, // EvRawImageNum + DEMOSAIC, // EvPixelShiftMotion + DEMOSAIC, // EvPixelShiftMotionCorrection + DEMOSAIC, // EvPixelShiftStddevFactorGreen + DEMOSAIC, // EvPixelShiftEperIso + DEMOSAIC, // EvPixelShiftNreadIso + DEMOSAIC, // EvPixelShiftPrnu + DEMOSAIC, // EvPixelshiftShowMotion + DEMOSAIC, // EvPixelshiftShowMotionMaskOnly + DEMOSAIC, // EvPixelShiftAutomatic + DEMOSAIC, // EvPixelShiftNonGreenHorizontal + DEMOSAIC, // EvPixelShiftNonGreenVertical + DEMOSAIC, // EvPixelShiftNonGreenCross + DEMOSAIC, // EvPixelShiftStddevFactorRed + DEMOSAIC, // EvPixelShiftStddevFactorBlue + DEMOSAIC, // EvPixelShiftNonGreenCross2 + DEMOSAIC, // EvPixelShiftNonGreenAmaze + DEMOSAIC, // EvPixelShiftGreen + DEMOSAIC, // EvPixelShiftRedBlueWeight + DEMOSAIC, // EvPixelShiftBlur + DEMOSAIC, // EvPixelShiftSigma + DEMOSAIC, // EvPixelShiftSum + DEMOSAIC, // EvPixelShiftExp0 + DEMOSAIC, // EvPixelShiftHoleFill + DEMOSAIC, // EvPixelShiftMedian + DEMOSAIC, // EvPixelShiftMedian3 + DEMOSAIC, // EvPixelShiftMotionMethod + DEMOSAIC, // EvPixelShiftSmooth + DEMOSAIC, // EvPixelShiftLmmse + DEMOSAIC // EvPixelShiftEqualBright }; diff --git a/rtengine/rtengine.h b/rtengine/rtengine.h index 4a87414b8..a018033d7 100644 --- a/rtengine/rtengine.h +++ b/rtengine/rtengine.h @@ -443,19 +443,13 @@ public: * @brief Initializes the RT engine * @param s is a struct of basic settings * @param baseDir base directory of RT's installation dir - * @param userSettingsDir RT's base directory in the user's settings dir */ -int init (const Settings* s, Glib::ustring baseDir, Glib::ustring userSettingsDir); + * @param userSettingsDir RT's base directory in the user's settings dir + * @param loadAll if false, don't load the various dependencies (profiles, HALDClut files, ...), they'll be loaded from disk each time they'll be used (launching time improvement) */ +int init (const Settings* s, Glib::ustring baseDir, Glib::ustring userSettingsDir, bool loadAll = true); /** Cleanup the RT engine (static variables) */ void cleanup (); -/** Returns the available working profile names - * @return a vector of the available working profile names */ -std::vector getWorkingProfiles (); -/** Returns the available output gammas - * @return a vector of the available gamma names */ -std::vector getGamma (); - /** This class holds all the necessary informations to accomplish the full processing of the image */ class ProcessingJob { diff --git a/rtengine/rtthumbnail.cc b/rtengine/rtthumbnail.cc index b30f8a915..cabcb34ab 100644 --- a/rtengine/rtthumbnail.cc +++ b/rtengine/rtthumbnail.cc @@ -176,7 +176,8 @@ Thumbnail* Thumbnail::loadFromImage (const Glib::ustring& fname, int &w, int &h, Thumbnail* Thumbnail::loadQuickFromRaw (const Glib::ustring& fname, RawMetaDataLocation& rml, int &w, int &h, int fixwh, bool rotate, bool inspectorMode) { RawImage *ri = new RawImage(fname); - int r = ri->loadRaw(false, false); + unsigned int imageNum = 0; + int r = ri->loadRaw(false, imageNum, false); if( r ) { delete ri; @@ -290,7 +291,9 @@ RawMetaDataLocation Thumbnail::loadMetaDataFromRaw (const Glib::ustring& fname) rml.ciffLength = -1; RawImage ri(fname); - int r = ri.loadRaw(false); + unsigned int imageNum = 0; + + int r = ri.loadRaw(false, imageNum); if( !r ) { rml.exifBase = ri.get_exifBase(); @@ -301,10 +304,12 @@ RawMetaDataLocation Thumbnail::loadMetaDataFromRaw (const Glib::ustring& fname) return rml; } -Thumbnail* Thumbnail::loadFromRaw (const Glib::ustring& fname, RawMetaDataLocation& rml, int &w, int &h, int fixwh, double wbEq, bool rotate) +Thumbnail* Thumbnail::loadFromRaw (const Glib::ustring& fname, RawMetaDataLocation& rml, int &w, int &h, int fixwh, double wbEq, bool rotate, int imageNum) { RawImage *ri = new RawImage (fname); - int r = ri->loadRaw(1, 0); + unsigned int tempImageNum = 0; + + int r = ri->loadRaw(1, tempImageNum, 0); if( r ) { delete ri; @@ -770,7 +775,7 @@ void Thumbnail::init () cam2xyz[i][j] += xyz_sRGB[i][k] * colorMatrix[k][j]; } - camProfile = iccStore->createFromMatrix (cam2xyz, false, "Camera"); + camProfile = ICCStore::getInstance()->createFromMatrix (cam2xyz, false, "Camera"); } Thumbnail::Thumbnail () : @@ -1033,13 +1038,13 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, int rhei bool opautili = false; if(params.colorToning.enabled) { - TMatrix wprof = iccStore->workingSpaceMatrix (params.icm.working); + TMatrix wprof = ICCStore::getInstance()->workingSpaceMatrix (params.icm.working); double wp[3][3] = { {wprof[0][0], wprof[0][1], wprof[0][2]}, {wprof[1][0], wprof[1][1], wprof[1][2]}, {wprof[2][0], wprof[2][1], wprof[2][2]} }; - TMatrix wiprof = iccStore->workingSpaceInverseMatrix (params.icm.working); + TMatrix wiprof = ICCStore::getInstance()->workingSpaceInverseMatrix (params.icm.working); double wip[3][3] = { {wiprof[0][0], wiprof[0][1], wiprof[0][2]}, {wiprof[1][0], wiprof[1][1], wiprof[1][2]}, diff --git a/rtengine/rtthumbnail.h b/rtengine/rtthumbnail.h index 56e68f815..c40a226c4 100644 --- a/rtengine/rtthumbnail.h +++ b/rtengine/rtthumbnail.h @@ -78,7 +78,7 @@ public: void getDimensions (int& w, int& h, double& scaleFac); static Thumbnail* loadQuickFromRaw (const Glib::ustring& fname, rtengine::RawMetaDataLocation& rml, int &w, int &h, int fixwh, bool rotate, bool inspectorMode = false); - static Thumbnail* loadFromRaw (const Glib::ustring& fname, RawMetaDataLocation& rml, int &w, int &h, int fixwh, double wbEq, bool rotate); + static Thumbnail* loadFromRaw (const Glib::ustring& fname, RawMetaDataLocation& rml, int &w, int &h, int fixwh, double wbEq, bool rotate, int imageNum); static Thumbnail* loadFromImage (const Glib::ustring& fname, int &w, int &h, int fixwh, double wbEq, bool inspectorMode = false); static RawMetaDataLocation loadMetaDataFromRaw (const Glib::ustring& fname); diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index bd8dc0e23..360b892c4 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -163,6 +163,7 @@ private: ImProcFunctions ipf (¶ms, true); pp = PreviewProps(0, 0, fw, fh, 1); + imgsrc->setCurrentFrame(params.raw.bayersensor.imageNum); imgsrc->preprocess( params.raw, params.lensProf, params.coarse, params.dirpyrDenoise.enabled); if (params.toneCurve.autoexp) {// this enabled HLRecovery @@ -868,13 +869,13 @@ private: bool opautili = false; if(params.colorToning.enabled) { - TMatrix wprof = iccStore->workingSpaceMatrix (params.icm.working); + TMatrix wprof = ICCStore::getInstance()->workingSpaceMatrix (params.icm.working); double wp[3][3] = { {wprof[0][0], wprof[0][1], wprof[0][2]}, {wprof[1][0], wprof[1][1], wprof[1][2]}, {wprof[2][0], wprof[2][1], wprof[2][2]} }; - TMatrix wiprof = iccStore->workingSpaceInverseMatrix (params.icm.working); + TMatrix wiprof = ICCStore::getInstance()->workingSpaceInverseMatrix (params.icm.working); double wip[3][3] = { {wiprof[0][0], wiprof[0][1], wiprof[0][2]}, {wiprof[1][0], wiprof[1][1], wiprof[1][2]}, @@ -1228,7 +1229,7 @@ private: //or selected Free gamma useLCMS = false; - if ((jprof = iccStore->createCustomGammaOutputProfile (params.icm, ga)) == nullptr) { + if ((jprof = ICCStore::getInstance()->createCustomGammaOutputProfile (params.icm, ga)) == nullptr) { useLCMS = true; } @@ -1284,15 +1285,15 @@ private: if (!useLCMS) { // use corrected sRGB profile in order to apply a good TRC if present, otherwise use LCMS2 profile generated by lab2rgb16 w/ gamma ProfileContent pc(jprof); - readyImg->setOutputProfile (pc.data, pc.length); + readyImg->setOutputProfile(pc.getData().c_str(), pc.getData().size()); } } else { // use the selected output profile if present, otherwise use LCMS2 profile generate by lab2rgb16 w/ gamma if (params.icm.output != "" && params.icm.output != ColorManagementParams::NoICMString) { - // if iccStore->getProfile send back an object, then iccStore->getContent will do too - cmsHPROFILE jprof = iccStore->getProfile(params.icm.output); //get outProfile + // if ICCStore::getInstance()->getProfile send back an object, then ICCStore::getInstance()->getContent will do too + cmsHPROFILE jprof = ICCStore::getInstance()->getProfile(params.icm.output); //get outProfile if (jprof == nullptr) { if (settings->verbose) { @@ -1303,8 +1304,8 @@ private: printf("Using \"%s\" output profile\n", params.icm.output.c_str()); } - ProfileContent pc = iccStore->getContent (params.icm.output); - readyImg->setOutputProfile (pc.data, pc.length); + ProfileContent pc = ICCStore::getInstance()->getContent (params.icm.output); + readyImg->setOutputProfile(pc.getData().c_str(), pc.getData().size()); } } else { // No ICM diff --git a/rtengine/stdimagesource.cc b/rtengine/stdimagesource.cc index 1ef4bf884..5e7001028 100644 --- a/rtengine/stdimagesource.cc +++ b/rtengine/stdimagesource.cc @@ -102,7 +102,7 @@ void StdImageSource::getSampleFormat (const Glib::ustring &fname, IIOSampleForma * and RT's image data type (Image8, Image16 and Imagefloat), then it will * load the image into it */ -int StdImageSource::load (const Glib::ustring &fname, bool batch) +int StdImageSource::load (const Glib::ustring &fname, int imageNum, bool batch) { fileName = fname; @@ -218,7 +218,7 @@ void StdImageSource::colorSpaceConversion (Imagefloat* im, const ColorManagement bool skipTransform = false; cmsHPROFILE in = nullptr; - cmsHPROFILE out = iccStore->workingSpace (cmp.working); + cmsHPROFILE out = ICCStore::getInstance()->workingSpace (cmp.working); if (cmp.input == "(embedded)" || cmp.input == "" || cmp.input == "(camera)" || cmp.input == "(cameraICC)") { if (embedded) { @@ -227,12 +227,12 @@ void StdImageSource::colorSpaceConversion (Imagefloat* im, const ColorManagement if (sampleFormat & (IIOSF_LOGLUV24 | IIOSF_LOGLUV32 | IIOSF_FLOAT)) { skipTransform = true; } else { - in = iccStore->getsRGBProfile (); + in = ICCStore::getInstance()->getsRGBProfile (); } } } else { if (cmp.input != "(none)") { - in = iccStore->getProfile (cmp.input); + in = ICCStore::getInstance()->getProfile (cmp.input); if (in == nullptr && embedded) { in = embedded; @@ -240,7 +240,7 @@ void StdImageSource::colorSpaceConversion (Imagefloat* im, const ColorManagement if (sampleFormat & (IIOSF_LOGLUV24 | IIOSF_LOGLUV32 | IIOSF_FLOAT)) { skipTransform = true; } else { - in = iccStore->getsRGBProfile (); + in = ICCStore::getInstance()->getsRGBProfile (); } } } @@ -249,7 +249,7 @@ void StdImageSource::colorSpaceConversion (Imagefloat* im, const ColorManagement if (!skipTransform && in) { if(in == embedded && cmsGetColorSpace(in) != cmsSigRgbData) { // if embedded profile is not an RGB profile, use sRGB printf("embedded profile is not an RGB profile, using sRGB as input profile\n"); - in = iccStore->getsRGBProfile (); + in = ICCStore::getInstance()->getsRGBProfile (); } lcmsMutex->lock (); diff --git a/rtengine/stdimagesource.h b/rtengine/stdimagesource.h index 0ef487a75..1dbddf325 100644 --- a/rtengine/stdimagesource.h +++ b/rtengine/stdimagesource.h @@ -42,7 +42,7 @@ public: StdImageSource (); ~StdImageSource (); - int load (const Glib::ustring &fname, bool batch = false); + int load (const Glib::ustring &fname, int imageNum = 0, bool batch = false); void getImage (const ColorTemp &ctemp, int tran, Imagefloat* image, const PreviewProps &pp, const ToneCurveParams &hrp, const ColorManagementParams &cmp, const RAWParams &raw); ColorTemp getWB () const { @@ -95,6 +95,11 @@ public: { return rgbSourceModified; } + void setCurrentFrame(unsigned int frameNum) {} + + void getRawValues(int x, int y, int rotate, int &R, int &G, int &B) { R = G = B = 0;} + + }; } #endif diff --git a/rtexif/pentaxattribs.cc b/rtexif/pentaxattribs.cc index 9fc6b6fb6..e18be36cd 100644 --- a/rtexif/pentaxattribs.cc +++ b/rtexif/pentaxattribs.cc @@ -42,6 +42,7 @@ public: choices[3] = "TIFF"; choices[4] = "RAW"; choices[5] = "Premium"; + choices[6] = "RAW (HDR enabled)"; choices[7] = "RAW (pixel shift enabled)"; choices[65535] = "n/a"; } @@ -371,6 +372,12 @@ public: choices[37] = "128000"; choices[38] = "160000"; choices[39] = "204800"; + choices[40] = "256000"; + choices[41] = "320000"; + choices[42] = "409600"; + choices[43] = "512000"; + choices[44] = "640000"; + choices[45] = "819200"; choices[50] = "50"; choices[100] = "100"; choices[200] = "200"; diff --git a/rtgui/CMakeLists.txt b/rtgui/CMakeLists.txt index 153c1a0ef..98382bf07 100644 --- a/rtgui/CMakeLists.txt +++ b/rtgui/CMakeLists.txt @@ -1,7 +1,11 @@ +# common source files for both CLI and non-CLI execautables +set (CLISOURCEFILES + paramsedited.cc options.cc multilangmgr.cc pathutils.cc edit.cc threadutils.cc + main-cli.cc) -set (BASESOURCEFILES +set (NONCLISOURCEFILES editwindow.cc batchtoolpanelcoord.cc paramsedited.cc cropwindow.cc previewhandler.cc previewwindow.cc navigator.cc indclippedpanel.cc previewmodepanel.cc filterpanel.cc - exportpanel.cc cursormanager.cc rtwindow.cc renamedlg.cc recentbrowser.cc placesbrowser.cc filepanel.cc editorpanel.cc batchqueuepanel.cc + exportpanel.cc cursormanager.cc rtwindow.cc renamedlg.cc recentbrowser.cc placesbrowser.cc filepanel.cc editorpanel.cc batchqueuepanel.cc checkbox.cc ilabel.cc thumbbrowserbase.cc adjuster.cc filebrowserentry.cc filebrowser.cc filethumbnailbuttonset.cc cachemanager.cc cacheimagedata.cc shcselector.cc perspective.cc thresholdselector.cc thresholdadjuster.cc clipboard.cc thumbimageupdater.cc bqentryupdater.cc lensgeom.cc coloredbar.cc edit.cc coordinateadjuster.cc @@ -21,7 +25,7 @@ set (BASESOURCEFILES preferences.cc profilepanel.cc saveasdlg.cc saveformatpanel.cc soundman.cc splash.cc thumbnail.cc tonecurve.cc toolbar.cc - guiutils.cc threadutils.cc zoompanel.cc toolpanelcoord.cc + pathutils.cc guiutils.cc threadutils.cc zoompanel.cc toolpanelcoord.cc thumbbrowserentrybase.cc batchqueueentry.cc batchqueue.cc lwbutton.cc lwbuttonset.cc batchqueuebuttonset.cc browserfilter.cc exiffiltersettings.cc @@ -42,7 +46,8 @@ if (APPLE) set (EXTRA_INCDIR ${EXTRA_INCDIR} ${MacIntegration_INCLUDE_DIRS}) endif (APPLE) if (WIN32) - set (EXTRA_SRC windirmonitor.cc myicon.rc) + set (EXTRA_SRC_CLI myicon.rc) + set (EXTRA_SRC_NONCLI myicon.rc windirmonitor.cc) set (EXTRA_LIB_RTGUI winmm) include_directories (${EXTRA_INCDIR} ${GLIB2_INCLUDE_DIRS} ${GLIBMM_INCLUDE_DIRS} ${GTK_INCLUDE_DIRS} ${GTKMM_INCLUDE_DIRS} ${GIO_INCLUDE_DIRS} ${GIOMM_INCLUDE_DIRS}) @@ -62,14 +67,27 @@ endif (WIN32) # create config.h which defines where data are stored configure_file ("${CMAKE_CURRENT_SOURCE_DIR}/config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/config.h") -add_executable (rth ${EXTRA_SRC} ${BASESOURCEFILES}) -add_dependencies (rth UpdateInfo) +# create new executables targets +add_executable (rth ${EXTRA_SRC_NONCLI} ${NONCLISOURCEFILES}) +#add_executable (rth-cli ${EXTRA_SRC_CLI} ${CLISOURCEFILES}) +# add dependencies to executables targets +add_dependencies (rth UpdateInfo) +#add_dependencies (rth-cli UpdateInfo) + +# set executables targets properties, i.e. output filename and compile flags set_target_properties (rth PROPERTIES COMPILE_FLAGS "${CMAKE_CXX_FLAGS}" OUTPUT_NAME rawtherapee) -#target_link_libraries (rth rtengine ${JPEG_LIBRARIES} ${PNG_LIBRARIES} ${ZLIB_LIBRARIES} ${TIFF_LIBRARIES} ${EXTRA_LIB} ${GOBJECT_LIBRARIES} ${GTHREAD_LIBRARIES} -# ${GLIB2_LIBRARIES} ${GLIBMM_LIBRARIES} ${GTK_LIBRARIES} ${GTKMM_LIBRARIES} ${GIO_LIBRARIES} ${GIOMM_LIBRARIES} ${LCMS_LIBRARIES} ${IPTCDATA_LIBRARIES}) +#set_target_properties (rth-cli PROPERTIES COMPILE_FLAGS "${CMAKE_CXX_FLAGS}" OUTPUT_NAME rawtherapee-cli) + +# add linked libraries dependencies to executables targets target_link_libraries (rth rtengine ${JPEG_LIBRARIES} ${PNG_LIBRARIES} ${ZLIB_LIBRARIES} ${TIFF_LIBRARIES} ${GOBJECT_LIBRARIES} ${GTHREAD_LIBRARIES} ${GLIB2_LIBRARIES} ${GLIBMM_LIBRARIES} ${GTK_LIBRARIES} ${GTKMM_LIBRARIES} ${GIO_LIBRARIES} ${GIOMM_LIBRARIES} ${LCMS_LIBRARIES} ${EXPAT_LIBRARIES} ${FFTW3F_LIBRARIES} ${IPTCDATA_LIBRARIES} ${CANBERRA-GTK_LIBRARIES} ${EXTRA_LIB_RTGUI}) -install (TARGETS rth DESTINATION ${BINDIR}) +#target_link_libraries (rth-cli rtengine ${JPEG_LIBRARIES} ${PNG_LIBRARIES} ${ZLIB_LIBRARIES} ${TIFF_LIBRARIES} ${GOBJECT_LIBRARIES} ${GTHREAD_LIBRARIES} +# ${GLIB2_LIBRARIES} ${GLIBMM_LIBRARIES} ${CAIROMM_LIBRARIES} ${GIO_LIBRARIES} ${GIOMM_LIBRARIES} ${LCMS_LIBRARIES} ${EXPAT_LIBRARIES} +# ${FFTW3F_LIBRARIES} ${IPTCDATA_LIBRARIES} ${EXTRA_LIB_RTGUI}) + +# install executables +install (TARGETS rth DESTINATION ${BINDIR}) +#install (TARGETS rth-cli DESTINATION ${BINDIR}) diff --git a/rtgui/batchqueuepanel.cc b/rtgui/batchqueuepanel.cc index 66f5341fa..1810501b5 100644 --- a/rtgui/batchqueuepanel.cc +++ b/rtgui/batchqueuepanel.cc @@ -115,6 +115,8 @@ BatchQueuePanel::BatchQueuePanel (FileCatalog* aFileCatalog) if (Glib::file_test (options.savePathFolder, Glib::FILE_TEST_IS_DIR)) { outdirFolder->set_current_folder (options.savePathFolder); + } else { + outdirFolder->set_current_folder (Glib::get_home_dir()); } outdirFolderButton = 0; diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index e1bf790a6..75b27b360 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -22,6 +22,7 @@ using namespace rtengine; using namespace rtengine::procparams; + BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RAW_LABEL"), true) { Gtk::HBox* hb1 = Gtk::manage (new Gtk::HBox ()); @@ -38,6 +39,30 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA hb1->pack_end (*method, Gtk::PACK_EXPAND_WIDGET, 4); pack_start( *hb1, Gtk::PACK_SHRINK, 4); + imageNumberBox = Gtk::manage (new Gtk::HBox ()); + imageNumberBox->pack_start (*Gtk::manage (new Gtk::Label ( M("TP_RAW_IMAGENUM") + ": ")), Gtk::PACK_SHRINK, 4); + imageNumber = Gtk::manage (new MyComboBoxText ()); + imageNumber->append("1"); + imageNumber->append("2"); + imageNumber->append("3"); + imageNumber->append("4"); + imageNumber->set_active(0); + imageNumberBox->set_tooltip_text(M("TP_RAW_IMAGENUM_TOOLTIP")); + imageNumberBox->pack_end (*imageNumber, Gtk::PACK_EXPAND_WIDGET, 4); + pack_start( *imageNumberBox, Gtk::PACK_SHRINK, 4); + + pack_start( *Gtk::manage( new Gtk::HSeparator()), Gtk::PACK_SHRINK, 0 ); + ccSteps = Gtk::manage (new Adjuster (M("TP_RAW_FALSECOLOR"), 0, 5, 1, 0 )); + ccSteps->setAdjusterListener (this); + + if (ccSteps->delay < options.adjusterMaxDelay) { + ccSteps->delay = options.adjusterMaxDelay; + } + + ccSteps->show(); + pack_start( *ccSteps, Gtk::PACK_SHRINK, 4); + + dcbOptions = Gtk::manage (new Gtk::VBox ()); dcbIterations = Gtk::manage (new Adjuster (M("TP_RAW_DCBITERATIONS"), 0, 5, 1, 2)); @@ -48,7 +73,8 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA } dcbIterations->show(); - dcbEnhance = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_DCBENHANCE"))); + dcbEnhance = Gtk::manage (new CheckBox(M("TP_RAW_DCBENHANCE"), multiImage)); + dcbEnhance->setCheckBoxListener (this); dcbOptions->pack_start(*dcbIterations); dcbOptions->pack_start(*dcbEnhance); pack_start( *dcbOptions, Gtk::PACK_SHRINK, 4); @@ -67,92 +93,413 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA lmmseOptions->pack_start(*lmmseIterations); pack_start( *lmmseOptions, Gtk::PACK_SHRINK, 4); - pack_start( *Gtk::manage( new Gtk::HSeparator()), Gtk::PACK_SHRINK, 0 ); - ccSteps = Gtk::manage (new Adjuster (M("TP_RAW_FALSECOLOR"), 0, 5, 1, 0 )); - ccSteps->setAdjusterListener (this); + pixelShiftFrame = Gtk::manage (new Gtk::VBox ()); + pixelShiftFrame->set_border_width(0); - if (ccSteps->delay < options.adjusterMaxDelay) { - ccSteps->delay = options.adjusterMaxDelay; + pixelShiftEqualBright = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTEQUALBRIGHT"), multiImage)); + pixelShiftEqualBright->setCheckBoxListener (this); + pixelShiftEqualBright->set_tooltip_text (M("TP_RAW_PIXELSHIFTEQUALBRIGHT_TOOLTIP")); + pixelShiftFrame->pack_start(*pixelShiftEqualBright); + + Gtk::HBox* hb3 = Gtk::manage (new Gtk::HBox ()); + hb3->pack_start (*Gtk::manage (new Gtk::Label ( M("TP_RAW_PIXELSHIFTMOTIONMETHOD") + ": ")), Gtk::PACK_SHRINK, 4); + pixelShiftMotionMethod = Gtk::manage (new MyComboBoxText ()); + pixelShiftMotionMethod->append(M("TP_RAW_PIXELSHIFTMM_OFF")); + pixelShiftMotionMethod->append(M("TP_RAW_PIXELSHIFTMM_AUTO")); + pixelShiftMotionMethod->append(M("TP_RAW_PIXELSHIFTMM_CUSTOM")); + pixelShiftMotionMethod->set_active(RAWParams::BayerSensor::ePSMotionCorrectionMethod::Automatic); + pixelShiftMotionMethod->show(); + hb3->pack_start(*pixelShiftMotionMethod); + pixelShiftFrame->pack_start(*hb3); + + pixelShiftOptions = Gtk::manage (new Gtk::VBox ()); + pixelShiftOptions->set_border_width(0); + + pixelShiftShowMotion = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTSHOWMOTION"), multiImage)); + pixelShiftShowMotion->setCheckBoxListener (this); + pixelShiftShowMotion->set_tooltip_text (M("TP_RAW_PIXELSHIFTSHOWMOTION_TOOLTIP")); + pixelShiftFrame->pack_start(*pixelShiftShowMotion); + + pixelShiftShowMotionMaskOnly = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTSHOWMOTIONMASKONLY"), multiImage)); + pixelShiftShowMotionMaskOnly->setCheckBoxListener (this); + pixelShiftShowMotionMaskOnly->set_tooltip_text (M("TP_RAW_PIXELSHIFTSHOWMOTIONMASKONLY_TOOLTIP")); + pixelShiftFrame->pack_start(*pixelShiftShowMotionMaskOnly); + +#ifdef PIXELSHIFTDEV + pixelShiftAutomatic = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTADAPTIVE"), multiImage)); + pixelShiftAutomatic->setCheckBoxListener (this); + pixelShiftOptions->pack_start(*pixelShiftAutomatic); +#endif + pixelShiftGreen = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTGREEN"), multiImage)); + pixelShiftGreen->setCheckBoxListener (this); + pixelShiftOptions->pack_start(*pixelShiftGreen); + + pixelShiftNonGreenCross = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTNONGREENCROSS"), multiImage)); + pixelShiftNonGreenCross->setCheckBoxListener (this); + pixelShiftOptions->pack_start(*pixelShiftNonGreenCross); + + pixelShiftHoleFill = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTHOLEFILL"), multiImage)); + pixelShiftHoleFill->setCheckBoxListener (this); + pixelShiftHoleFill->set_tooltip_text (M("TP_RAW_PIXELSHIFTHOLEFILL_TOOLTIP")); + pixelShiftOptions->pack_start(*pixelShiftHoleFill); + + pixelShiftBlur = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTBLUR"), multiImage)); + pixelShiftBlur->setCheckBoxListener (this); + pixelShiftBlur->set_tooltip_text (M("TP_RAW_PIXELSHIFTSIGMA_TOOLTIP")); + pixelShiftOptions->pack_start(*pixelShiftBlur); + + pixelShiftSigma = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTSIGMA"), 0.5, 25, 0.1, 1.0)); + pixelShiftSigma->set_tooltip_text (M("TP_RAW_PIXELSHIFTSIGMA_TOOLTIP")); + pixelShiftSigma->setAdjusterListener (this); + + if (pixelShiftSigma->delay < options.adjusterMaxDelay) { + pixelShiftSigma->delay = options.adjusterMaxDelay; } - ccSteps->show(); - pack_start( *ccSteps, Gtk::PACK_SHRINK, 4); + pixelShiftSigma->show(); + pixelShiftOptions->pack_start(*pixelShiftSigma); - //pack_start( *Gtk::manage( new Gtk::HSeparator()), Gtk::PACK_SHRINK, 0 ); - //allOptions = Gtk::manage (new Gtk::VBox ()); - //allEnhance = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_ALLENHANCE"))); - //allOptions->pack_start(*allEnhance); - //pack_start( *allOptions, Gtk::PACK_SHRINK, 4); - methodconn = method->signal_changed().connect( sigc::mem_fun(*this, &BayerProcess::methodChanged) ); - dcbEnhconn = dcbEnhance->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::dcbEnhanceChanged), true); - //allEnhconn = allEnhance->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::allEnhanceChanged), true); + pixelShiftSmooth = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTSMOOTH"), 0, 1, 0.05, 0.7)); + pixelShiftSmooth->set_tooltip_text (M("TP_RAW_PIXELSHIFTSMOOTH_TOOLTIP")); + pixelShiftSmooth->setAdjusterListener (this); + + if (pixelShiftSmooth->delay < options.adjusterMaxDelay) { + pixelShiftSmooth->delay = options.adjusterMaxDelay; + } + + pixelShiftSmooth->show(); + pixelShiftOptions->pack_start(*pixelShiftSmooth); + + pixelShiftEperIso = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTEPERISO"), -5.0, 5.0, 0.05, 0.0)); + pixelShiftEperIso->set_tooltip_text(M("TP_RAW_PIXELSHIFTEPERISO_TOOLTIP")); + pixelShiftEperIso->setAdjusterListener (this); + + if (pixelShiftEperIso->delay < options.adjusterMaxDelay) { + pixelShiftEperIso->delay = options.adjusterMaxDelay; + } + + pixelShiftEperIso->show(); + pixelShiftOptions->pack_start(*pixelShiftEperIso); + + + pixelShiftMedian = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTMEDIAN"), multiImage)); + pixelShiftMedian->setCheckBoxListener (this); + pixelShiftMedian->set_tooltip_text (M("TP_RAW_PIXELSHIFTMEDIAN_TOOLTIP")); + pixelShiftOptions->pack_start(*pixelShiftMedian); + + +#ifdef PIXELSHIFTDEV + pixelShiftMedian3 = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTMEDIAN3"), multiImage)); + pixelShiftMedian3->setCheckBoxListener (this); + pixelShiftMedian3->set_tooltip_text (M("TP_RAW_PIXELSHIFTMEDIAN3_TOOLTIP")); + pixelShiftOptions->pack_start(*pixelShiftMedian3); + + pixelShiftNonGreenCross2 = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTNONGREENCROSS2"), multiImage)); + pixelShiftNonGreenCross2->setCheckBoxListener (this); + pixelShiftOptions->pack_start(*pixelShiftNonGreenCross2); + + pixelShiftNonGreenAmaze = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTNONGREENAMAZE"), multiImage)); + pixelShiftNonGreenAmaze->setCheckBoxListener (this); + pixelShiftOptions->pack_start(*pixelShiftNonGreenAmaze); + + pixelShiftNonGreenHorizontal = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTNONGREENHORIZONTAL"), multiImage)); + pixelShiftNonGreenHorizontal->setCheckBoxListener (this); + pixelShiftOptions->pack_start(*pixelShiftNonGreenHorizontal); + + pixelShiftNonGreenVertical = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTNONGREENVERTICAL"), multiImage)); + pixelShiftNonGreenVertical->setCheckBoxListener (this); + pixelShiftOptions->pack_start(*pixelShiftNonGreenVertical); + + pixelShiftExp0 = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTEXP0"), multiImage)); + pixelShiftExp0->setCheckBoxListener (this); + pixelShiftOptions->pack_start(*pixelShiftExp0); +#endif + pixelShiftLmmse = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTLMMSE"), multiImage)); + pixelShiftLmmse->setCheckBoxListener (this); + pixelShiftLmmse->set_tooltip_text (M("TP_RAW_PIXELSHIFTLMMSE_TOOLTIP")); + pixelShiftOptions->pack_start(*pixelShiftLmmse); + +#ifdef PIXELSHIFTDEV + pixelShiftMotion = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTMOTION"), 0, 100, 1, 70)); + pixelShiftMotion->setAdjusterListener (this); + pixelShiftMotion->set_tooltip_text (M("TP_RAW_PIXELSHIFTMOTION_TOOLTIP")); + + if (pixelShiftMotion->delay < options.adjusterMaxDelay) { + pixelShiftMotion->delay = options.adjusterMaxDelay; + } + pixelShiftMotion->show(); + pixelShiftOptions->pack_start(*pixelShiftMotion); + + Gtk::HBox* hb2 = Gtk::manage (new Gtk::HBox ()); + hb2->pack_start (*Gtk::manage (new Gtk::Label ( M("TP_RAW_PIXELSHIFTMOTIONCORRECTION") + ": ")), Gtk::PACK_SHRINK, 0); + pixelShiftMotionCorrection = Gtk::manage (new MyComboBoxText ()); + pixelShiftMotionCorrection->append("1x1"); + pixelShiftMotionCorrection->append("1x2"); + pixelShiftMotionCorrection->append("3x3"); + pixelShiftMotionCorrection->append("5x5"); + pixelShiftMotionCorrection->append("7x7"); + pixelShiftMotionCorrection->append("3x3 new"); + pixelShiftMotionCorrection->set_active(0); + pixelShiftMotionCorrection->show(); + hb2->pack_start(*pixelShiftMotionCorrection); + pixelShiftOptions->pack_start(*hb2); + pixelShiftStddevFactorGreen = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTSTDDEVFACTORGREEN"), 2, 8, 0.1, 5)); + pixelShiftStddevFactorGreen->setAdjusterListener (this); + + if (pixelShiftStddevFactorGreen->delay < options.adjusterMaxDelay) { + pixelShiftStddevFactorGreen->delay = options.adjusterMaxDelay; + } + + pixelShiftStddevFactorGreen->show(); + pixelShiftOptions->pack_start(*pixelShiftStddevFactorGreen); + + pixelShiftStddevFactorRed = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTSTDDEVFACTORRED"), 1, 8, 0.1, 5)); + pixelShiftStddevFactorRed->setAdjusterListener (this); + + if (pixelShiftStddevFactorRed->delay < options.adjusterMaxDelay) { + pixelShiftStddevFactorRed->delay = options.adjusterMaxDelay; + } + + pixelShiftStddevFactorRed->show(); + pixelShiftOptions->pack_start(*pixelShiftStddevFactorRed); + + pixelShiftStddevFactorBlue = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTSTDDEVFACTORBLUE"), 1, 8, 0.1, 5)); + pixelShiftStddevFactorBlue->setAdjusterListener (this); + + if (pixelShiftStddevFactorBlue->delay < options.adjusterMaxDelay) { + pixelShiftStddevFactorBlue->delay = options.adjusterMaxDelay; + } + + pixelShiftStddevFactorBlue->show(); + pixelShiftOptions->pack_start(*pixelShiftStddevFactorBlue); +#endif + +#ifdef PIXELSHIFTDEV + pixelShiftNreadIso = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTNREADISO"), -2.0, 2.0, 0.05, 0.0)); + pixelShiftNreadIso->setAdjusterListener (this); + + if (pixelShiftNreadIso->delay < options.adjusterMaxDelay) { + pixelShiftNreadIso->delay = options.adjusterMaxDelay; + } + + pixelShiftNreadIso->show(); + pixelShiftOptions->pack_start(*pixelShiftNreadIso); + + + pixelShiftPrnu = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTPRNU"), 0.3, 2.0, 0.1, 1.0)); + pixelShiftPrnu->setAdjusterListener (this); + + if (pixelShiftPrnu->delay < options.adjusterMaxDelay) { + pixelShiftPrnu->delay = options.adjusterMaxDelay; + } + + pixelShiftPrnu->show(); + pixelShiftOptions->pack_start(*pixelShiftPrnu); + + pixelShiftSum = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTMASKTHRESHOLD"), 1.0, 8.0, 0.1, 3.0)); + pixelShiftSum->setAdjusterListener (this); + + if (pixelShiftSum->delay < options.adjusterMaxDelay) { + pixelShiftSum->delay = options.adjusterMaxDelay; + } + + pixelShiftSum->show(); + pixelShiftOptions->pack_start(*pixelShiftSum); + + pixelShiftRedBlueWeight = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTREDBLUEWEIGHT"), 0.1, 1.0, 0.1, 0.7)); + pixelShiftRedBlueWeight->setAdjusterListener (this); + + if (pixelShiftRedBlueWeight->delay < options.adjusterMaxDelay) { + pixelShiftRedBlueWeight->delay = options.adjusterMaxDelay; + } + + pixelShiftRedBlueWeight->show(); + pixelShiftOptions->pack_start(*pixelShiftRedBlueWeight); +#endif + + pixelShiftFrame->pack_start(*pixelShiftOptions); + pixelShiftOptions->hide(); + + pack_start( *pixelShiftFrame, Gtk::PACK_SHRINK, 4); + + method->connect(method->signal_changed().connect( sigc::mem_fun(*this, &BayerProcess::methodChanged) )); + imageNumber->connect(imageNumber->signal_changed().connect( sigc::mem_fun(*this, &BayerProcess::imageNumberChanged) )); + pixelShiftMotionMethod->connect(pixelShiftMotionMethod->signal_changed().connect( sigc::mem_fun(*this, &BayerProcess::pixelShiftMotionMethodChanged) )); +#ifdef PIXELSHIFTDEV + pixelShiftMotionCorrection->connect(pixelShiftMotionCorrection->signal_changed().connect( sigc::mem_fun(*this, &BayerProcess::psMotionCorrectionChanged) )); +#endif } void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited) { disableListener (); - methodconn.block (true); - dcbEnhconn.block (true); + method->block (true); + imageNumber->block (true); //allEnhconn.block (true); +#ifdef PIXELSHIFTDEV + pixelShiftMotionCorrection->block (true); +#endif method->set_active(procparams::RAWParams::BayerSensor::numMethods); + imageNumber->set_active(pp->raw.bayersensor.imageNum); - for( size_t i = 0; i < procparams::RAWParams::BayerSensor::numMethods; i++) + for( size_t i = 0; i < procparams::RAWParams::BayerSensor::numMethods; i++) { if( pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[i]) { method->set_active(i); - oldSelection = i; + oldMethod = i; break; } - - if(pedited ) { - ccSteps->setEditedState (pedited->raw.bayersensor.ccSteps ? Edited : UnEdited); - dcbIterations->setEditedState ( pedited->raw.bayersensor.dcbIterations ? Edited : UnEdited); - dcbEnhance->set_inconsistent(!pedited->raw.bayersensor.dcbEnhance); - //allEnhance->set_inconsistent(!pedited->raw.bayersensor.allEnhance); - lmmseIterations->setEditedState ( pedited->raw.bayersensor.lmmseIterations ? Edited : UnEdited); - - if( !pedited->raw.bayersensor.method ) { - method->set_active(procparams::RAWParams::BayerSensor::numMethods); // No name - } } //allEnhance->set_active(pp->raw.bayersensor.all_enhance); dcbIterations->setValue (pp->raw.bayersensor.dcb_iterations); - dcbEnhance->set_active(pp->raw.bayersensor.dcb_enhance); + dcbEnhance->setValue (pp->raw.bayersensor.dcb_enhance); + pixelShiftShowMotion->setValue (pp->raw.bayersensor.pixelshiftShowMotion); + if (!batchMode) { + pixelShiftShowMotionMaskOnly->set_sensitive (pp->raw.bayersensor.pixelshiftShowMotion); + } + pixelShiftShowMotionMaskOnly->setValue (pp->raw.bayersensor.pixelshiftShowMotionMaskOnly); + pixelShiftHoleFill->setValue (pp->raw.bayersensor.pixelShiftHoleFill); + pixelShiftMedian->setValue (pp->raw.bayersensor.pixelShiftMedian); + pixelShiftGreen->setValue (pp->raw.bayersensor.pixelShiftGreen); + pixelShiftBlur->setValue (pp->raw.bayersensor.pixelShiftBlur); + if (!batchMode) { + pixelShiftSmooth->set_sensitive (pp->raw.bayersensor.pixelShiftBlur); + } + pixelShiftSmooth->setValue (pp->raw.bayersensor.pixelShiftSmoothFactor); + pixelShiftLmmse->setValue (pp->raw.bayersensor.pixelShiftLmmse); + pixelShiftEqualBright->setValue (pp->raw.bayersensor.pixelShiftEqualBright); + pixelShiftNonGreenCross->setValue (pp->raw.bayersensor.pixelShiftNonGreenCross); ccSteps->setValue (pp->raw.bayersensor.ccSteps); - - if (pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::dcb] || - method->get_active_row_number() == procparams::RAWParams::BayerSensor::numMethods) { - dcbOptions->show(); - } else { - dcbOptions->hide(); - } - lmmseIterations->setValue (pp->raw.bayersensor.lmmse_iterations); + pixelShiftMotionMethod->set_active ((int)pp->raw.bayersensor.pixelShiftMotionCorrectionMethod); + pixelShiftEperIso->setValue (pp->raw.bayersensor.pixelShiftEperIso); + pixelShiftSigma->setValue (pp->raw.bayersensor.pixelShiftSigma); + if (!batchMode) { + pixelShiftSigma->set_sensitive (pp->raw.bayersensor.pixelShiftBlur); + } +#ifdef PIXELSHIFTDEV + pixelShiftStddevFactorGreen->setValue (pp->raw.bayersensor.pixelShiftStddevFactorGreen); + pixelShiftStddevFactorRed->setValue (pp->raw.bayersensor.pixelShiftStddevFactorRed); + pixelShiftStddevFactorBlue->setValue (pp->raw.bayersensor.pixelShiftStddevFactorBlue); + pixelShiftSum->setValue (pp->raw.bayersensor.pixelShiftSum); + pixelShiftMedian3->setValue (pp->raw.bayersensor.pixelShiftMedian3); + if (!batchMode) { + pixelShiftMedian3->set_sensitive (pixelShiftMedian->getValue() != CheckValue::off); + } + pixelShiftAutomatic->setValue (pp->raw.bayersensor.pixelShiftAutomatic); + pixelShiftNonGreenHorizontal->setValue (pp->raw.bayersensor.pixelShiftNonGreenHorizontal); + pixelShiftNonGreenVertical->setValue (pp->raw.bayersensor.pixelShiftNonGreenVertical); + pixelShiftExp0->setValue (pp->raw.bayersensor.pixelShiftExp0); + pixelShiftNonGreenCross2->setValue (pp->raw.bayersensor.pixelShiftNonGreenCross2); + pixelShiftNonGreenAmaze->setValue (pp->raw.bayersensor.pixelShiftNonGreenAmaze); + pixelShiftMotion->setValue (pp->raw.bayersensor.pixelShiftMotion); + pixelShiftMotionCorrection->setValue ((int)pp->raw.bayersensor.pixelShiftMotionCorrection); + if (!batchMode) { + pixelShiftHoleFill->set_sensitive (pixelShiftAutomatic->getValue () != CheckValue::off && pixelShiftMotionCorrection->get_active_row_number() == 5); + pixelShiftBlur->set_sensitive(pixelShiftAutomatic->getValue () != CheckValue::off && pixelShiftMotionCorrection->get_active_row_number() == 5); + pixelShiftSmooth->set_sensitive(pixelShiftAutomatic->getValue () != CheckValue::off && pixelShiftMotionCorrection->get_active_row_number() == 5 && pixelShiftBlur->getValue() != CheckValue::off); + } + pixelShiftNreadIso->setValue (pp->raw.bayersensor.pixelShiftNreadIso); + pixelShiftPrnu->setValue (pp->raw.bayersensor.pixelShiftPrnu); + pixelShiftRedBlueWeight->setValue (pp->raw.bayersensor.pixelShiftRedBlueWeight); +#endif - if (pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::lmmse] || - method->get_active_row_number() == procparams::RAWParams::BayerSensor::numMethods) { - lmmseOptions->show(); - } else { - lmmseOptions->hide(); + if(pedited) { + ccSteps->setEditedState (pedited->raw.bayersensor.ccSteps ? Edited : UnEdited); + dcbIterations->setEditedState ( pedited->raw.bayersensor.dcbIterations ? Edited : UnEdited); + dcbEnhance->setEdited (pedited->raw.bayersensor.dcbEnhance); + pixelShiftShowMotion->setEdited (pedited->raw.bayersensor.pixelshiftShowMotion); + pixelShiftShowMotionMaskOnly->setEdited (pedited->raw.bayersensor.pixelshiftShowMotionMaskOnly); + pixelShiftHoleFill->setEdited (pedited->raw.bayersensor.pixelShiftHoleFill); + pixelShiftMedian->setEdited(pedited->raw.bayersensor.pixelShiftMedian); + pixelShiftGreen->setEdited (pedited->raw.bayersensor.pixelShiftGreen); + pixelShiftBlur->setEdited (pedited->raw.bayersensor.pixelShiftBlur); + pixelShiftSmooth->setEditedState ( pedited->raw.bayersensor.pixelShiftSmooth ? Edited : UnEdited); + pixelShiftLmmse->setEdited (pedited->raw.bayersensor.pixelShiftLmmse); + pixelShiftEqualBright->setEdited (pedited->raw.bayersensor.pixelShiftEqualBright); + pixelShiftNonGreenCross->setEdited (pedited->raw.bayersensor.pixelShiftNonGreenCross); + lmmseIterations->setEditedState ( pedited->raw.bayersensor.lmmseIterations ? Edited : UnEdited); + pixelShiftEperIso->setEditedState ( pedited->raw.bayersensor.pixelShiftEperIso ? Edited : UnEdited); + pixelShiftSigma->setEditedState ( pedited->raw.bayersensor.pixelShiftSigma ? Edited : UnEdited); +#ifdef PIXELSHIFTDEV + pixelShiftNreadIso->setEditedState ( pedited->raw.bayersensor.pixelShiftNreadIso ? Edited : UnEdited); + pixelShiftPrnu->setEditedState ( pedited->raw.bayersensor.pixelShiftPrnu ? Edited : UnEdited); + pixelShiftStddevFactorGreen->setEditedState ( pedited->raw.bayersensor.pixelShiftStddevFactorGreen ? Edited : UnEdited); + pixelShiftStddevFactorRed->setEditedState ( pedited->raw.bayersensor.pixelShiftStddevFactorRed ? Edited : UnEdited); + pixelShiftStddevFactorBlue->setEditedState ( pedited->raw.bayersensor.pixelShiftStddevFactorBlue ? Edited : UnEdited); + pixelShiftSum->setEditedState ( pedited->raw.bayersensor.pixelShiftSum ? Edited : UnEdited); + pixelShiftAutomatic->setEdited (pedited->raw.bayersensor.pixelShiftAutomatic); + pixelShiftNonGreenHorizontal->setEdited (pedited->raw.bayersensor.pixelShiftNonGreenHorizontal); + pixelShiftNonGreenVertical->setEdited (pedited->raw.bayersensor.pixelShiftNonGreenVertical); + pixelShiftMedian3->setEdited (pedited->raw.bayersensor.pixelShiftMedian3); + pixelShiftExp0->setEdited (pedited->raw.bayersensor.pixelShiftExp0); + pixelShiftNonGreenCross2->setEdited (pedited->raw.bayersensor.pixelShiftNonGreenCross2); + pixelShiftNonGreenAmaze->setEdited (pedited->raw.bayersensor.pixelShiftNonGreenAmaze); + pixelShiftMotion->setEditedState ( pedited->raw.bayersensor.pixelShiftMotion ? Edited : UnEdited); + pixelShiftRedBlueWeight->setEditedState ( pedited->raw.bayersensor.pixelShiftRedBlueWeight ? Edited : UnEdited); +#endif + + if(!pedited->raw.bayersensor.method) { + method->set_active(procparams::RAWParams::BayerSensor::numMethods); // No name + } + if(!pedited->raw.bayersensor.imageNum) { + imageNumber->set_active_text(M("GENERAL_UNCHANGED")); + } +#ifdef PIXELSHIFTDEV + if(!pedited->raw.bayersensor.pixelShiftMotionCorrection) { + pixelShiftMotionCorrection->set_active_text(M("GENERAL_UNCHANGED")); + } +#endif + if(!pedited->raw.bayersensor.pixelShiftMotionCorrectionMethod) { + pixelShiftMotionMethod->set_active_text(M("GENERAL_UNCHANGED")); + } } - // Flase color suppression is applied to all demozaicing method, so don't hide anything - /*if (pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::eahd] || - pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::hphd] || - pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::vng4]) - ccSteps->show(); - else - ccSteps->hide();*/ + if (!batchMode) { + if (pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::dcb] || + method->get_active_row_number() == procparams::RAWParams::BayerSensor::numMethods) { + dcbOptions->show(); + } else { + dcbOptions->hide(); + } + if (pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::lmmse] || + method->get_active_row_number() == procparams::RAWParams::BayerSensor::numMethods) { + lmmseOptions->show(); + } else { + lmmseOptions->hide(); + } + if (pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::pixelshift] || + method->get_active_row_number() == procparams::RAWParams::BayerSensor::numMethods) { + if(pp->raw.bayersensor.pixelShiftMotionCorrectionMethod == RAWParams::BayerSensor::Custom) { + pixelShiftOptions->show(); + } else { + pixelShiftOptions->hide(); + } + pixelShiftFrame->show(); + } else { + pixelShiftFrame->hide(); + } + + // Flase color suppression is applied to all demozaicing method, so don't hide anything + /*if (pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::eahd] || + pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::hphd] || + pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::vng4]) + ccSteps->show(); + else + ccSteps->hide();*/ + } - lastDCBen = pp->raw.bayersensor.dcb_enhance; //lastALLen = pp->raw.bayersensor.all_enhance; - methodconn.block (false); - dcbEnhconn.block (false); + method->block (false); +#ifdef PIXELSHIFTDEV + pixelShiftMotionCorrection->block (false); +#endif + imageNumber->block (false); //allEnhconn.block (false); enableListener (); @@ -162,24 +509,91 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe { pp->raw.bayersensor.ccSteps = ccSteps->getIntValue(); pp->raw.bayersensor.dcb_iterations = dcbIterations->getIntValue(); - pp->raw.bayersensor.dcb_enhance = dcbEnhance->get_active(); - //pp->raw.bayersensor.all_enhance = allEnhance->get_active(); + pp->raw.bayersensor.dcb_enhance = dcbEnhance->getLastActive (); + //pp->raw.bayersensor.all_enhance = allEnhance->getLastActive (); pp->raw.bayersensor.lmmse_iterations = lmmseIterations->getIntValue(); + pp->raw.bayersensor.pixelShiftMotionCorrectionMethod = (RAWParams::BayerSensor::ePSMotionCorrectionMethod)pixelShiftMotionMethod->get_active_row_number(); + pp->raw.bayersensor.pixelShiftEperIso = pixelShiftEperIso->getValue(); + pp->raw.bayersensor.pixelShiftSigma = pixelShiftSigma->getValue(); + pp->raw.bayersensor.pixelshiftShowMotion = pixelShiftShowMotion->getLastActive (); + pp->raw.bayersensor.pixelshiftShowMotionMaskOnly = pixelShiftShowMotionMaskOnly->getLastActive (); + pp->raw.bayersensor.pixelShiftHoleFill = pixelShiftHoleFill->getLastActive (); + pp->raw.bayersensor.pixelShiftMedian = pixelShiftMedian->getLastActive (); + pp->raw.bayersensor.pixelShiftGreen = pixelShiftGreen->getLastActive (); + pp->raw.bayersensor.pixelShiftBlur = pixelShiftBlur->getLastActive (); + pp->raw.bayersensor.pixelShiftSmoothFactor = pixelShiftSmooth->getValue(); + pp->raw.bayersensor.pixelShiftLmmse = pixelShiftLmmse->getLastActive (); + pp->raw.bayersensor.pixelShiftEqualBright = pixelShiftEqualBright->getLastActive (); + pp->raw.bayersensor.pixelShiftNonGreenCross = pixelShiftNonGreenCross->getLastActive (); +#ifdef PIXELSHIFTDEV + pp->raw.bayersensor.pixelShiftStddevFactorGreen = pixelShiftStddevFactorGreen->getValue(); + pp->raw.bayersensor.pixelShiftStddevFactorRed = pixelShiftStddevFactorRed->getValue(); + pp->raw.bayersensor.pixelShiftStddevFactorBlue = pixelShiftStddevFactorBlue->getValue(); + pp->raw.bayersensor.pixelShiftSum = pixelShiftSum->getValue(); + pp->raw.bayersensor.pixelShiftMedian3 = pixelShiftMedian3->getLastActive (); + pp->raw.bayersensor.pixelShiftMotion = pixelShiftMotion->getIntValue(); + pp->raw.bayersensor.pixelShiftMotionCorrection = (RAWParams::BayerSensor::ePSMotionCorrection)pixelShiftMotionCorrection->get_active_row_number(); + pp->raw.bayersensor.pixelShiftAutomatic = pixelShiftAutomatic->getLastActive (); + pp->raw.bayersensor.pixelShiftNonGreenHorizontal = pixelShiftNonGreenHorizontal->getLastActive (); + pp->raw.bayersensor.pixelShiftNonGreenVertical = pixelShiftNonGreenVertical->getLastActive (); + pp->raw.bayersensor.pixelShiftExp0 = pixelShiftExp0->getLastActive (); + pp->raw.bayersensor.pixelShiftNonGreenCross2 = pixelShiftNonGreenCross2->getLastActive (); + pp->raw.bayersensor.pixelShiftNonGreenAmaze = pixelShiftNonGreenAmaze->getLastActive (); + pp->raw.bayersensor.pixelShiftNreadIso = pixelShiftNreadIso->getValue(); + pp->raw.bayersensor.pixelShiftPrnu = pixelShiftPrnu->getValue(); + pp->raw.bayersensor.pixelShiftRedBlueWeight = pixelShiftRedBlueWeight->getValue(); +#endif int currentRow = method->get_active_row_number(); - if( currentRow >= 0 && currentRow < procparams::RAWParams::BayerSensor::numMethods) { pp->raw.bayersensor.method = procparams::RAWParams::BayerSensor::methodstring[currentRow]; } + currentRow = imageNumber->get_active_row_number(); + if (currentRow < 4) { + pp->raw.bayersensor.imageNum = currentRow; + } + + if (pedited) { pedited->raw.bayersensor.ccSteps = ccSteps->getEditedState (); pedited->raw.bayersensor.method = method->get_active_row_number() != procparams::RAWParams::BayerSensor::numMethods; + pedited->raw.bayersensor.imageNum = imageNumber->get_active_text() != M("GENERAL_UNCHANGED"); pedited->raw.bayersensor.dcbIterations = dcbIterations->getEditedState (); pedited->raw.bayersensor.dcbEnhance = !dcbEnhance->get_inconsistent(); //pedited->raw.bayersensor.allEnhance = !allEnhance->get_inconsistent(); pedited->raw.bayersensor.lmmseIterations = lmmseIterations->getEditedState (); - + pedited->raw.bayersensor.pixelShiftMotionCorrectionMethod = pixelShiftMotionMethod->get_active_text() != M("GENERAL_UNCHANGED"); + pedited->raw.bayersensor.pixelShiftEperIso = pixelShiftEperIso->getEditedState (); + pedited->raw.bayersensor.pixelShiftSigma = pixelShiftSigma->getEditedState (); + pedited->raw.bayersensor.pixelshiftShowMotion = !pixelShiftShowMotion->get_inconsistent(); + pedited->raw.bayersensor.pixelshiftShowMotionMaskOnly = !pixelShiftShowMotionMaskOnly->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftHoleFill = !pixelShiftHoleFill->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftMedian = !pixelShiftMedian->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftGreen = !pixelShiftGreen->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftBlur = !pixelShiftBlur->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftSmooth = pixelShiftSmooth->getEditedState(); + pedited->raw.bayersensor.pixelShiftLmmse = !pixelShiftLmmse->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftEqualBright = !pixelShiftEqualBright->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftNonGreenCross = !pixelShiftNonGreenCross->get_inconsistent(); +#ifdef PIXELSHIFTDEV + pedited->raw.bayersensor.pixelShiftStddevFactorGreen = pixelShiftStddevFactorGreen->getEditedState (); + pedited->raw.bayersensor.pixelShiftStddevFactorRed = pixelShiftStddevFactorRed->getEditedState (); + pedited->raw.bayersensor.pixelShiftStddevFactorBlue = pixelShiftStddevFactorBlue->getEditedState (); + pedited->raw.bayersensor.pixelShiftSum = pixelShiftSum->getEditedState (); + pedited->raw.bayersensor.pixelShiftMedian3 = !pixelShiftMedian3->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftMotion = pixelShiftMotion->getEditedState (); + pedited->raw.bayersensor.pixelShiftMotionCorrection = pixelShiftMotionCorrection->get_active_text() != M("GENERAL_UNCHANGED"); + pedited->raw.bayersensor.pixelShiftAutomatic = !pixelShiftAutomatic->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftNonGreenHorizontal = !pixelShiftNonGreenHorizontal->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftNonGreenVertical = !pixelShiftNonGreenVertical->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftExp0 = !pixelShiftExp0->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftNonGreenCross2 = !pixelShiftNonGreenCross2->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftNonGreenAmaze = !pixelShiftNonGreenAmaze->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftNreadIso = pixelShiftNreadIso->getEditedState (); + pedited->raw.bayersensor.pixelShiftPrnu = pixelShiftPrnu->getEditedState (); + pedited->raw.bayersensor.pixelShiftRedBlueWeight = pixelShiftRedBlueWeight->getEditedState (); +#endif } } @@ -187,27 +601,81 @@ void BayerProcess::setBatchMode(bool batchMode) { method->append (M("GENERAL_UNCHANGED")); method->set_active(procparams::RAWParams::BayerSensor::numMethods); // No name - dcbOptions->hide(); - lmmseOptions->hide(); +#ifdef PIXELSHIFTDEV + pixelShiftMotionCorrection->append (M("GENERAL_UNCHANGED")); + pixelShiftMotionCorrection->set_active_text (M("GENERAL_UNCHANGED")); +#endif + pixelShiftMotionMethod->append (M("GENERAL_UNCHANGED")); + pixelShiftMotionMethod->set_active_text (M("GENERAL_UNCHANGED")); + imageNumber->append (M("GENERAL_UNCHANGED")); + imageNumber->set_active_text (M("GENERAL_UNCHANGED")); ToolPanel::setBatchMode (batchMode); ccSteps->showEditedCB (); dcbIterations->showEditedCB (); lmmseIterations->showEditedCB (); +#ifdef PIXELSHIFTDEV + pixelShiftMotion->showEditedCB (); + pixelShiftSum->showEditedCB (); + pixelShiftStddevFactorGreen->showEditedCB (); + pixelShiftStddevFactorRed->showEditedCB (); + pixelShiftStddevFactorBlue->showEditedCB (); + pixelShiftNreadIso->showEditedCB (); + pixelShiftPrnu->showEditedCB (); + pixelShiftRedBlueWeight->showEditedCB (); +#endif + pixelShiftEperIso->showEditedCB (); + pixelShiftSigma->showEditedCB (); } void BayerProcess::setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited) { dcbIterations->setDefault( defParams->raw.bayersensor.dcb_iterations); lmmseIterations->setDefault( defParams->raw.bayersensor.lmmse_iterations); +#ifdef PIXELSHIFTDEV + pixelShiftMotion->setDefault( defParams->raw.bayersensor.pixelShiftMotion); + pixelShiftSum->setDefault( defParams->raw.bayersensor.pixelShiftSum); + pixelShiftStddevFactorGreen->setDefault( defParams->raw.bayersensor.pixelShiftStddevFactorGreen); + pixelShiftStddevFactorRed->setDefault( defParams->raw.bayersensor.pixelShiftStddevFactorRed); + pixelShiftStddevFactorBlue->setDefault( defParams->raw.bayersensor.pixelShiftStddevFactorBlue); + pixelShiftNreadIso->setDefault( defParams->raw.bayersensor.pixelShiftNreadIso); + pixelShiftPrnu->setDefault( defParams->raw.bayersensor.pixelShiftPrnu); + pixelShiftRedBlueWeight->setDefault( defParams->raw.bayersensor.pixelShiftRedBlueWeight); +#endif + pixelShiftEperIso->setDefault( defParams->raw.bayersensor.pixelShiftEperIso); + pixelShiftSigma->setDefault( defParams->raw.bayersensor.pixelShiftSigma); ccSteps->setDefault (defParams->raw.bayersensor.ccSteps); if (pedited) { dcbIterations->setDefaultEditedState( pedited->raw.bayersensor.dcbIterations ? Edited : UnEdited); lmmseIterations->setDefaultEditedState( pedited->raw.bayersensor.lmmseIterations ? Edited : UnEdited); +#ifdef PIXELSHIFTDEV + pixelShiftMotion->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftMotion ? Edited : UnEdited); + pixelShiftSum->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftSum ? Edited : UnEdited); + pixelShiftStddevFactorGreen->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftStddevFactorGreen ? Edited : UnEdited); + pixelShiftStddevFactorRed->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftStddevFactorRed ? Edited : UnEdited); + pixelShiftStddevFactorBlue->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftStddevFactorBlue ? Edited : UnEdited); + pixelShiftNreadIso->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftNreadIso ? Edited : UnEdited); + pixelShiftPrnu->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftPrnu ? Edited : UnEdited); + pixelShiftRedBlueWeight->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftRedBlueWeight ? Edited : UnEdited); +#endif + pixelShiftEperIso->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftEperIso ? Edited : UnEdited); + pixelShiftSigma->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftSigma ? Edited : UnEdited); ccSteps->setDefaultEditedState(pedited->raw.bayersensor.ccSteps ? Edited : UnEdited); } else { dcbIterations->setDefaultEditedState( Irrelevant ); lmmseIterations->setDefaultEditedState( Irrelevant ); +#ifdef PIXELSHIFTDEV + pixelShiftMotion->setDefaultEditedState( Irrelevant ); + pixelShiftSum->setDefaultEditedState( Irrelevant ); + pixelShiftStddevFactorGreen->setDefaultEditedState( Irrelevant ); + pixelShiftStddevFactorRed->setDefaultEditedState( Irrelevant ); + pixelShiftStddevFactorBlue->setDefaultEditedState( Irrelevant ); + pixelShiftNreadIso->setDefaultEditedState( Irrelevant ); + pixelShiftPrnu->setDefaultEditedState( Irrelevant ); + pixelShiftRedBlueWeight->setDefaultEditedState( Irrelevant ); +#endif + pixelShiftEperIso->setDefaultEditedState( Irrelevant ); + pixelShiftSigma->setDefaultEditedState( Irrelevant ); ccSteps->setDefaultEditedState(Irrelevant ); } } @@ -221,24 +689,82 @@ void BayerProcess::adjusterChanged (Adjuster* a, double newval) listener->panelChanged (EvDemosaicFalseColorIter, a->getTextValue() ); } else if (a == lmmseIterations) { listener->panelChanged (EvDemosaicLMMSEIter, a->getTextValue() ); +#ifdef PIXELSHIFTDEV + } else if (a == pixelShiftMotion) { + listener->panelChanged (EvPixelShiftMotion, a->getTextValue() ); + } else if (a == pixelShiftSum) { + listener->panelChanged (EvPixelShiftSum, a->getTextValue() ); + } else if (a == pixelShiftStddevFactorGreen) { + listener->panelChanged (EvPixelShiftStddevFactorGreen, a->getTextValue() ); + } else if (a == pixelShiftStddevFactorRed) { + listener->panelChanged (EvPixelShiftStddevFactorRed, a->getTextValue() ); + } else if (a == pixelShiftStddevFactorBlue) { + listener->panelChanged (EvPixelShiftStddevFactorBlue, a->getTextValue() ); + } else if (a == pixelShiftNreadIso) { + listener->panelChanged (EvPixelShiftNreadIso, a->getTextValue() ); + } else if (a == pixelShiftPrnu) { + listener->panelChanged (EvPixelShiftPrnu, a->getTextValue() ); + } else if (a == pixelShiftRedBlueWeight) { + listener->panelChanged (EvPixelShiftRedBlueWeight, a->getTextValue() ); +#endif + } else if (a == pixelShiftEperIso) { + listener->panelChanged (EvPixelShiftEperIso, a->getTextValue() ); + } else if (a == pixelShiftSigma) { + listener->panelChanged (EvPixelShiftSigma, a->getTextValue() ); + } else if (a == pixelShiftSmooth) { + listener->panelChanged (EvPixelShiftSmooth, a->getTextValue() ); } } } +#ifdef PIXELSHIFTDEV +void BayerProcess::psMotionCorrectionChanged () +{ + if (!batchMode) { + if(pixelShiftMotionCorrection->get_active_row_number() == 5) { + pixelShiftBlur->set_sensitive(true); + pixelShiftHoleFill->set_sensitive(true); + pixelShiftSmooth->set_sensitive(pixelShiftBlur->getValue() != CheckValue::off); + } else { + pixelShiftBlur->set_sensitive(false); + pixelShiftHoleFill->set_sensitive(false); + pixelShiftSmooth->set_sensitive(false); + } + } + + if (listener) { + listener->panelChanged (EvPixelShiftMotionCorrection, pixelShiftMotionCorrection->get_active_text()); + } +} +#endif + void BayerProcess::methodChanged () { int curSelection = method->get_active_row_number(); - if ( curSelection == procparams::RAWParams::BayerSensor::dcb) { - dcbOptions->show(); - } else { - dcbOptions->hide(); - } + if (!batchMode) { + if ( curSelection == procparams::RAWParams::BayerSensor::dcb) { + dcbOptions->show(); + } else { + dcbOptions->hide(); + } - if ( curSelection == procparams::RAWParams::BayerSensor::lmmse) { - lmmseOptions->show(); - } else { - lmmseOptions->hide(); + if ( curSelection == procparams::RAWParams::BayerSensor::lmmse) { + lmmseOptions->show(); + } else { + lmmseOptions->hide(); + } + + if ( curSelection == procparams::RAWParams::BayerSensor::pixelshift) { + if(pixelShiftMotionMethod->get_active_row_number() == 2) { + pixelShiftOptions->show(); + } else { + pixelShiftOptions->hide(); + } + pixelShiftFrame->show(); + } else { + pixelShiftFrame->hide(); + } } Glib::ustring methodName = ""; @@ -247,52 +773,155 @@ void BayerProcess::methodChanged () if( curSelection >= 0 && curSelection < procparams::RAWParams::BayerSensor::numMethods) { methodName = procparams::RAWParams::BayerSensor::methodstring[curSelection]; - if (curSelection == procparams::RAWParams::BayerSensor::mono || oldSelection == procparams::RAWParams::BayerSensor::mono) { + if (curSelection == procparams::RAWParams::BayerSensor::mono || oldMethod == procparams::RAWParams::BayerSensor::mono) { ppreq = true; } } - oldSelection = curSelection; + oldMethod = curSelection; if (listener) { listener->panelChanged (ppreq ? EvDemosaicMethodPreProc : EvDemosaicMethod, methodName); } } -void BayerProcess::dcbEnhanceChanged () +void BayerProcess::imageNumberChanged () { - if (batchMode) { - if (dcbEnhance->get_inconsistent()) { - dcbEnhance->set_inconsistent (false); - dcbEnhconn.block (true); - dcbEnhance->set_active (false); - dcbEnhconn.block (false); - } else if (lastDCBen) { - dcbEnhance->set_inconsistent (true); - } - - lastDCBen = dcbEnhance->get_active (); - } - if (listener) { - listener->panelChanged (EvDemosaicDCBEnhanced, dcbEnhance->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + listener->panelChanged (EvRawImageNum, imageNumber->get_active_text()); } } -/*void BayerProcess::allEnhanceChanged () +void BayerProcess::checkBoxToggled (CheckBox* c, CheckValue newval) { - if (batchMode) { - if (allEnhance->get_inconsistent()) { - allEnhance->set_inconsistent (false); - allEnhconn.block (true); - allEnhance->set_active (false); - allEnhconn.block (false); + if (c == dcbEnhance) { + if (listener) { + listener->panelChanged (EvDemosaicDCBEnhanced, dcbEnhance->getValueAsStr ()); + } + } else if (c == pixelShiftShowMotion) { + if (!batchMode) { + pixelShiftShowMotionMaskOnly->set_sensitive(newval != CheckValue::off); + } + if (listener) { + listener->panelChanged (EvPixelshiftShowMotion, pixelShiftShowMotion->getValueAsStr ()); + } + } else if (c == pixelShiftShowMotionMaskOnly) { + if (listener) { + listener->panelChanged (EvPixelshiftShowMotionMaskOnly, pixelShiftShowMotionMaskOnly->getValueAsStr ()); + } + } else if (c == pixelShiftHoleFill) { + if (listener) { + listener->panelChanged (EvPixelShiftHoleFill, pixelShiftHoleFill->getValueAsStr ()); + } + } else if (c == pixelShiftMedian) { +#ifdef PIXELSHIFTDEV + if (!batchMode) { + pixelShiftMedian3->set_sensitive(newval != CheckValue::off); + } +#endif + if (listener) { + listener->panelChanged (EvPixelShiftMedian, pixelShiftMedian->getValueAsStr ()); + } + } else if (c == pixelShiftGreen) { + if (listener) { + listener->panelChanged (EvPixelShiftGreen, pixelShiftGreen->getValueAsStr ()); + } + } else if (c == pixelShiftBlur) { + if (!batchMode) { + pixelShiftSmooth->set_sensitive(newval != CheckValue::off); + pixelShiftSigma->set_sensitive(newval != CheckValue::off); + } + if (listener) { + listener->panelChanged (EvPixelShiftBlur, pixelShiftBlur->getValueAsStr ()); + } + } else if (c == pixelShiftLmmse) { + if (listener) { + listener->panelChanged (EvPixelShiftLmmse, pixelShiftLmmse->getValueAsStr ()); + } + } else if (c == pixelShiftEqualBright) { + if (listener) { + listener->panelChanged (EvPixelShiftEqualBright, pixelShiftEqualBright->getValueAsStr ()); + } + } else if (c == pixelShiftNonGreenCross) { + if (listener) { + listener->panelChanged (EvPixelShiftNonGreenCross, pixelShiftNonGreenCross->getValueAsStr ()); } - else if (lastALLen) - allEnhance->set_inconsistent (true); - - lastALLen = allEnhance->get_active (); } - if (listener) - listener->panelChanged (EvDemosaicALLEnhanced, allEnhance->get_active()?M("GENERAL_ENABLED"):M("GENERAL_DISABLED")); -}*/ +#ifdef PIXELSHIFTDEV + else if (c == pixelShiftAutomatic) { + if (!batchMode) { + pixelShiftMotion->set_sensitive(!newval != CheckValue::off); + pixelShiftEperIso->set_sensitive(newval != CheckValue::off); + pixelShiftNreadIso->set_sensitive(newval != CheckValue::off); + pixelShiftPrnu->set_sensitive(newval != CheckValue::off); + pixelShiftSigma->set_sensitive(newval != CheckValue::off); + pixelShiftSum->set_sensitive(newval != CheckValue::off); + pixelShiftRedBlueWeight->set_sensitive(newval != CheckValue::off); + pixelShiftStddevFactorGreen->set_sensitive(newval != CheckValue::off); + pixelShiftStddevFactorRed->set_sensitive(newval != CheckValue::off); + pixelShiftStddevFactorBlue->set_sensitive(newval != CheckValue::off); + pixelShiftNonGreenHorizontal->set_sensitive(newval != CheckValue::off); + pixelShiftNonGreenVertical->set_sensitive(newval != CheckValue::off); + pixelShiftHoleFill->set_sensitive(newval != CheckValue::off && pixelShiftMotionCorrection->get_active_row_number() == 5); + pixelShiftMedian3->set_sensitive(newval != CheckValue::off && pixelShiftMedian->getValue () != CheckValue::off); + pixelShiftGreen->set_sensitive(newval != CheckValue::off); + pixelShiftBlur->set_sensitive(newval != CheckValue::off && pixelShiftMotionCorrection->get_active_row_number() == 5); + pixelShiftSmooth->set_sensitive(newval != CheckValue::off && pixelShiftMotionCorrection->get_active_row_number() == 5 && pixelShiftBlur->getValue () != CheckValue::off); + pixelShiftExp0->set_sensitive(newval != CheckValue::off); + pixelShiftNonGreenCross->set_sensitive(newval != CheckValue::off); + pixelShiftNonGreenCross2->set_sensitive(newval != CheckValue::off); + pixelShiftNonGreenAmaze->set_sensitive(newval != CheckValue::off); + } + + if (listener) { + listener->panelChanged (EvPixelShiftAutomatic, pixelShiftAutomatic->getValueAsStr ()); + } + } else if (c == pixelShiftNonGreenHorizontal) { + if (listener) { + listener->panelChanged (EvPixelShiftNonGreenHorizontal, pixelShiftNonGreenHorizontal->getValueAsStr ()); + } + } else if (c == pixelShiftNonGreenVertical) { + if (listener) { + listener->panelChanged (EvPixelShiftNonGreenVertical, pixelShiftNonGreenVertical->getValueAsStr ()); + } + } else if (c == pixelShiftMedian3) { + if (listener) { + listener->panelChanged (EvPixelShiftMedian3, pixelShiftMedian3->getValueAsStr ()); + } + } else if (c == pixelShiftExp0) { + if (listener) { + listener->panelChanged (EvPixelShiftExp0, pixelShiftExp0->getValueAsStr ()); + } + } else if (c == pixelShiftNonGreenCross2) { + if (listener) { + listener->panelChanged (EvPixelShiftGreenAmaze, pixelShiftNonGreenCross2->getValueAsStr ()); + } + } else if (c == pixelShiftNonGreenAmaze) { + if (listener) { + listener->panelChanged (EvPixelShiftNonGreenAmaze, pixelShiftNonGreenAmaze->getValueAsStr ()); + } + } +#endif +} + +void BayerProcess::pixelShiftMotionMethodChanged () +{ + if (!batchMode) { + if(pixelShiftMotionMethod->get_active_row_number() == 0) { + pixelShiftOptions->hide(); + pixelShiftShowMotion->hide(); + pixelShiftShowMotionMaskOnly->hide(); + } else if(pixelShiftMotionMethod->get_active_row_number() == 2) { + pixelShiftOptions->show(); + pixelShiftShowMotion->show(); + pixelShiftShowMotionMaskOnly->show(); + } else { + pixelShiftOptions->hide(); + pixelShiftShowMotion->show(); + pixelShiftShowMotionMaskOnly->show(); + } + } + if (listener) { + listener->panelChanged (EvPixelShiftMotionMethod, pixelShiftMotionMethod->get_active_text()); + } +} diff --git a/rtgui/bayerprocess.h b/rtgui/bayerprocess.h index b78002ea3..9c2285951 100644 --- a/rtgui/bayerprocess.h +++ b/rtgui/bayerprocess.h @@ -21,29 +21,59 @@ #include #include "adjuster.h" +#include "checkbox.h" #include "guiutils.h" #include "toolpanel.h" -class BayerProcess : public ToolParamBlock, public AdjusterListener, public FoldableToolPanel +class BayerProcess : public ToolParamBlock, public AdjusterListener, public CheckBoxListener, public FoldableToolPanel { protected: MyComboBoxText* method; + Gtk::HBox *imageNumberBox; + MyComboBoxText* imageNumber; Adjuster* ccSteps; Gtk::VBox *dcbOptions; Adjuster* dcbIterations; - Gtk::CheckButton* dcbEnhance; - //Gtk::VBox *allOptions; - //Gtk::CheckButton* allEnhance; + CheckBox* dcbEnhance; Gtk::VBox *lmmseOptions; Adjuster* lmmseIterations; - - bool lastDCBen; - int oldSelection; - //bool lastALLen; - sigc::connection methodconn, dcbEnhconn; //,allEnhconn; + Gtk::VBox *pixelShiftFrame; + Gtk::VBox *pixelShiftOptions; + MyComboBoxText* pixelShiftMotionMethod; + CheckBox* pixelShiftShowMotion; + CheckBox* pixelShiftShowMotionMaskOnly; + CheckBox* pixelShiftNonGreenCross; + CheckBox* pixelShiftGreen; + CheckBox* pixelShiftBlur; + CheckBox* pixelShiftHoleFill; + CheckBox* pixelShiftMedian; + CheckBox* pixelShiftLmmse; + CheckBox* pixelShiftEqualBright; + Adjuster* pixelShiftSmooth; + Adjuster* pixelShiftEperIso; + Adjuster* pixelShiftSigma; +#ifdef PIXELSHIFTDEV + Adjuster* pixelShiftSum; + Adjuster* pixelShiftMotion; + MyComboBoxText* pixelShiftMotionCorrection; + CheckBox* pixelShiftAutomatic; + CheckBox* pixelShiftNonGreenHorizontal; + CheckBox* pixelShiftNonGreenVertical; + CheckBox* pixelShiftNonGreenCross2; + CheckBox* pixelShiftNonGreenAmaze; + CheckBox* pixelShiftExp0; + CheckBox* pixelShiftMedian3; + Adjuster* pixelShiftStddevFactorGreen; + Adjuster* pixelShiftStddevFactorRed; + Adjuster* pixelShiftStddevFactorBlue; + Adjuster* pixelShiftNreadIso; + Adjuster* pixelShiftPrnu; + Adjuster* pixelShiftRedBlueWeight; +#endif + int oldMethod; public: BayerProcess (); @@ -54,9 +84,13 @@ public: void setDefaults (const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited = nullptr); void methodChanged (); - void adjusterChanged (Adjuster* a, double newval); - void dcbEnhanceChanged(); - //void allEnhanceChanged(); + void imageNumberChanged (); + void adjusterChanged (Adjuster* a, double newval); + void checkBoxToggled (CheckBox* c, CheckValue newval); + void pixelShiftMotionMethodChanged(); +#ifdef PIXELSHIFTDEV + void psMotionCorrectionChanged (); +#endif }; #endif diff --git a/rtgui/bayerrawexposure.cc b/rtgui/bayerrawexposure.cc index ee9b4fc4d..2194d1639 100644 --- a/rtgui/bayerrawexposure.cc +++ b/rtgui/bayerrawexposure.cc @@ -18,7 +18,6 @@ */ #include "bayerrawexposure.h" #include "guiutils.h" -#include using namespace rtengine; using namespace rtengine::procparams; @@ -57,9 +56,9 @@ BayerRAWExposure::BayerRAWExposure () : FoldableToolPanel(this, "bayerrawexposur } PexBlack0->show(); - PextwoGreen = Gtk::manage(new Gtk::CheckButton((M("TP_RAWEXPOS_TWOGREEN"))));// two green + PextwoGreen = Gtk::manage(new CheckBox(M("TP_RAWEXPOS_TWOGREEN"), multiImage));// two green PextwoGreen->set_active (true); - greenconn = PextwoGreen->signal_toggled().connect ( sigc::mem_fun(*this, &BayerRAWExposure::GreenChanged)); + PextwoGreen->setCheckBoxListener (this); pack_start( *PexBlack1, Gtk::PACK_SHRINK, 0);//black R pack_start( *PexBlack0, Gtk::PACK_SHRINK, 0);//black G1 @@ -79,16 +78,13 @@ void BayerRAWExposure::read(const rtengine::procparams::ProcParams* pp, const Pa PexBlack3->setEditedState( pedited->raw.bayersensor.exBlack3 ? Edited : UnEdited ); } - greenconn.block (true); - PextwoGreen->set_active (pp->raw.bayersensor.twogreen); - greenconn.block (false); - lastPextwoGreen = pp->raw.bayersensor.twogreen; + PextwoGreen->setValue (pp->raw.bayersensor.twogreen); PexBlack0->setValue (pp->raw.bayersensor.black0);//black PexBlack1->setValue (pp->raw.bayersensor.black1);//black PexBlack2->setValue (pp->raw.bayersensor.black2);//black - if(!PextwoGreen->get_active()) { + if(!PextwoGreen->getLastActive()) { PexBlack3->setValue (pp->raw.bayersensor.black3); } else { PexBlack3->setValue (PexBlack0->getValue()); @@ -102,9 +98,9 @@ void BayerRAWExposure::write( rtengine::procparams::ProcParams* pp, ParamsEdited pp->raw.bayersensor.black0 = PexBlack0->getValue();// black pp->raw.bayersensor.black1 = PexBlack1->getValue();// black pp->raw.bayersensor.black2 = PexBlack2->getValue();// black - pp->raw.bayersensor.twogreen = PextwoGreen->get_active(); + pp->raw.bayersensor.twogreen = PextwoGreen->getLastActive(); - if(PextwoGreen->get_active()) { + if(PextwoGreen->getLastActive()) { pp->raw.bayersensor.black3 = pp->raw.bayersensor.black0; // active or desactive 2 green together } else { pp->raw.bayersensor.black3 = PexBlack3->getValue(); @@ -126,7 +122,7 @@ void BayerRAWExposure::adjusterChanged (Adjuster* a, double newval) Glib::ustring value = a->getTextValue(); if (a == PexBlack0) { - if(!PextwoGreen->get_active()) { + if(!PextwoGreen->getLastActive()) { listener->panelChanged (EvPreProcessExpBlackzero, value ); } else { listener->panelChanged (EvPreProcessExpBlackzero, value ); @@ -137,7 +133,7 @@ void BayerRAWExposure::adjusterChanged (Adjuster* a, double newval) } else if (a == PexBlack2) { listener->panelChanged (EvPreProcessExpBlacktwo, value ); } else if (a == PexBlack3) { - if(!PextwoGreen->get_active()) { + if(!PextwoGreen->getLastActive()) { listener->panelChanged (EvPreProcessExpBlackthree, value ); } else { listener->panelChanged (EvPreProcessExpBlackthree, value ); @@ -146,33 +142,17 @@ void BayerRAWExposure::adjusterChanged (Adjuster* a, double newval) } } } -void BayerRAWExposure::GreenChanged() + +void BayerRAWExposure::checkBoxToggled (CheckBox* c, CheckValue newval) { - if (batchMode) { - if (PextwoGreen->get_inconsistent()) { - PextwoGreen->set_inconsistent (false); - greenconn.block (true); - PextwoGreen->set_active (false); - greenconn.block (false); - } else if (lastPextwoGreen) { - PextwoGreen->set_inconsistent (true); + if (c == PextwoGreen) { + if (listener) { + listener->panelChanged (EvPreProcessExptwoGreen, PextwoGreen->getLastActive() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + if (PextwoGreen->getLastActive()) { + PexBlack3->setValue (PexBlack0->getValue());//two green together + } } - - lastPextwoGreen = PextwoGreen->get_active (); } - - if (listener) { - if (PextwoGreen->get_active()) { - listener->panelChanged (EvPreProcessExptwoGreen, M("GENERAL_ENABLED")); - PexBlack3->setValue (PexBlack0->getValue());//two green together - } - - else { - listener->panelChanged (EvPreProcessExptwoGreen, M("GENERAL_DISABLED")); - } - - } - } void BayerRAWExposure::setBatchMode(bool batchMode) diff --git a/rtgui/bayerrawexposure.h b/rtgui/bayerrawexposure.h index 733aafc25..5d51babbb 100644 --- a/rtgui/bayerrawexposure.h +++ b/rtgui/bayerrawexposure.h @@ -21,10 +21,10 @@ #include #include "adjuster.h" +#include "checkbox.h" #include "toolpanel.h" -#include "../rtengine/rawimage.h" -class BayerRAWExposure : public ToolParamBlock, public AdjusterListener, public FoldableToolPanel +class BayerRAWExposure : public ToolParamBlock, public AdjusterListener, public CheckBoxListener, public FoldableToolPanel { protected: @@ -32,12 +32,8 @@ protected: Adjuster* PexBlack1; Adjuster* PexBlack2; Adjuster* PexBlack3; - bool lastPextwoGreen; - sigc::connection greenconn; - Gtk::CheckButton* PextwoGreen; + CheckBox* PextwoGreen; -private: -// Gtk::CheckButton* PextwoGreen; public: BayerRAWExposure (); @@ -46,8 +42,8 @@ public: void write (rtengine::procparams::ProcParams* pp, ParamsEdited* pedited = nullptr); void setBatchMode (bool batchMode); void setDefaults (const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited = nullptr); - void GreenChanged() ; void adjusterChanged (Adjuster* a, double newval); + void checkBoxToggled (CheckBox* c, CheckValue newval); void setAdjusterBehavior (bool pexblackadd); void trimValues (rtengine::procparams::ProcParams* pp); }; diff --git a/rtgui/checkbox.cc b/rtgui/checkbox.cc new file mode 100644 index 000000000..716802f0f --- /dev/null +++ b/rtgui/checkbox.cc @@ -0,0 +1,156 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2017 Jean-Christophe FRISCH + * + * RawTherapee is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RawTherapee is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RawTherapee. If not, see . + */ + +#include + +#include "multilangmgr.h" +#include "checkbox.h" +#include "guiutils.h" + +CheckBox::CheckBox (Glib::ustring label, bool const& multiImageVal) + : Gtk::CheckButton (label) + , listener (nullptr) + , lastActive (false) + , inBatchMode (false) + , multiImage (multiImageVal) +{ + conn = signal_toggled().connect( sigc::mem_fun(*this, &CheckBox::buttonToggled) ); +} + +void CheckBox::buttonToggled () +{ + + CheckValue newValue; + + if (multiImage) { + if (get_inconsistent()) { + set_inconsistent (false); + ConnectionBlocker bloker (conn); + set_active (false); + newValue = CheckValue::off; + } else if (getLastActive()) { + set_inconsistent (true); + newValue = CheckValue::unchanged; + } + } else { + newValue = get_active () ? CheckValue::on : CheckValue::off; + } + setLastActive(); + + if (listener) { + listener->checkBoxToggled(this, newValue); + } +} + +void CheckBox::setLastActive() +{ + lastActive = get_active(); +} + +// return the actual bool value, ignoring the inconsistent state +bool CheckBox::getLastActive () +{ + return lastActive; +} + +void CheckBox::setValue (CheckValue newValue) +{ + + ConnectionBlocker blocker (conn); + switch (newValue) { + case CheckValue::on: + set_inconsistent (false); + set_active(true); + lastActive = true; + break; + case CheckValue::off: + set_inconsistent (false); + set_active(true); + lastActive = false; + break; + case CheckValue::unchanged: + set_inconsistent (true); + break; + default: + break; + } +} + +void CheckBox::setValue (bool active) +{ + + ConnectionBlocker blocker (conn); + set_inconsistent (false); + set_active(active); + lastActive = active; +} + +CheckValue CheckBox::getValue () +{ + return (get_inconsistent() ? CheckValue::unchanged : get_active() ? CheckValue::on : CheckValue::off); +} + +Glib::ustring CheckBox::getValueAsStr () +{ + if (get_inconsistent()) { + return M("GENERAL_UNCHANGED"); + } else if (get_active ()) { + return M("GENERAL_ENABLED"); + } else { + return M("GENERAL_DISABLED"); + } +} + +/* +void CheckBox::set_sensitive (bool isSensitive) +{ + Gtk::CheckButton::set_sensitive(isSensitive); +} + +void CheckBox::set_tooltip_text (const Glib::ustring& tooltip) +{ + Gtk::CheckButton::set_tooltip_text (tooltip); +} + +void CheckBox::set_tooltip_markup (const Glib::ustring& tooltip) +{ + Gtk::CheckButton::set_tooltip_markup (tooltip); +} +*/ + +void CheckBox::setEdited (bool edited) +{ + + ConnectionBlocker blocker (conn); + set_inconsistent (!edited); + if (edited) { + set_active (lastActive); + } +} + +bool CheckBox::getEdited () +{ + + return !get_inconsistent (); +} + +void CheckBox::setCheckBoxListener (CheckBoxListener* cblistener) +{ + listener = cblistener; +} diff --git a/rtgui/checkbox.h b/rtgui/checkbox.h new file mode 100644 index 000000000..8745de6d3 --- /dev/null +++ b/rtgui/checkbox.h @@ -0,0 +1,78 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2017 Jean-Christophe FRISCH + * + * RawTherapee is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RawTherapee is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RawTherapee. If not, see . + */ +#ifndef _CHECKBOX_H_ +#define _CHECKBOX_H_ + +#include +#include "editedstate.h" +#include "guiutils.h" + +class CheckBox; + +enum class CheckValue { + on, + off, + unchanged +}; + +class CheckBoxListener +{ + +public: + virtual ~CheckBoxListener() {}; + virtual void checkBoxToggled (CheckBox* c, CheckValue newval) {} +}; + + +/** + * @brief subclass of Gtk::CheckButton for convenience + */ +class CheckBox : public Gtk::CheckButton // Should ideally be private, but in this case build fail on the instantiation +{ + + CheckBoxListener *listener; + bool lastActive; + bool inBatchMode; + bool const& multiImage; + sigc::connection conn; + void buttonToggled (); + void setLastActive(); + +public: + //using CheckButton::CheckButton; + explicit CheckBox (Glib::ustring label, bool const& multiImageVal); + bool getLastActive(); + void setValue (CheckValue newValue); + void setValue (bool active); + CheckValue getValue (); + void setEdited (bool edited); + bool getEdited (); + Glib::ustring getValueAsStr (); + + void setCheckBoxListener (CheckBoxListener* cblistener); + + /* Used if the Gtk::CheckButton parent class can be private + * + void set_sensitive (bool isSensitive = true); + void set_tooltip_text (const Glib::ustring& tooltip); + void set_tooltip_markup (const Glib::ustring& tooltip); + */ +}; + +#endif diff --git a/rtgui/crophandler.cc b/rtgui/crophandler.cc index 42c6eb756..10280f5c9 100644 --- a/rtgui/crophandler.cc +++ b/rtgui/crophandler.cc @@ -376,12 +376,12 @@ void CropHandler::setDetailedCrop (IImage8* im, IImage8* imtrue, rtengine::procp imh = ch->wh; } - Glib::RefPtr tmpPixbuf = Gdk::Pixbuf::create_from_data (ch->cropimg, Gdk::COLORSPACE_RGB, false, 8, ch->cropimg_width, 2 * ch->cropimg_height, 3 * ch->cropimg_width); + Glib::RefPtr tmpPixbuf = Gdk::Pixbuf::create_from_data (ch->cropimg, Gdk::COLORSPACE_RGB, false, 8, ch->cropimg_width, ch->cropimg_height, 3 * ch->cropimg_width); ch->cropPixbuf = Gdk::Pixbuf::create (Gdk::COLORSPACE_RGB, false, 8, imw, imh); tmpPixbuf->scale (ch->cropPixbuf, 0, 0, imw, imh, 0, 0, czoom / 1000.0, czoom / 1000.0, Gdk::INTERP_NEAREST); tmpPixbuf.clear (); - Glib::RefPtr tmpPixbuftrue = Gdk::Pixbuf::create_from_data (ch->cropimgtrue, Gdk::COLORSPACE_RGB, false, 8, ch->cropimg_width, 2 * ch->cropimg_height, 3 * ch->cropimg_width); + Glib::RefPtr tmpPixbuftrue = Gdk::Pixbuf::create_from_data (ch->cropimgtrue, Gdk::COLORSPACE_RGB, false, 8, ch->cropimg_width, ch->cropimg_height, 3 * ch->cropimg_width); ch->cropPixbuftrue = Gdk::Pixbuf::create (Gdk::COLORSPACE_RGB, false, 8, imw, imh); tmpPixbuftrue->scale (ch->cropPixbuftrue, 0, 0, imw, imh, 0, 0, czoom / 1000.0, czoom / 1000.0, Gdk::INTERP_NEAREST); tmpPixbuftrue.clear (); diff --git a/rtgui/cropwindow.cc b/rtgui/cropwindow.cc index 46c956436..d261da64c 100644 --- a/rtgui/cropwindow.cc +++ b/rtgui/cropwindow.cc @@ -61,9 +61,10 @@ ZoomStep zoomSteps[] = { {"500%", 5.0, 5000}, {"600%", 6.0, 6000}, {"700%", 7.0, 7000}, - {"800%", 8.0, 8000} + {"800%", 8.0, 8000}, + {"1600%", 16.0, 16000} }; -#define MAXZOOMSTEPS 20 +#define MAXZOOMSTEPS 21 #define ZOOM11INDEX 13 CropWindow::CropWindow (ImageArea* parent, bool isLowUpdatePriority_, bool isDetailWindow) @@ -1034,9 +1035,21 @@ void CropWindow::pointerMoved (int bstate, int x, int y) int imheight = cropHandler.cropPixbuf->get_height(); guint8* pix = cropHandler.cropPixbuftrue->get_pixels() + vy * cropHandler.cropPixbuf->get_rowstride() + vx * 3; + int rval = pix[0]; + int gval = pix[1]; + int bval = pix[2]; if (vx < imwidth && vy < imheight) { + rtengine::StagedImageProcessor* ipc = iarea->getImProcCoordinator(); + if(ipc) { + procparams::ProcParams params; + ipc->getParams(¶ms); + if(params.raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::none] || params.raw.xtranssensor.method == RAWParams::XTransSensor::methodstring[RAWParams::XTransSensor::none]) { + ImageSource *isrc = static_cast(ipc->getInitialImage()); + isrc->getRawValues(mx, my, params.coarse.rotate, rval, gval, bval); + } + } // pmlistener->pointerMoved (true, cropHandler.colorParams.working, mx, my, pix[0], pix[1], pix[2]); - pmlistener->pointerMoved (true, cropHandler.colorParams.output, cropHandler.colorParams.working, mx, my, pix[0], pix[1], pix[2]); + pmlistener->pointerMoved (true, cropHandler.colorParams.output, cropHandler.colorParams.working, mx, my, rval, gval, bval); if (pmhlistener) // pmhlistener->pointerMoved (true, cropHandler.colorParams.working, mx, my, pix[0], pix[1], pix[2]); diff --git a/rtgui/edit.cc b/rtgui/edit.cc index 764678634..476cd3602 100644 --- a/rtgui/edit.cc +++ b/rtgui/edit.cc @@ -18,7 +18,7 @@ */ #include "edit.h" -#include "rtimage.h" +#include "../rtengine/icons.h" ObjectMOBuffer::ObjectMOBuffer(EditDataProvider *dataProvider) : objectMap(nullptr), objectMode(OM_255), dataProvider(dataProvider) {} @@ -834,23 +834,23 @@ OPIcon::OPIcon(Glib::ustring normalImage, Glib::ustring activeImage, Glib::ustri Glib::ustring draggedImage, Glib::ustring insensitiveImage, DrivenPoint drivenPoint) : drivenPoint(drivenPoint) { if (!normalImage.empty()) { - normalImg = Cairo::ImageSurface::create_from_png( RTImage::findIconAbsolutePath(normalImage) ); + normalImg = Cairo::ImageSurface::create_from_png( rtengine::findIconAbsolutePath(normalImage) ); } if (!prelightImage.empty()) { - prelightImg = Cairo::ImageSurface::create_from_png( RTImage::findIconAbsolutePath(prelightImage) ); + prelightImg = Cairo::ImageSurface::create_from_png( rtengine::findIconAbsolutePath(prelightImage) ); } if (!activeImage.empty()) { - activeImg = Cairo::ImageSurface::create_from_png( RTImage::findIconAbsolutePath(activeImage) ); + activeImg = Cairo::ImageSurface::create_from_png( rtengine::findIconAbsolutePath(activeImage) ); } if (!draggedImage.empty()) { - draggedImg = Cairo::ImageSurface::create_from_png( RTImage::findIconAbsolutePath(draggedImage) ); + draggedImg = Cairo::ImageSurface::create_from_png( rtengine::findIconAbsolutePath(draggedImage) ); } if (!insensitiveImage.empty()) { - insensitiveImg = Cairo::ImageSurface::create_from_png( RTImage::findIconAbsolutePath(insensitiveImage) ); + insensitiveImg = Cairo::ImageSurface::create_from_png( rtengine::findIconAbsolutePath(insensitiveImage) ); } } diff --git a/rtgui/edit.h b/rtgui/edit.h index e28a6acec..eeeca694d 100644 --- a/rtgui/edit.h +++ b/rtgui/edit.h @@ -409,10 +409,8 @@ private: Cairo::RefPtr draggedImg; Cairo::RefPtr insensitiveImg; - static void setPaths(Options &opt); static void updateImages(); void changeImage(Glib::ustring &newImage); - static Glib::ustring findIconAbsolutePath(const Glib::ustring &iconFName); void drawImage (const Cairo::RefPtr &img, Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem); void drawMOImage (const Cairo::RefPtr &img, Cairo::RefPtr &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem); void drivenPointToRectangle(const rtengine::Coord &pos, rtengine::Coord &topLeft, rtengine::Coord &bottomRight, int W, int H); diff --git a/rtgui/editorpanel.cc b/rtgui/editorpanel.cc index 8da176fc3..7bc1631d3 100644 --- a/rtgui/editorpanel.cc +++ b/rtgui/editorpanel.cc @@ -96,12 +96,12 @@ private: profileBox.append (M ("PREFERENCES_PROFILE_NONE")); #ifdef WIN32 - profileBox.append (M ("MONITOR_PROFILE_SYSTEM") + " (" + rtengine::iccStore->getDefaultMonitorProfileName() + ")"); + profileBox.append (M ("MONITOR_PROFILE_SYSTEM") + " (" + rtengine::ICCStore::getInstance()->getDefaultMonitorProfileName() + ")"); profileBox.set_active (options.rtSettings.autoMonitorProfile ? 1 : 0); #else profileBox.set_active (0); #endif - const std::vector profiles = rtengine::iccStore->getProfiles (rtengine::ICCStore::ProfileType::MONITOR); + const std::vector profiles = rtengine::ICCStore::getInstance()->getProfiles (rtengine::ICCStore::ProfileType::MONITOR); for (const auto profile: profiles) { profileBox.append (profile); } @@ -176,7 +176,7 @@ private: #if !defined(__APPLE__) // monitor profile not supported on apple #ifdef WIN32 if (profileBox.get_active_row_number () == 1) { - profile = rtengine::iccStore->getDefaultMonitorProfileName (); + profile = rtengine::ICCStore::getInstance()->getDefaultMonitorProfileName (); if (profile.empty ()) { profile = options.rtSettings.monitorProfile; } @@ -206,7 +206,7 @@ private: profileBox.set_tooltip_text (""); } else { - const uint8_t supportedIntents = rtengine::iccStore->getProofIntents (profile); + const uint8_t supportedIntents = rtengine::ICCStore::getInstance()->getProofIntents (profile); const bool supportsRelativeColorimetric = supportedIntents & 1 << INTENT_RELATIVE_COLORIMETRIC; const bool supportsPerceptual = supportedIntents & 1 << INTENT_PERCEPTUAL; const bool supportsAbsoluteColorimetric = supportedIntents & 1 << INTENT_ABSOLUTE_COLORIMETRIC; diff --git a/rtgui/editwindow.cc b/rtgui/editwindow.cc index 355c22876..1d08ac783 100644 --- a/rtgui/editwindow.cc +++ b/rtgui/editwindow.cc @@ -23,6 +23,7 @@ #include #include "rtimage.h" #include "threadutils.h" +#include "../rtengine/icons.h" // Check if the system has more than one display and option is set bool EditWindow::isMultiDisplayEnabled() @@ -59,7 +60,7 @@ EditWindow::EditWindow (RTWindow* p) : parent(p) , isFullscreen(false) { Glib::ustring fName = "rt-logo-tiny.png"; - Glib::ustring fullPath = RTImage::findIconAbsolutePath(fName); + Glib::ustring fullPath = rtengine::findIconAbsolutePath(fName); try { set_default_icon_from_file (fullPath); diff --git a/rtgui/guiutils.cc b/rtgui/guiutils.cc index e8310ff0e..451af4e38 100644 --- a/rtgui/guiutils.cc +++ b/rtgui/guiutils.cc @@ -22,6 +22,7 @@ #include "options.h" #include "../rtengine/rt_math.h" #include "../rtengine/utils.h" +#include "../rtengine/icons.h" #include "rtimage.h" #include "multilangmgr.h" @@ -216,34 +217,6 @@ void thumbInterp (const unsigned char* src, int sw, int sh, unsigned char* dst, } } -Glib::ustring removeExtension (const Glib::ustring& filename) -{ - - Glib::ustring bname = Glib::path_get_basename(filename); - size_t lastdot = bname.find_last_of ('.'); - size_t lastwhitespace = bname.find_last_of (" \t\f\v\n\r"); - - if (lastdot != bname.npos && (lastwhitespace == bname.npos || lastdot > lastwhitespace)) { - return filename.substr (0, filename.size() - (bname.size() - lastdot)); - } else { - return filename; - } -} - -Glib::ustring getExtension (const Glib::ustring& filename) -{ - - Glib::ustring bname = Glib::path_get_basename(filename); - size_t lastdot = bname.find_last_of ('.'); - size_t lastwhitespace = bname.find_last_of (" \t\f\v\n\r"); - - if (lastdot != bname.npos && (lastwhitespace == bname.npos || lastdot > lastwhitespace)) { - return filename.substr (filename.size() - (bname.size() - lastdot) + 1, filename.npos); - } else { - return ""; - } -} - bool confirmOverwrite (Gtk::Window& parent, const std::string& filename) { bool safe = true; @@ -589,11 +562,11 @@ void ExpanderBox::hideBox() void MyExpander::init() { - inconsistentPBuf = Gdk::Pixbuf::create_from_file(RTImage::findIconAbsolutePath("expanderInconsistent.png")); - enabledPBuf = Gdk::Pixbuf::create_from_file(RTImage::findIconAbsolutePath("expanderEnabled.png")); - disabledPBuf = Gdk::Pixbuf::create_from_file(RTImage::findIconAbsolutePath("expanderDisabled.png")); - openedPBuf = Gdk::Pixbuf::create_from_file(RTImage::findIconAbsolutePath("expanderOpened.png")); - closedPBuf = Gdk::Pixbuf::create_from_file(RTImage::findIconAbsolutePath("expanderClosed.png")); + inconsistentPBuf = Gdk::Pixbuf::create_from_file(rtengine::findIconAbsolutePath("expanderInconsistent.png")); + enabledPBuf = Gdk::Pixbuf::create_from_file(rtengine::findIconAbsolutePath("expanderEnabled.png")); + disabledPBuf = Gdk::Pixbuf::create_from_file(rtengine::findIconAbsolutePath("expanderDisabled.png")); + openedPBuf = Gdk::Pixbuf::create_from_file(rtengine::findIconAbsolutePath("expanderOpened.png")); + closedPBuf = Gdk::Pixbuf::create_from_file(rtengine::findIconAbsolutePath("expanderClosed.png")); } MyExpander::MyExpander(bool useEnabled, Gtk::Widget* titleWidget) : diff --git a/rtgui/guiutils.h b/rtgui/guiutils.h index 12370a571..3f88adf0c 100644 --- a/rtgui/guiutils.h +++ b/rtgui/guiutils.h @@ -31,11 +31,13 @@ #include "rtimage.h" +// for convenience... +#include "pathutils.h" + + Glib::ustring escapeHtmlChars(const Glib::ustring &src); bool removeIfThere (Gtk::Container* cont, Gtk::Widget* w, bool increference = true); void thumbInterp (const unsigned char* src, int sw, int sh, unsigned char* dst, int dw, int dh); -Glib::ustring removeExtension (const Glib::ustring& filename); -Glib::ustring getExtension (const Glib::ustring& filename); bool confirmOverwrite (Gtk::Window& parent, const std::string& filename); void writeFailed (Gtk::Window& parent, const std::string& filename); void drawCrop (Cairo::RefPtr cr, int imx, int imy, int imw, int imh, int startx, int starty, double scale, const rtengine::procparams::CropParams& cparams, bool drawGuide = true, bool useBgColor = true, bool fullImageVisible = true); @@ -313,6 +315,7 @@ public: class MyComboBoxText : public Gtk::ComboBoxText { int naturalWidth, minimumWidth; + sigc::connection myConnection; bool on_scroll_event (GdkEventScroll* event); void get_preferred_width_vfunc (int &minimum_width, int &natural_width) const; @@ -322,6 +325,8 @@ public: MyComboBoxText (bool has_entry = false); void setPreferredWidth (int minimum_width, int natural_width); + void connect(const sigc::connection &connection) { myConnection = connection; } + void block(bool blocked) { myConnection.block(blocked); } }; /** diff --git a/rtgui/icmpanel.cc b/rtgui/icmpanel.cc index 5b4883dce..232d626a0 100644 --- a/rtgui/icmpanel.cc +++ b/rtgui/icmpanel.cc @@ -166,7 +166,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 wpnames = rtengine::getWorkingProfiles (); + std::vector wpnames = rtengine::ICCStore::getWorkingProfiles(); for (size_t i = 0; i < wpnames.size(); i++) { wnames->append (wpnames[i]); @@ -193,7 +193,7 @@ ICMPanel::ICMPanel () : FoldableToolPanel(this, "icm", M("TP_ICM_LABEL")), iunch onames->append (M("TP_ICM_NOICM")); onames->set_active (0); - std::vector opnames = iccStore->getProfiles (rtengine::ICCStore::ProfileType::OUTPUT); + std::vector opnames = ICCStore::getInstance()->getProfiles (rtengine::ICCStore::ProfileType::OUTPUT); for (size_t i = 0; i < opnames.size(); i++) { onames->append (opnames[i]); @@ -232,7 +232,7 @@ ICMPanel::ICMPanel () : FoldableToolPanel(this, "icm", M("TP_ICM_LABEL")), iunch oVBox->pack_start(*gaHBox, Gtk::PACK_EXPAND_WIDGET); - std::vector wpgamma = rtengine::getGamma (); + std::vector wpgamma = rtengine::ICCStore::getGamma(); for (size_t i = 0; i < wpgamma.size(); i++) { wgamma->append (wpgamma[i]); @@ -332,7 +332,7 @@ ICMPanel::ICMPanel () : FoldableToolPanel(this, "icm", M("TP_ICM_LABEL")), iunch } void ICMPanel::updateRenderingIntent (const Glib::ustring &profile) { - const uint8_t supportedIntents = rtengine::iccStore->getOutputIntents (profile); + const uint8_t supportedIntents = rtengine::ICCStore::getInstance()->getOutputIntents (profile); const bool supportsPerceptual = supportedIntents & 1 << INTENT_PERCEPTUAL; const bool supportsRelative = supportedIntents & 1 << INTENT_RELATIVE_COLORIMETRIC; const bool supportsSaturation = supportedIntents & 1 << INTENT_SATURATION; @@ -1033,7 +1033,7 @@ void ICMPanel::setRawMeta (bool raw, const rtengine::ImageData* pMeta) iembedded->set_active (!raw); icamera->set_sensitive (raw); camName = pMeta->getCamera(); - icameraICC->set_sensitive (raw && (iccStore->getStdProfile(pMeta->getCamera()) != nullptr || DCPStore::getInstance()->getStdProfile(pMeta->getCamera()) != nullptr)); + icameraICC->set_sensitive (raw && (ICCStore::getInstance()->getStdProfile(pMeta->getCamera()) != nullptr || DCPStore::getInstance()->getStdProfile(pMeta->getCamera()) != nullptr)); iembedded->set_sensitive (!raw); enableListener (); diff --git a/rtgui/main-cli.cc b/rtgui/main-cli.cc new file mode 100644 index 000000000..3a601e18e --- /dev/null +++ b/rtgui/main-cli.cc @@ -0,0 +1,781 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2004-2010 Gabor Horvath + * + * RawTherapee is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RawTherapee is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RawTherapee. If not, see . + */ + +#ifdef __GNUC__ +#if defined(__FAST_MATH__) +#error Using the -ffast-math CFLAG is known to lead to problems. Disable it to compile RawTherapee. +#endif +#endif + +#include "config.h" +#include +#include +#include +#include +#include "rtwindow.h" +#include +#include +#include +#include "options.h" +#include "../rtengine/icons.h" +#include "soundman.h" +#include "rtimage.h" +#include "version.h" +#include "extprog.h" + +#ifndef WIN32 +#include +#include +#include +#include +#else +#include +#include "conio.h" +#endif + +extern Options options; + +// stores path to data files +Glib::ustring argv0; +Glib::ustring creditsPath; +Glib::ustring licensePath; +Glib::ustring argv1; +bool simpleEditor; +//Glib::Threads::Thread* mainThread; + +namespace +{ + +// For an unknown reason, Glib::filename_to_utf8 doesn't work on reliably Windows, +// so we're using Glib::filename_to_utf8 for Linux/Apple and Glib::locale_to_utf8 for Windows. +Glib::ustring fname_to_utf8 (const char* fname) +{ +#ifdef WIN32 + + try { + return Glib::locale_to_utf8 (fname); + } catch (Glib::Error&) { + return Glib::convert_with_fallback (fname, "UTF-8", "ISO-8859-1", "?"); + } + +#else + + return Glib::filename_to_utf8 (fname); + +#endif +} + +bool fast_export = false; + +} + +/* Process line command options + * Returns + * 0 if process in batch has executed + * 1 to start GUI (with a dir or file option) + * 2 to start GUI because no files found + * -1 if there is an error in parameters + * -2 if an error occurred during processing + * -3 if at least one required procparam file was not found */ +int processLineParams( int argc, char **argv ); + +bool dontLoadCache( int argc, char **argv ); + +int main(int argc, char **argv) +{ + setlocale(LC_ALL, ""); + setlocale(LC_NUMERIC, "C"); // to set decimal point to "." + + Gio::init (); + + //mainThread = Glib::Threads::Thread::self(); + +#ifdef BUILD_BUNDLE + char exname[512] = {0}; + Glib::ustring exePath; + // get the path where the rawtherapee executable is stored +#ifdef WIN32 + WCHAR exnameU[512] = {0}; + GetModuleFileNameW (NULL, exnameU, 512); + WideCharToMultiByte(CP_UTF8, 0, exnameU, -1, exname, 512, 0, 0 ); +#else + + if (readlink("/proc/self/exe", exname, 512) < 0) { + strncpy(exname, argv[0], 512); + } + +#endif + exePath = Glib::path_get_dirname(exname); + + // set paths + if (Glib::path_is_absolute(DATA_SEARCH_PATH)) { + argv0 = DATA_SEARCH_PATH; + } else { + argv0 = Glib::build_filename(exePath, DATA_SEARCH_PATH); + } + + if (Glib::path_is_absolute(CREDITS_SEARCH_PATH)) { + creditsPath = CREDITS_SEARCH_PATH; + } else { + creditsPath = Glib::build_filename(exePath, CREDITS_SEARCH_PATH); + } + + if (Glib::path_is_absolute(LICENCE_SEARCH_PATH)) { + licensePath = LICENCE_SEARCH_PATH; + } else { + licensePath = Glib::build_filename(exePath, LICENCE_SEARCH_PATH); + } + +#else + argv0 = DATA_SEARCH_PATH; + creditsPath = CREDITS_SEARCH_PATH; + licensePath = LICENCE_SEARCH_PATH; +#endif + + bool quickstart = dontLoadCache(argc, argv); + + if (!Options::load (quickstart)) { + printf("Fatal error!\nThe RT_SETTINGS and/or RT_PATH environment variables are set, but use a relative path. The path must be absolute!\n"); + return -2; + } + + rtengine::setPaths(options); + + TIFFSetWarningHandler(nullptr); // avoid annoying message boxes + +#ifndef WIN32 + + // Move the old path to the new one if the new does not exist + if (Glib::file_test(Glib::build_filename(options.rtdir, "cache"), Glib::FILE_TEST_IS_DIR) && !Glib::file_test(options.cacheBaseDir, Glib::FILE_TEST_IS_DIR)) { + g_rename(Glib::build_filename (options.rtdir, "cache").c_str (), options.cacheBaseDir.c_str ()); + } + +#endif + +#ifdef WIN32 + bool consoleOpened = false; + + // suppression of annoying error boxes + SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); + + if (argc > 1 || options.rtSettings.verbose) { + if (options.rtSettings.verbose || ( !Glib::file_test (fname_to_utf8 (argv[1]), Glib::FILE_TEST_EXISTS ) && !Glib::file_test (fname_to_utf8 (argv[1]), Glib::FILE_TEST_IS_DIR))) { + bool stdoutRedirectedtoFile = (GetFileType(GetStdHandle(STD_OUTPUT_HANDLE)) == 0x0001); + bool stderrRedirectedtoFile = (GetFileType(GetStdHandle(STD_ERROR_HANDLE)) == 0x0001); + + // no console, if stdout and stderr both are redirected to file + if( !(stdoutRedirectedtoFile && stderrRedirectedtoFile)) { + // check if parameter -w was passed. + // We have to do that in this step, because it controls whether to open a console to show the output of following steps + bool Console = true; + + for(int i = 1; i < argc; i++) + if(!strcmp(argv[i], "-w")) { + Console = false; + break; + } + + if(Console) { + AllocConsole(); + AttachConsole( GetCurrentProcessId() ) ; + // Don't allow CTRL-C in console to terminate RT + SetConsoleCtrlHandler( NULL, true ); + // Set title of console + char consoletitle[128]; + sprintf(consoletitle, "RawTherapee %s Console", RTVERSION); + SetConsoleTitle(consoletitle); + // increase size of screen buffer + COORD c; + c.X = 200; + c.Y = 1000; + SetConsoleScreenBufferSize( GetStdHandle( STD_OUTPUT_HANDLE ), c ); + // Disable console-Cursor + CONSOLE_CURSOR_INFO cursorInfo; + cursorInfo.dwSize = 100; + cursorInfo.bVisible = false; + SetConsoleCursorInfo( GetStdHandle( STD_OUTPUT_HANDLE ), &cursorInfo ); + + if(!stdoutRedirectedtoFile) { + freopen( "CON", "w", stdout ) ; + } + + if(!stderrRedirectedtoFile) { + freopen( "CON", "w", stderr ) ; + } + + freopen( "CON", "r", stdin ) ; + + consoleOpened = true; + + // printing RT's version in every case, particularly useful for the 'verbose' mode, but also for the batch processing + std::cout << "RawTherapee, version " << RTVERSION << ", command line" << std::endl; + std::cout << "WARNING: closing this window will close RawTherapee!" << std::endl << std::endl; + } + } + } + } +#endif + + int ret = 0; + + // printing RT's version in all case, particularly useful for the 'verbose' mode, but also for the batch processing + std::cout << "RawTherapee, version " << RTVERSION << ", command line" << std::endl; + if (argc > 1) { + ret = processLineParams(argc, argv); + } + else { + std::cout << "Terminating without anything to do." << std::endl; + } + +#ifdef WIN32 + if(consoleOpened) { + printf("Press any key to exit RawTherapee\n"); + FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE)); + getch(); + } +#endif + + return ret; +} + +void deleteProcParams(std::vector &pparams) +{ + for (unsigned int i = 0; i < pparams.size(); i++) { + pparams[i]->deleteInstance(); + delete pparams[i]; + pparams[i] = NULL; + } + + return; +} + + +bool dontLoadCache( int argc, char **argv ) +{ + for( int iArg = 1; iArg < argc; iArg++) { + if( argv[iArg][0] == '-' && argv[iArg][1] == 'q' ) { + return true; + } + } + + return false; +} + +int processLineParams( int argc, char **argv ) +{ + rtengine::procparams::PartialProfile *rawParams = nullptr, *imgParams = nullptr; + std::vector inputFiles; + Glib::ustring outputPath = ""; + std::vector processingParams; + bool outputDirectory = false; + bool overwriteFiles = false; + bool sideProcParams = false; + bool copyParamsFile = false; + bool skipIfNoSidecar = false; + bool useDefault = false; + unsigned int sideCarFilePos = 0; + int compression = 92; + int subsampling = 3; + int bits = -1; + std::string outputType = ""; + unsigned errors = 0; + + for( int iArg = 1; iArg < argc; iArg++) { + if( argv[iArg][0] == '-' ) { + switch( argv[iArg][1] ) { + case 'O': + copyParamsFile = true; + + case 'o': // outputfile or dir + if( iArg + 1 < argc ) { + iArg++; + outputPath = fname_to_utf8 (argv[iArg]); + + if( Glib::file_test (outputPath, Glib::FILE_TEST_IS_DIR)) { + outputDirectory = true; + } + } + + break; + + case 'p': // processing parameters for all inputs; all set procparams are required, so + + // RT stop if any of them can't be loaded for any reason. + if( iArg + 1 < argc ) { + iArg++; + Glib::ustring fname = fname_to_utf8 (argv[iArg]); + + if (fname.at(0) == '-') { + std::cerr << "Error: filename missing next to the -p switch" << std::endl; + deleteProcParams(processingParams); + return -3; + } + + rtengine::procparams::PartialProfile* currentParams = new rtengine::procparams::PartialProfile(true); + + if (!(currentParams->load ( fname ))) { + processingParams.push_back(currentParams); + } else { + std::cerr << "Error: \"" << fname << "\" not found" << std::endl; + deleteProcParams(processingParams); + return -3; + } + } + + break; + + case 'S': + skipIfNoSidecar = true; + + case 's': // Processing params next to file (file extension appended) + sideProcParams = true; + sideCarFilePos = processingParams.size(); + break; + + case 'd': + useDefault = true; + break; + + case 'q': + break; + + case 'Y': + overwriteFiles = true; + break; + + case 'j': + if (strlen(argv[iArg]) > 2 && argv[iArg][2] == 's') { + if (strlen(argv[iArg]) == 3) { + std::cerr << "Error: the -js switch requires a mandatory value!" << std::endl; + deleteProcParams(processingParams); + return -3; + } + + // looking for the subsampling parameter + sscanf(&argv[iArg][3], "%d", &subsampling); + + if (subsampling < 1 || subsampling > 3) { + std::cerr << "Error: the value accompanying the -js switch has to be in the [1-3] range!" << std::endl; + deleteProcParams(processingParams); + return -3; + } + } else { + outputType = "jpg"; + sscanf(&argv[iArg][2], "%d", &compression); + + if (compression < 0 || compression > 100) { + std::cerr << "Error: the value accompanying the -j switch has to be in the [0-100] range!" << std::endl; + deleteProcParams(processingParams); + return -3; + } + } + + break; + + case 'b': + sscanf(&argv[iArg][2], "%d", &bits); + + if (bits != 8 && bits != 16) { + std::cerr << "Error: specify -b8 for 8-bit or -b16 for 16-bit output." << std::endl; + deleteProcParams(processingParams); + return -3; + } + + break; + + case 't': + outputType = "tif"; + compression = ((argv[iArg][2] != 'z') ? 0 : 1); + break; + + case 'n': + outputType = "png"; + compression = -1; + break; + + case 'f': + fast_export = true; + break; + + case 'c': // MUST be last option + while (iArg + 1 < argc) { + iArg++; + + const auto argument = fname_to_utf8 (argv[iArg]); + + if (Glib::file_test (argument, Glib::FILE_TEST_IS_REGULAR)) { + inputFiles.emplace_back (argument); + continue; + } + + if (Glib::file_test (argument, Glib::FILE_TEST_IS_DIR)) { + + auto dir = Gio::File::create_for_path (argument); + if (!dir || !dir->query_exists()) { + continue; + } + + try { + + auto enumerator = dir->enumerate_children (); + + while (auto file = enumerator->next_file ()) { + + const auto fileName = Glib::build_filename (argument, file->get_name ()); + + if (Glib::file_test (fileName, Glib::FILE_TEST_IS_DIR)) { + continue; + } + + // skip files without extension and sidecar files + auto lastdot = fileName.find_last_of('.'); + if (lastdot == Glib::ustring::npos) { + continue; + } + + if (fileName.substr (lastdot).compare (paramFileExtension) == 0) { + continue; + } + + inputFiles.emplace_back (fileName); + } + + } catch (Glib::Exception&) {} + + continue; + } + + std::cerr << "\"" << argument << "\" is neither a regular file nor a directory." << std::endl; + } + + break; +#ifdef WIN32 + + case 'w': // This case is handled outside this function + break; +#endif + + case 'h': + case '?': + default: { + Glib::ustring pparamsExt = paramFileExtension.substr(1); + std::cout << " An advanced, cross-platform program for developing raw photos." << std::endl; + std::cout << std::endl; + std::cout << " Website: http://www.rawtherapee.com/" << std::endl; + std::cout << " Documentation: http://rawpedia.rawtherapee.com/" << std::endl; + std::cout << " Forum: https://discuss.pixls.us/c/software/rawtherapee" << std::endl; + std::cout << " Code and bug reports: https://github.com/Beep6581/RawTherapee" << std::endl; + std::cout << std::endl; + std::cout << "Symbols:" << std::endl; + std::cout << " indicate parameters you can change." << std::endl; + std::cout << " [Square brackets] mean the parameter is optional." << std::endl; + std::cout << " The pipe symbol | indicates a choice of one or the other." << std::endl; + std::cout << " The dash symbol - denotes a range of possible values from one to the other." << std::endl; + std::cout << std::endl; + std::cout << "Usage:" << std::endl; + std::cout << " " << Glib::path_get_basename(argv[0]) << " -c | Convert files in batch with default parameters." << std::endl; + std::cout << " " << Glib::path_get_basename(argv[0]) << " -c | Convert files in batch with your own settings." << std::endl; + std::cout << std::endl; +#ifdef WIN32 + std::cout << " -w Do not open the Windows console" << std::endl; + std::cout << std::endl; +#endif + std::cout << "Options:" << std::endl; + std::cout << " " << Glib::path_get_basename(argv[0]) << " [-o |-O ] [-s|-S] [-p [-p ...] ] [-d] [ -j[1-100] [-js<1-3>] | [-b<8|16>] [-t[z] | [-n]] ] [-Y] [-f] -c " << std::endl; + std::cout << std::endl; + std::cout << " -q Quick Start mode : do not load cached files to speedup start time." << std::endl; + std::cout << " -c Specify one or more input files." << std::endl; + std::cout << " -c must be the last option." << std::endl; + std::cout << " -o | Set output file or folder." << std::endl; + std::cout << " Saves output file alongside input file if -o is not specified." << std::endl; + std::cout << " -O | Set output file or folder and copy " << pparamsExt << " file into it." << std::endl; + std::cout << " Saves output file alongside input file if -O is not specified." << std::endl; + std::cout << " -s Use the existing sidecar file to build the processing parameters," << std::endl; + std::cout << " e.g. for photo.raw there should be a photo.raw." << pparamsExt << " file in the same folder." << std::endl; + std::cout << " If the sidecar file does not exist, neutral values will be used." << std::endl; + std::cout << " -S Like -s but skip if the sidecar file does not exist." << std::endl; + std::cout << " -p Specify processing profile to be used for all conversions." << std::endl; + std::cout << " You can specify as many sets of \"-p \" options as you like," << std::endl; + std::cout << " each will be built on top of the previous one, as explained below." << std::endl; + std::cout << " -d Use the default raw or non-raw processing profile as set in" << std::endl; + std::cout << " Preferences > Image Processing > Default Processing Profile" << std::endl; + std::cout << " -j[1-100] Specify output to be JPEG (default, if -t and -n are not set)." << std::endl; + std::cout << " Optionally, specify compression 1-100 (default value: 92)." << std::endl; + std::cout << " -js<1-3> Specify the JPEG chroma subsampling parameter, where:" << std::endl; + std::cout << " 1 = Best compression: 2x2, 1x1, 1x1 (4:2:0)" << std::endl; + std::cout << " Chroma halved vertically and horizontally." << std::endl; + std::cout << " 2 = Balanced (default): 2x1, 1x1, 1x1 (4:2:2)" << std::endl; + std::cout << " Chroma halved horizontally." << std::endl; + std::cout << " 3 = Best quality: 1x1, 1x1, 1x1 (4:4:4)" << std::endl; + std::cout << " No chroma subsampling." << std::endl; + std::cout << " -b<8|16> Specify bit depth per channel (default value: 16 for TIFF, 8 for PNG)." << std::endl; + std::cout << " Only applies to TIFF and PNG output, JPEG is always 8." << std::endl; + std::cout << " -t[z] Specify output to be TIFF." << std::endl; + std::cout << " Uncompressed by default, or deflate compression with 'z'." << std::endl; + std::cout << " -n Specify output to be compressed PNG." << std::endl; + std::cout << " Compression is hard-coded to 6." << std::endl; + std::cout << " -Y Overwrite output if present." << std::endl; + std::cout << " -f Use the custom fast-export processing pipeline." << std::endl; + std::cout << std::endl; + std::cout << "Your " << pparamsExt << " files can be incomplete, RawTherapee will build the final values as follows:" << std::endl; + std::cout << " 1- A new processing profile is created using neutral values," << std::endl; + std::cout << " 2- If the \"-d\" option is set, the values are overridden by those found in" << std::endl; + std::cout << " the default raw or non-raw processing profile." << std::endl; + std::cout << " 3- If one or more \"-p\" options are set, the values are overridden by those" << std::endl; + std::cout << " found in these processing profiles." << std::endl; + std::cout << " 4- If the \"-s\" or \"-S\" options are set, the values are finally overridden by those" << std::endl; + std::cout << " found in the sidecar files." << std::endl; + std::cout << " The processing profiles are processed in the order specified on the command line." << std::endl; + return -1; + } + } + } else { + argv1 = fname_to_utf8 (argv[iArg]); + + if( outputDirectory ) { + options.savePathFolder = outputPath; + options.saveUsePathTemplate = false; + } else { + options.saveUsePathTemplate = true; + + if (options.savePathTemplate.empty()) + // If the save path template is empty, we use its default value + { + options.savePathTemplate = "%p1/converted/%f"; + } + } + + if (outputType == "jpg") { + options.saveFormat.format = outputType; + options.saveFormat.jpegQuality = compression; + options.saveFormat.jpegSubSamp = subsampling; + } else if (outputType == "tif") { + options.saveFormat.format = outputType; + } else if (outputType == "png") { + options.saveFormat.format = outputType; + } + + break; + } + } + + if( !argv1.empty() ) { + return 1; + } + + if( inputFiles.empty() ) { + return 2; + } + + if (useDefault) { + rawParams = new rtengine::procparams::PartialProfile(true, true); + Glib::ustring profPath = options.findProfilePath(options.defProfRaw); + + if (options.is_defProfRawMissing() || profPath.empty() || (profPath != DEFPROFILE_DYNAMIC && rawParams->load(profPath == DEFPROFILE_INTERNAL ? DEFPROFILE_INTERNAL : Glib::build_filename(profPath, options.defProfRaw.substr(5) + paramFileExtension)))) { + std::cerr << "Error: default raw processing profile not found" << std::endl; + rawParams->deleteInstance(); + delete rawParams; + deleteProcParams(processingParams); + return -3; + } + + imgParams = new rtengine::procparams::PartialProfile(true); + profPath = options.findProfilePath(options.defProfImg); + + if (options.is_defProfImgMissing() || profPath.empty() || (profPath != DEFPROFILE_DYNAMIC && imgParams->load(profPath == DEFPROFILE_INTERNAL ? DEFPROFILE_INTERNAL : Glib::build_filename(profPath, options.defProfImg.substr(5) + paramFileExtension)))) { + std::cerr << "Error: default non-raw processing profile not found" << std::endl; + imgParams->deleteInstance(); + delete imgParams; + rawParams->deleteInstance(); + delete rawParams; + deleteProcParams(processingParams); + return -3; + } + } + + for( size_t iFile = 0; iFile < inputFiles.size(); iFile++) { + + // Has to be reinstanciated at each profile to have a ProcParams object with default values + rtengine::procparams::ProcParams currentParams; + + Glib::ustring inputFile = inputFiles[iFile]; + std::cout << "Processing: " << inputFile << std::endl; + + rtengine::InitialImage* ii = nullptr; + rtengine::ProcessingJob* job = nullptr; + int errorCode; + bool isRaw = false; + + Glib::ustring outputFile; + + if( outputType.empty() ) { + outputType = "jpg"; + } + + if( outputPath.empty() ) { + Glib::ustring s = inputFile; + Glib::ustring::size_type ext = s.find_last_of('.'); + outputFile = s.substr(0, ext) + "." + outputType; + } else if( outputDirectory ) { + Glib::ustring s = Glib::path_get_basename( inputFile ); + Glib::ustring::size_type ext = s.find_last_of('.'); + outputFile = outputPath + "/" + s.substr(0, ext) + "." + outputType; + } else { + Glib::ustring s = outputPath; + Glib::ustring::size_type ext = s.find_last_of('.'); + outputFile = s.substr(0, ext) + "." + outputType; + } + + if( inputFile == outputFile) { + std::cerr << "Cannot overwrite: " << inputFile << std::endl; + continue; + } + + if( !overwriteFiles && Glib::file_test( outputFile , Glib::FILE_TEST_EXISTS ) ) { + std::cerr << outputFile << " already exists: use -Y option to overwrite. This image has been skipped." << std::endl; + continue; + } + + // Load the image + isRaw = true; + Glib::ustring ext = getExtension (inputFile); + + if (ext.lowercase() == "jpg" || ext.lowercase() == "jpeg" || ext.lowercase() == "tif" || ext.lowercase() == "tiff" || ext.lowercase() == "png") { + isRaw = false; + } + + ii = rtengine::InitialImage::load ( inputFile, isRaw, &errorCode, nullptr ); + + if (!ii) { + errors++; + std::cerr << "Error loading file: " << inputFile << std::endl; + continue; + } + + if (useDefault) { + if (isRaw) { + if (options.defProfRaw == DEFPROFILE_DYNAMIC) { + rawParams->deleteInstance(); + delete rawParams; + rawParams = loadDynamicProfile(ii->getMetaData()); + } + std::cout << " Merging default raw processing profile" << std::endl; + rawParams->applyTo(¤tParams); + } else { + if (options.defProfImg == DEFPROFILE_DYNAMIC) { + imgParams->deleteInstance(); + delete imgParams; + imgParams = loadDynamicProfile(ii->getMetaData()); + } + std::cout << " Merging default non-raw processing profile" << std::endl; + imgParams->applyTo(¤tParams); + } + } + + bool sideCarFound = false; + unsigned int i = 0; + + // Iterate the procparams file list in order to build the final ProcParams + do { + if (sideProcParams && i == sideCarFilePos) { + // using the sidecar file + Glib::ustring sideProcessingParams = inputFile + paramFileExtension; + + // the "load" method don't reset the procparams values anymore, so values found in the procparam file override the one of currentParams + if( !Glib::file_test( sideProcessingParams, Glib::FILE_TEST_EXISTS ) || currentParams.load ( sideProcessingParams )) { + std::cerr << "Warning: sidecar file requested but not found for: " << sideProcessingParams << std::endl; + } else { + sideCarFound = true; + std::cout << " Merging sidecar procparams" << std::endl; + } + } + + if( processingParams.size() > i ) { + std::cout << " Merging procparams #" << i << std::endl; + processingParams[i]->applyTo(¤tParams); + } + + i++; + } while (i < processingParams.size() + (sideProcParams ? 1 : 0)); + + if( sideProcParams && !sideCarFound && skipIfNoSidecar ) { + delete ii; + errors++; + std::cerr << "Error: no sidecar procparams found for: " << inputFile << std::endl; + continue; + } + + job = rtengine::ProcessingJob::create (ii, currentParams, fast_export); + + if( !job ) { + errors++; + std::cerr << "Error creating processing for: " << inputFile << std::endl; + ii->decreaseRef(); + continue; + } + + // Process image + rtengine::IImage16* resultImage = rtengine::processImage (job, errorCode, nullptr, options.tunnelMetaData); + + if( !resultImage ) { + errors++; + std::cerr << "Error processing: " << inputFile << std::endl; + rtengine::ProcessingJob::destroy( job ); + continue; + } + + // save image to disk + if( outputType == "jpg" ) { + errorCode = resultImage->saveAsJPEG( outputFile, compression, subsampling ); + } else if( outputType == "tif" ) { + errorCode = resultImage->saveAsTIFF( outputFile, bits, compression == 0 ); + } else if( outputType == "png" ) { + errorCode = resultImage->saveAsPNG( outputFile, compression, bits ); + } else { + errorCode = resultImage->saveToFile (outputFile); + } + + if(errorCode) { + errors++; + std::cerr << "Error saving to: " << outputFile << std::endl; + } else { + if( copyParamsFile ) { + Glib::ustring outputProcessingParams = outputFile + paramFileExtension; + currentParams.save( outputProcessingParams ); + } + } + + ii->decreaseRef(); + resultImage->free(); + } + + if (imgParams) { + imgParams->deleteInstance(); + delete imgParams; + } + + if (rawParams) { + rawParams->deleteInstance(); + delete rawParams; + } + + deleteProcParams(processingParams); + + return errors > 0 ? -2 : 0; +} diff --git a/rtgui/main.cc b/rtgui/main.cc index 7e11825a2..155d9f3ba 100644 --- a/rtgui/main.cc +++ b/rtgui/main.cc @@ -28,6 +28,7 @@ #include #include #include +#include "../rtengine/icons.h" #include "rtwindow.h" #include #include @@ -83,8 +84,6 @@ Glib::ustring fname_to_utf8 (const char* fname) #endif } -bool fast_export = false; - } // This recursive mutex will be used by gdk_threads_enter/leave instead of a simple mutex @@ -178,6 +177,7 @@ int main(int argc, char **argv) return -2; } + profileStore.init (); extProgStore->init(); SoundManager::init(); @@ -204,8 +204,7 @@ int main(int argc, char **argv) break; } - if(Console) { - AllocConsole(); + if(Console && AllocConsole()) { AttachConsole( GetCurrentProcessId() ) ; // Don't allow CTRL-C in console to terminate RT SetConsoleCtrlHandler( NULL, true ); @@ -243,16 +242,18 @@ int main(int argc, char **argv) } } - int ret = processLineParams( argc, argv); + if(argc > 1) { + int ret = processLineParams( argc, argv); - if( ret <= 0 ) { - if(consoleOpened) { - printf("Press any key to exit RawTherapee\n"); - FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE)); - getch(); + if( ret <= 0 ) { + if(consoleOpened) { + printf("Press any key to exit RawTherapee\n"); + FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE)); + getch(); + } + + return ret; } - - return ret; } } @@ -302,8 +303,8 @@ int main(int argc, char **argv) Glib::RefPtr defaultIconTheme = Gtk::IconTheme::get_default(); defaultIconTheme->append_search_path(icon_path); - RTImage::setPaths(options); - MyExpander::init(); // has to stay AFTER RTImage::setPaths + rtengine::setPaths(options); + MyExpander::init(); // has to stay AFTER rtengine::setPaths // ------- loading theme files @@ -412,189 +413,11 @@ void deleteProcParams(std::vector &pparam int processLineParams( int argc, char **argv ) { - rtengine::procparams::PartialProfile *rawParams = nullptr, *imgParams = nullptr; - std::vector inputFiles; - Glib::ustring outputPath = ""; - std::vector processingParams; - bool outputDirectory = false; - bool overwriteFiles = false; - bool sideProcParams = false; - bool copyParamsFile = false; - bool skipIfNoSidecar = false; - bool useDefault = false; - unsigned int sideCarFilePos = 0; - int compression = 92; - int subsampling = 3; - int bits = -1; - std::string outputType = ""; unsigned errors = 0; for( int iArg = 1; iArg < argc; iArg++) { if( argv[iArg][0] == '-' ) { switch( argv[iArg][1] ) { - case 'O': - copyParamsFile = true; - - case 'o': // outputfile or dir - if( iArg + 1 < argc ) { - iArg++; - outputPath = fname_to_utf8 (argv[iArg]); - - if( Glib::file_test (outputPath, Glib::FILE_TEST_IS_DIR)) { - outputDirectory = true; - } - } - - break; - - case 'p': // processing parameters for all inputs; all set procparams are required, so - - // RT stop if any of them can't be loaded for any reason. - if( iArg + 1 < argc ) { - iArg++; - Glib::ustring fname = fname_to_utf8 (argv[iArg]); - - if (fname.at(0) == '-') { - std::cerr << "Error: filename missing next to the -p switch" << std::endl; - deleteProcParams(processingParams); - return -3; - } - - rtengine::procparams::PartialProfile* currentParams = new rtengine::procparams::PartialProfile(true); - - if (!(currentParams->load ( fname ))) { - processingParams.push_back(currentParams); - } else { - std::cerr << "Error: \"" << fname << "\" not found" << std::endl; - deleteProcParams(processingParams); - return -3; - } - } - - break; - - case 'S': - skipIfNoSidecar = true; - - case 's': // Processing params next to file (file extension appended) - sideProcParams = true; - sideCarFilePos = processingParams.size(); - break; - - case 'd': - useDefault = true; - break; - - case 'Y': - overwriteFiles = true; - break; - - case 'j': - if (strlen(argv[iArg]) > 2 && argv[iArg][2] == 's') { - if (strlen(argv[iArg]) == 3) { - std::cerr << "Error: the -js switch requires a mandatory value!" << std::endl; - deleteProcParams(processingParams); - return -3; - } - - // looking for the subsampling parameter - sscanf(&argv[iArg][3], "%d", &subsampling); - - if (subsampling < 1 || subsampling > 3) { - std::cerr << "Error: the value accompanying the -js switch has to be in the [1-3] range!" << std::endl; - deleteProcParams(processingParams); - return -3; - } - } else { - outputType = "jpg"; - sscanf(&argv[iArg][2], "%d", &compression); - - if (compression < 0 || compression > 100) { - std::cerr << "Error: the value accompanying the -j switch has to be in the [0-100] range!" << std::endl; - deleteProcParams(processingParams); - return -3; - } - } - - break; - - case 'b': - sscanf(&argv[iArg][2], "%d", &bits); - - if (bits != 8 && bits != 16) { - std::cerr << "Error: specify -b8 for 8-bit or -b16 for 16-bit output." << std::endl; - deleteProcParams(processingParams); - return -3; - } - - break; - - case 't': - outputType = "tif"; - compression = ((argv[iArg][2] != 'z') ? 0 : 1); - break; - - case 'n': - outputType = "png"; - compression = -1; - break; - - case 'f': - fast_export = true; - break; - - case 'c': // MUST be last option - while (iArg + 1 < argc) { - iArg++; - - const auto argument = fname_to_utf8 (argv[iArg]); - - if (Glib::file_test (argument, Glib::FILE_TEST_IS_REGULAR)) { - inputFiles.emplace_back (argument); - continue; - } - - if (Glib::file_test (argument, Glib::FILE_TEST_IS_DIR)) { - - auto dir = Gio::File::create_for_path (argument); - if (!dir || !dir->query_exists()) { - continue; - } - - try { - - auto enumerator = dir->enumerate_children (); - - while (auto file = enumerator->next_file ()) { - - const auto fileName = Glib::build_filename (argument, file->get_name ()); - - if (Glib::file_test (fileName, Glib::FILE_TEST_IS_DIR)) { - continue; - } - - // skip files without extension and sidecar files - auto lastdot = fileName.find_last_of('.'); - if (lastdot == Glib::ustring::npos) { - continue; - } - - if (fileName.substr (lastdot).compare (paramFileExtension) == 0) { - continue; - } - - inputFiles.emplace_back (fileName); - } - - } catch (Glib::Exception&) {} - - continue; - } - - std::cerr << "\"" << argument << "\" is neither a regular file nor a directory." << std::endl; - } - - break; #ifdef WIN32 case 'w': // This case is handled outside this function @@ -621,86 +444,17 @@ int processLineParams( int argc, char **argv ) std::cout << "Usage:" << std::endl; std::cout << " " << Glib::path_get_basename(argv[0]) << " Start File Browser inside folder." << std::endl; std::cout << " " << Glib::path_get_basename(argv[0]) << " Start Image Editor with file." << std::endl; - std::cout << " " << Glib::path_get_basename(argv[0]) << " -c | Convert files in batch with default parameters." << std::endl; - std::cout << " " << Glib::path_get_basename(argv[0]) << " -c | Convert files in batch with your own settings." << std::endl; std::cout << std::endl; + std::cout << "Options:" << std::endl; #ifdef WIN32 std::cout << " -w Do not open the Windows console" << std::endl; - std::cout << std::endl; #endif - std::cout << "Options:" << std::endl; - std::cout << " " << Glib::path_get_basename(argv[0]) << " [-o |-O ] [-s|-S] [-p [-p ...] ] [-d] [ -j[1-100] [-js<1-3>] | [-b<8|16>] [-t[z] | [-n]] ] [-Y] -c " << std::endl; - std::cout << std::endl; - std::cout << " -c Specify one or more input files." << std::endl; - std::cout << " -c must be the last option." << std::endl; - std::cout << " -o | Set output file or folder." << std::endl; - std::cout << " Saves output file alongside input file if -o is not specified." << std::endl; - std::cout << " -O | Set output file or folder and copy " << pparamsExt << " file into it." << std::endl; - std::cout << " Saves output file alongside input file if -O is not specified." << std::endl; - std::cout << " -s Use the existing sidecar file to build the processing parameters," << std::endl; - std::cout << " e.g. for photo.raw there should be a photo.raw." << pparamsExt << " file in the same folder." << std::endl; - std::cout << " If the sidecar file does not exist, neutral values will be used." << std::endl; - std::cout << " -S Like -s but skip if the sidecar file does not exist." << std::endl; - std::cout << " -p Specify processing profile to be used for all conversions." << std::endl; - std::cout << " You can specify as many sets of \"-p \" options as you like," << std::endl; - std::cout << " each will be built on top of the previous one, as explained below." << std::endl; - std::cout << " -d Use the default raw or non-raw processing profile as set in" << std::endl; - std::cout << " Preferences > Image Processing > Default Processing Profile" << std::endl; - std::cout << " -j[1-100] Specify output to be JPEG (default, if -t and -n are not set)." << std::endl; - std::cout << " Optionally, specify compression 1-100 (default value: 92)." << std::endl; - std::cout << " -js<1-3> Specify the JPEG chroma subsampling parameter, where:" << std::endl; - std::cout << " 1 = Best compression: 2x2, 1x1, 1x1 (4:2:0)" << std::endl; - std::cout << " Chroma halved vertically and horizontally." << std::endl; - std::cout << " 2 = Balanced (default): 2x1, 1x1, 1x1 (4:2:2)" << std::endl; - std::cout << " Chroma halved horizontally." << std::endl; - std::cout << " 3 = Best quality: 1x1, 1x1, 1x1 (4:4:4)" << std::endl; - std::cout << " No chroma subsampling." << std::endl; - std::cout << " -b<8|16> Specify bit depth per channel (default value: 16 for TIFF, 8 for PNG)." << std::endl; - std::cout << " Only applies to TIFF and PNG output, JPEG is always 8." << std::endl; - std::cout << " -t[z] Specify output to be TIFF." << std::endl; - std::cout << " Uncompressed by default, or deflate compression with 'z'." << std::endl; - std::cout << " -n Specify output to be compressed PNG." << std::endl; - std::cout << " Compression is hard-coded to 6." << std::endl; - std::cout << " -Y Overwrite output if present." << std::endl; - std::cout << std::endl; - std::cout << "Your " << pparamsExt << " files can be incomplete, RawTherapee will build the final values as follows:" << std::endl; - std::cout << " 1- A new processing profile is created using neutral values," << std::endl; - std::cout << " 2- If the \"-d\" option is set, the values are overridden by those found in" << std::endl; - std::cout << " the default raw or non-raw processing profile." << std::endl; - std::cout << " 3- If one or more \"-p\" options are set, the values are overridden by those" << std::endl; - std::cout << " found in these processing profiles." << std::endl; - std::cout << " 4- If the \"-s\" or \"-S\" options are set, the values are finally overridden by those" << std::endl; - std::cout << " found in the sidecar files." << std::endl; - std::cout << " The processing profiles are processed in the order specified on the command line." << std::endl; + std::cout << " -h -? Display this help message" << std::endl; return -1; } } } else { argv1 = fname_to_utf8 (argv[iArg]); - - if( outputDirectory ) { - options.savePathFolder = outputPath; - options.saveUsePathTemplate = false; - } else { - options.saveUsePathTemplate = true; - - if (options.savePathTemplate.empty()) - // If the save path template is empty, we use its default value - { - options.savePathTemplate = "%p1/converted/%f"; - } - } - - if (outputType == "jpg") { - options.saveFormat.format = outputType; - options.saveFormat.jpegQuality = compression; - options.saveFormat.jpegSubSamp = subsampling; - } else if (outputType == "tif") { - options.saveFormat.format = outputType; - } else if (outputType == "png") { - options.saveFormat.format = outputType; - } - break; } } @@ -709,203 +463,5 @@ int processLineParams( int argc, char **argv ) return 1; } - if( inputFiles.empty() ) { - return 2; - } - - if (useDefault) { - rawParams = new rtengine::procparams::PartialProfile(true, true); - Glib::ustring profPath = options.findProfilePath(options.defProfRaw); - - if (options.is_defProfRawMissing() || profPath.empty() || (profPath != DEFPROFILE_DYNAMIC && rawParams->load(profPath == DEFPROFILE_INTERNAL ? DEFPROFILE_INTERNAL : Glib::build_filename(profPath, options.defProfRaw.substr(5) + paramFileExtension)))) { - std::cerr << "Error: default raw processing profile not found" << std::endl; - rawParams->deleteInstance(); - delete rawParams; - deleteProcParams(processingParams); - return -3; - } - - imgParams = new rtengine::procparams::PartialProfile(true); - profPath = options.findProfilePath(options.defProfImg); - - if (options.is_defProfImgMissing() || profPath.empty() || (profPath != DEFPROFILE_DYNAMIC && imgParams->load(profPath == DEFPROFILE_INTERNAL ? DEFPROFILE_INTERNAL : Glib::build_filename(profPath, options.defProfImg.substr(5) + paramFileExtension)))) { - std::cerr << "Error: default non-raw processing profile not found" << std::endl; - imgParams->deleteInstance(); - delete imgParams; - rawParams->deleteInstance(); - delete rawParams; - deleteProcParams(processingParams); - return -3; - } - } - - for( size_t iFile = 0; iFile < inputFiles.size(); iFile++) { - - // Has to be reinstanciated at each profile to have a ProcParams object with default values - rtengine::procparams::ProcParams currentParams; - - Glib::ustring inputFile = inputFiles[iFile]; - std::cout << "Processing: " << inputFile << std::endl; - - rtengine::InitialImage* ii = nullptr; - rtengine::ProcessingJob* job = nullptr; - int errorCode; - bool isRaw = false; - - Glib::ustring outputFile; - - if( outputType.empty() ) { - outputType = "jpg"; - } - - if( outputPath.empty() ) { - Glib::ustring s = inputFile; - Glib::ustring::size_type ext = s.find_last_of('.'); - outputFile = s.substr(0, ext) + "." + outputType; - } else if( outputDirectory ) { - Glib::ustring s = Glib::path_get_basename( inputFile ); - Glib::ustring::size_type ext = s.find_last_of('.'); - outputFile = outputPath + "/" + s.substr(0, ext) + "." + outputType; - } else { - Glib::ustring s = outputPath; - Glib::ustring::size_type ext = s.find_last_of('.'); - outputFile = s.substr(0, ext) + "." + outputType; - } - - if( inputFile == outputFile) { - std::cerr << "Cannot overwrite: " << inputFile << std::endl; - continue; - } - - if( !overwriteFiles && Glib::file_test( outputFile , Glib::FILE_TEST_EXISTS ) ) { - std::cerr << outputFile << " already exists: use -Y option to overwrite. This image has been skipped." << std::endl; - continue; - } - - // Load the image - isRaw = true; - Glib::ustring ext = getExtension (inputFile); - - if (ext.lowercase() == "jpg" || ext.lowercase() == "jpeg" || ext.lowercase() == "tif" || ext.lowercase() == "tiff" || ext.lowercase() == "png") { - isRaw = false; - } - - ii = rtengine::InitialImage::load ( inputFile, isRaw, &errorCode, nullptr ); - - if (!ii) { - errors++; - std::cerr << "Error loading file: " << inputFile << std::endl; - continue; - } - - if (useDefault) { - if (isRaw) { - if (options.defProfRaw == DEFPROFILE_DYNAMIC) { - rawParams->deleteInstance(); - delete rawParams; - rawParams = loadDynamicProfile(ii->getMetaData()); - } - std::cout << " Merging default raw processing profile" << std::endl; - rawParams->applyTo(¤tParams); - } else { - if (options.defProfImg == DEFPROFILE_DYNAMIC) { - imgParams->deleteInstance(); - delete imgParams; - imgParams = loadDynamicProfile(ii->getMetaData()); - } - std::cout << " Merging default non-raw processing profile" << std::endl; - imgParams->applyTo(¤tParams); - } - } - - bool sideCarFound = false; - unsigned int i = 0; - - // Iterate the procparams file list in order to build the final ProcParams - do { - if (sideProcParams && i == sideCarFilePos) { - // using the sidecar file - Glib::ustring sideProcessingParams = inputFile + paramFileExtension; - - // the "load" method don't reset the procparams values anymore, so values found in the procparam file override the one of currentParams - if( !Glib::file_test( sideProcessingParams, Glib::FILE_TEST_EXISTS ) || currentParams.load ( sideProcessingParams )) { - std::cerr << "Warning: sidecar file requested but not found for: " << sideProcessingParams << std::endl; - } else { - sideCarFound = true; - std::cout << " Merging sidecar procparams" << std::endl; - } - } - - if( processingParams.size() > i ) { - std::cout << " Merging procparams #" << i << std::endl; - processingParams[i]->applyTo(¤tParams); - } - - i++; - } while (i < processingParams.size() + (sideProcParams ? 1 : 0)); - - if( sideProcParams && !sideCarFound && skipIfNoSidecar ) { - delete ii; - errors++; - std::cerr << "Error: no sidecar procparams found for: " << inputFile << std::endl; - continue; - } - - job = rtengine::ProcessingJob::create (ii, currentParams, fast_export); - - if( !job ) { - errors++; - std::cerr << "Error creating processing for: " << inputFile << std::endl; - ii->decreaseRef(); - continue; - } - - // Process image - rtengine::IImage16* resultImage = rtengine::processImage (job, errorCode, nullptr, options.tunnelMetaData); - - if( !resultImage ) { - errors++; - std::cerr << "Error processing: " << inputFile << std::endl; - rtengine::ProcessingJob::destroy( job ); - continue; - } - - // save image to disk - if( outputType == "jpg" ) { - errorCode = resultImage->saveAsJPEG( outputFile, compression, subsampling ); - } else if( outputType == "tif" ) { - errorCode = resultImage->saveAsTIFF( outputFile, bits, compression == 0 ); - } else if( outputType == "png" ) { - errorCode = resultImage->saveAsPNG( outputFile, compression, bits ); - } else { - errorCode = resultImage->saveToFile (outputFile); - } - - if(errorCode) { - errors++; - std::cerr << "Error saving to: " << outputFile << std::endl; - } else { - if( copyParamsFile ) { - Glib::ustring outputProcessingParams = outputFile + paramFileExtension; - currentParams.save( outputProcessingParams ); - } - } - - ii->decreaseRef(); - resultImage->free(); - } - - if (imgParams) { - imgParams->deleteInstance(); - delete imgParams; - } - - if (rawParams) { - rawParams->deleteInstance(); - delete rawParams; - } - - deleteProcParams(processingParams); - return errors > 0 ? -2 : 0; } diff --git a/rtgui/options.cc b/rtgui/options.cc index 9a8b5e08b..2aaf302c1 100644 --- a/rtgui/options.cc +++ b/rtgui/options.cc @@ -2186,7 +2186,7 @@ int Options::saveToFile (Glib::ustring fname) } } -bool Options::load () +bool Options::load (bool lightweight) { // Find the application data path @@ -2325,7 +2325,7 @@ bool Options::load () // out which are the parent translations. Furthermore, there must be a file for each locale () -- you cannot have // 'French (CA)' unless there is a file 'French'. - Glib::ustring defaultTranslation = argv0 + "/languages/default"; + Glib::ustring defaultTranslation = Glib::build_filename (argv0, "languages", "default"); Glib::ustring languageTranslation = ""; Glib::ustring localeTranslation = ""; @@ -2337,17 +2337,17 @@ bool Options::load () std::vector langPortions = Glib::Regex::split_simple (" ", options.language); if (langPortions.size() >= 1) { - languageTranslation = argv0 + "/languages/" + langPortions.at (0); + languageTranslation = Glib::build_filename (argv0, "languages", langPortions.at (0)); } if (langPortions.size() >= 2) { - localeTranslation = argv0 + "/languages/" + options.language; + localeTranslation = Glib::build_filename (argv0, "languages", options.language); } } langMgr.load (localeTranslation, new MultiLangMgr (languageTranslation, new MultiLangMgr (defaultTranslation))); - rtengine::init (&options.rtSettings, argv0, rtdir); + rtengine::init (&options.rtSettings, argv0, rtdir, !lightweight); return true; } diff --git a/rtgui/options.h b/rtgui/options.h index c35668ccc..d25a6bde8 100644 --- a/rtgui/options.h +++ b/rtgui/options.h @@ -40,7 +40,7 @@ // Profile name to use for internal values' profile #define DEFPROFILE_INTERNAL "Neutral" // Special name for the Dynamic profile -#define DEFPROFILE_DYNAMIC "Dynamic" +#define DEFPROFILE_DYNAMIC "Dynamic" struct SaveFormat { @@ -318,7 +318,7 @@ public: void setDefaults (); int readFromFile (Glib::ustring fname); int saveToFile (Glib::ustring fname); - static bool load (); + static bool load (bool lightweight = false); static void save (); // if multiUser=false, send back the global profile path diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 7704327bb..4d05158c0 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -360,6 +360,7 @@ void ParamsEdited::set (bool v) icm.gampos = v; icm.slpos = v; raw.bayersensor.method = v; + raw.bayersensor.imageNum = v; raw.bayersensor.ccSteps = v; raw.bayersensor.exBlack0 = v; raw.bayersensor.exBlack1 = v; @@ -370,6 +371,35 @@ void ParamsEdited::set (bool v) raw.bayersensor.dcbEnhance = v; //raw.bayersensor.allEnhance = v; raw.bayersensor.lmmseIterations = v; + raw.bayersensor.pixelShiftMotion = v; + raw.bayersensor.pixelShiftMotionCorrection = v; + raw.bayersensor.pixelShiftMotionCorrectionMethod = v; + raw.bayersensor.pixelShiftStddevFactorGreen = v; + raw.bayersensor.pixelShiftStddevFactorRed = v; + raw.bayersensor.pixelShiftStddevFactorBlue = v; + raw.bayersensor.pixelShiftEperIso = v; + raw.bayersensor.pixelShiftNreadIso = v; + raw.bayersensor.pixelShiftPrnu = v; + raw.bayersensor.pixelShiftSigma = v; + raw.bayersensor.pixelShiftSum = v; + raw.bayersensor.pixelShiftRedBlueWeight = v; + raw.bayersensor.pixelshiftShowMotion = v; + raw.bayersensor.pixelshiftShowMotionMaskOnly = v; + raw.bayersensor.pixelShiftAutomatic = v; + raw.bayersensor.pixelShiftNonGreenHorizontal = v; + raw.bayersensor.pixelShiftNonGreenVertical = v; + raw.bayersensor.pixelShiftHoleFill = v; + raw.bayersensor.pixelShiftMedian = v; + raw.bayersensor.pixelShiftMedian3 = v; + raw.bayersensor.pixelShiftGreen = v; + raw.bayersensor.pixelShiftBlur = v; + raw.bayersensor.pixelShiftSmooth = v; + raw.bayersensor.pixelShiftExp0 = v; + raw.bayersensor.pixelShiftLmmse = v; + raw.bayersensor.pixelShiftEqualBright = v; + raw.bayersensor.pixelShiftNonGreenCross = v; + raw.bayersensor.pixelShiftNonGreenCross2 = v; + raw.bayersensor.pixelShiftNonGreenAmaze = v; raw.bayersensor.greenEq = v; raw.bayersensor.linenoise = v; raw.xtranssensor.method = v; @@ -856,6 +886,7 @@ void ParamsEdited::initFrom (const std::vector icm.gampos = icm.gampos && p.icm.gampos == other.icm.gampos; icm.slpos = icm.slpos && p.icm.slpos == other.icm.slpos; raw.bayersensor.method = raw.bayersensor.method && p.raw.bayersensor.method == other.raw.bayersensor.method; + raw.bayersensor.imageNum = raw.bayersensor.imageNum && p.raw.bayersensor.imageNum == other.raw.bayersensor.imageNum; raw.bayersensor.ccSteps = raw.bayersensor.ccSteps && p.raw.bayersensor.ccSteps == other.raw.bayersensor.ccSteps; raw.bayersensor.exBlack0 = raw.bayersensor.exBlack0 && p.raw.bayersensor.black0 == other.raw.bayersensor.black0; raw.bayersensor.exBlack1 = raw.bayersensor.exBlack1 && p.raw.bayersensor.black1 == other.raw.bayersensor.black1; @@ -866,6 +897,35 @@ void ParamsEdited::initFrom (const std::vector raw.bayersensor.dcbEnhance = raw.bayersensor.dcbEnhance && p.raw.bayersensor.dcb_enhance == other.raw.bayersensor.dcb_enhance; //raw.bayersensor.allEnhance = raw.bayersensor.allEnhance && p.raw.bayersensor.all_enhance == other.raw.bayersensor.all_enhance; raw.bayersensor.lmmseIterations = raw.bayersensor.lmmseIterations && p.raw.bayersensor.lmmse_iterations == other.raw.bayersensor.lmmse_iterations; + raw.bayersensor.pixelShiftMotion = raw.bayersensor.pixelShiftMotion && p.raw.bayersensor.pixelShiftMotion == other.raw.bayersensor.pixelShiftMotion; + raw.bayersensor.pixelShiftMotionCorrection = raw.bayersensor.pixelShiftMotionCorrection && p.raw.bayersensor.pixelShiftMotionCorrection == other.raw.bayersensor.pixelShiftMotionCorrection; + raw.bayersensor.pixelShiftMotionCorrectionMethod = raw.bayersensor.pixelShiftMotionCorrectionMethod && p.raw.bayersensor.pixelShiftMotionCorrectionMethod == other.raw.bayersensor.pixelShiftMotionCorrectionMethod; + raw.bayersensor.pixelShiftStddevFactorGreen = raw.bayersensor.pixelShiftStddevFactorGreen && p.raw.bayersensor.pixelShiftStddevFactorGreen == other.raw.bayersensor.pixelShiftStddevFactorGreen; + raw.bayersensor.pixelShiftStddevFactorRed = raw.bayersensor.pixelShiftStddevFactorRed && p.raw.bayersensor.pixelShiftStddevFactorRed == other.raw.bayersensor.pixelShiftStddevFactorRed; + raw.bayersensor.pixelShiftStddevFactorBlue = raw.bayersensor.pixelShiftStddevFactorBlue && p.raw.bayersensor.pixelShiftStddevFactorBlue == other.raw.bayersensor.pixelShiftStddevFactorBlue; + raw.bayersensor.pixelShiftEperIso = raw.bayersensor.pixelShiftEperIso && p.raw.bayersensor.pixelShiftEperIso == other.raw.bayersensor.pixelShiftEperIso; + raw.bayersensor.pixelShiftNreadIso = raw.bayersensor.pixelShiftNreadIso && p.raw.bayersensor.pixelShiftNreadIso == other.raw.bayersensor.pixelShiftNreadIso; + raw.bayersensor.pixelShiftPrnu = raw.bayersensor.pixelShiftPrnu && p.raw.bayersensor.pixelShiftPrnu == other.raw.bayersensor.pixelShiftPrnu; + raw.bayersensor.pixelShiftSigma = raw.bayersensor.pixelShiftSigma && p.raw.bayersensor.pixelShiftSigma == other.raw.bayersensor.pixelShiftSigma; + raw.bayersensor.pixelShiftSum = raw.bayersensor.pixelShiftSum && p.raw.bayersensor.pixelShiftSum == other.raw.bayersensor.pixelShiftSum; + raw.bayersensor.pixelShiftRedBlueWeight = raw.bayersensor.pixelShiftRedBlueWeight && p.raw.bayersensor.pixelShiftRedBlueWeight == other.raw.bayersensor.pixelShiftRedBlueWeight; + raw.bayersensor.pixelshiftShowMotion = raw.bayersensor.pixelshiftShowMotion && p.raw.bayersensor.pixelshiftShowMotion == other.raw.bayersensor.pixelshiftShowMotion; + raw.bayersensor.pixelshiftShowMotionMaskOnly = raw.bayersensor.pixelshiftShowMotionMaskOnly && p.raw.bayersensor.pixelshiftShowMotionMaskOnly == other.raw.bayersensor.pixelshiftShowMotionMaskOnly; + raw.bayersensor.pixelShiftAutomatic = raw.bayersensor.pixelShiftAutomatic && p.raw.bayersensor.pixelShiftAutomatic == other.raw.bayersensor.pixelShiftAutomatic; + raw.bayersensor.pixelShiftNonGreenHorizontal = raw.bayersensor.pixelShiftNonGreenHorizontal && p.raw.bayersensor.pixelShiftNonGreenHorizontal == other.raw.bayersensor.pixelShiftNonGreenHorizontal; + raw.bayersensor.pixelShiftNonGreenVertical = raw.bayersensor.pixelShiftNonGreenVertical && p.raw.bayersensor.pixelShiftNonGreenVertical == other.raw.bayersensor.pixelShiftNonGreenVertical; + raw.bayersensor.pixelShiftHoleFill = raw.bayersensor.pixelShiftHoleFill && p.raw.bayersensor.pixelShiftHoleFill == other.raw.bayersensor.pixelShiftHoleFill; + raw.bayersensor.pixelShiftMedian = raw.bayersensor.pixelShiftMedian && p.raw.bayersensor.pixelShiftMedian == other.raw.bayersensor.pixelShiftMedian; + raw.bayersensor.pixelShiftMedian3 = raw.bayersensor.pixelShiftMedian3 && p.raw.bayersensor.pixelShiftMedian3 == other.raw.bayersensor.pixelShiftMedian3; + raw.bayersensor.pixelShiftGreen = raw.bayersensor.pixelShiftGreen && p.raw.bayersensor.pixelShiftGreen == other.raw.bayersensor.pixelShiftGreen; + raw.bayersensor.pixelShiftBlur = raw.bayersensor.pixelShiftBlur && p.raw.bayersensor.pixelShiftBlur == other.raw.bayersensor.pixelShiftBlur; + raw.bayersensor.pixelShiftSmooth = raw.bayersensor.pixelShiftSmooth && p.raw.bayersensor.pixelShiftSmoothFactor == other.raw.bayersensor.pixelShiftSmoothFactor; + raw.bayersensor.pixelShiftExp0 = raw.bayersensor.pixelShiftExp0 && p.raw.bayersensor.pixelShiftExp0 == other.raw.bayersensor.pixelShiftExp0; + raw.bayersensor.pixelShiftLmmse = raw.bayersensor.pixelShiftLmmse && p.raw.bayersensor.pixelShiftLmmse == other.raw.bayersensor.pixelShiftLmmse; + raw.bayersensor.pixelShiftEqualBright = raw.bayersensor.pixelShiftEqualBright && p.raw.bayersensor.pixelShiftEqualBright == other.raw.bayersensor.pixelShiftEqualBright; + raw.bayersensor.pixelShiftNonGreenCross = raw.bayersensor.pixelShiftNonGreenCross && p.raw.bayersensor.pixelShiftNonGreenCross == other.raw.bayersensor.pixelShiftNonGreenCross; + raw.bayersensor.pixelShiftNonGreenCross2 = raw.bayersensor.pixelShiftNonGreenCross2 && p.raw.bayersensor.pixelShiftNonGreenCross2 == other.raw.bayersensor.pixelShiftNonGreenCross2; + raw.bayersensor.pixelShiftNonGreenAmaze = raw.bayersensor.pixelShiftNonGreenAmaze && p.raw.bayersensor.pixelShiftNonGreenAmaze == other.raw.bayersensor.pixelShiftNonGreenAmaze; raw.bayersensor.greenEq = raw.bayersensor.greenEq && p.raw.bayersensor.greenthresh == other.raw.bayersensor.greenthresh; raw.bayersensor.linenoise = raw.bayersensor.linenoise && p.raw.bayersensor.linenoise == other.raw.bayersensor.linenoise; raw.xtranssensor.method = raw.xtranssensor.method && p.raw.xtranssensor.method == other.raw.xtranssensor.method; @@ -2242,6 +2302,10 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten toEdit.raw.bayersensor.method = mods.raw.bayersensor.method; } + if (raw.bayersensor.imageNum) { + toEdit.raw.bayersensor.imageNum = mods.raw.bayersensor.imageNum; + } + if (raw.bayersensor.ccSteps) { toEdit.raw.bayersensor.ccSteps = mods.raw.bayersensor.ccSteps; } @@ -2278,7 +2342,122 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten toEdit.raw.bayersensor.lmmse_iterations = mods.raw.bayersensor.lmmse_iterations; } - //if (raw.bayersensor.allEnhance) toEdit.raw.bayersensor.all_enhance = mods.raw.bayersensor.all_enhance; + if (raw.bayersensor.pixelShiftMotion) { + toEdit.raw.bayersensor.pixelShiftMotion = mods.raw.bayersensor.pixelShiftMotion; + } + + if (raw.bayersensor.pixelShiftMotionCorrection) { + toEdit.raw.bayersensor.pixelShiftMotionCorrection = mods.raw.bayersensor.pixelShiftMotionCorrection; + } + + if (raw.bayersensor.pixelShiftMotionCorrectionMethod) { + toEdit.raw.bayersensor.pixelShiftMotionCorrectionMethod = mods.raw.bayersensor.pixelShiftMotionCorrectionMethod; + } + + if (raw.bayersensor.pixelShiftStddevFactorGreen) { + toEdit.raw.bayersensor.pixelShiftStddevFactorGreen = mods.raw.bayersensor.pixelShiftStddevFactorGreen; + } + + if (raw.bayersensor.pixelShiftStddevFactorRed) { + toEdit.raw.bayersensor.pixelShiftStddevFactorRed = mods.raw.bayersensor.pixelShiftStddevFactorRed; + } + + if (raw.bayersensor.pixelShiftStddevFactorBlue) { + toEdit.raw.bayersensor.pixelShiftStddevFactorBlue = mods.raw.bayersensor.pixelShiftStddevFactorBlue; + } + + if (raw.bayersensor.pixelShiftEperIso) { + toEdit.raw.bayersensor.pixelShiftEperIso = mods.raw.bayersensor.pixelShiftEperIso; + } + + if (raw.bayersensor.pixelShiftNreadIso) { + toEdit.raw.bayersensor.pixelShiftNreadIso = mods.raw.bayersensor.pixelShiftNreadIso; + } + + if (raw.bayersensor.pixelShiftPrnu) { + toEdit.raw.bayersensor.pixelShiftPrnu = mods.raw.bayersensor.pixelShiftPrnu; + } + + if (raw.bayersensor.pixelShiftSigma) { + toEdit.raw.bayersensor.pixelShiftSigma = mods.raw.bayersensor.pixelShiftSigma; + } + + if (raw.bayersensor.pixelShiftSum) { + toEdit.raw.bayersensor.pixelShiftSum = mods.raw.bayersensor.pixelShiftSum; + } + + if (raw.bayersensor.pixelShiftRedBlueWeight) { + toEdit.raw.bayersensor.pixelShiftRedBlueWeight = mods.raw.bayersensor.pixelShiftRedBlueWeight; + } + + if (raw.bayersensor.pixelshiftShowMotion) { + toEdit.raw.bayersensor.pixelshiftShowMotion = mods.raw.bayersensor.pixelshiftShowMotion; + } + + if (raw.bayersensor.pixelshiftShowMotionMaskOnly) { + toEdit.raw.bayersensor.pixelshiftShowMotionMaskOnly = mods.raw.bayersensor.pixelshiftShowMotionMaskOnly; + } + + if (raw.bayersensor.pixelShiftAutomatic) { + toEdit.raw.bayersensor.pixelShiftAutomatic = mods.raw.bayersensor.pixelShiftAutomatic; + } + + if (raw.bayersensor.pixelShiftNonGreenHorizontal) { + toEdit.raw.bayersensor.pixelShiftNonGreenHorizontal = mods.raw.bayersensor.pixelShiftNonGreenHorizontal; + } + + if (raw.bayersensor.pixelShiftNonGreenVertical) { + toEdit.raw.bayersensor.pixelShiftNonGreenVertical = mods.raw.bayersensor.pixelShiftNonGreenVertical; + } + + if (raw.bayersensor.pixelShiftHoleFill) { + toEdit.raw.bayersensor.pixelShiftHoleFill = mods.raw.bayersensor.pixelShiftHoleFill; + } + + if (raw.bayersensor.pixelShiftMedian) { + toEdit.raw.bayersensor.pixelShiftMedian = mods.raw.bayersensor.pixelShiftMedian; + } + + if (raw.bayersensor.pixelShiftMedian3) { + toEdit.raw.bayersensor.pixelShiftMedian3 = mods.raw.bayersensor.pixelShiftMedian3; + } + + if (raw.bayersensor.pixelShiftGreen) { + toEdit.raw.bayersensor.pixelShiftGreen = mods.raw.bayersensor.pixelShiftGreen; + } + + if (raw.bayersensor.pixelShiftBlur) { + toEdit.raw.bayersensor.pixelShiftBlur = mods.raw.bayersensor.pixelShiftBlur; + } + + if (raw.bayersensor.pixelShiftSmooth) { + toEdit.raw.bayersensor.pixelShiftSmoothFactor = mods.raw.bayersensor.pixelShiftSmoothFactor; + } + + if (raw.bayersensor.pixelShiftExp0) { + toEdit.raw.bayersensor.pixelShiftExp0 = mods.raw.bayersensor.pixelShiftExp0; + } + + if (raw.bayersensor.pixelShiftLmmse) { + toEdit.raw.bayersensor.pixelShiftLmmse = mods.raw.bayersensor.pixelShiftLmmse; + } + + if (raw.bayersensor.pixelShiftEqualBright) { + toEdit.raw.bayersensor.pixelShiftEqualBright = mods.raw.bayersensor.pixelShiftEqualBright; + } + + if (raw.bayersensor.pixelShiftNonGreenCross) { + toEdit.raw.bayersensor.pixelShiftNonGreenCross = mods.raw.bayersensor.pixelShiftNonGreenCross; + } + + if (raw.bayersensor.pixelShiftNonGreenCross2) { + toEdit.raw.bayersensor.pixelShiftNonGreenCross2 = mods.raw.bayersensor.pixelShiftNonGreenCross2; + } + + if (raw.bayersensor.pixelShiftNonGreenAmaze) { + toEdit.raw.bayersensor.pixelShiftNonGreenAmaze = mods.raw.bayersensor.pixelShiftNonGreenAmaze; + } + if (raw.bayersensor.greenEq) { toEdit.raw.bayersensor.greenthresh = dontforceSet && options.baBehav[ADDSET_PREPROCESS_GREENEQUIL] ? toEdit.raw.bayersensor.greenthresh + mods.raw.bayersensor.greenthresh : mods.raw.bayersensor.greenthresh; } @@ -2788,7 +2967,10 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten bool RAWParamsEdited::BayerSensor::isUnchanged() const { - return method && dcbIterations && dcbEnhance && lmmseIterations/*&& allEnhance*/ && greenEq + return method && imageNum && dcbIterations && dcbEnhance && lmmseIterations/*&& allEnhance*/ && greenEq + && pixelShiftMotion && pixelShiftMotionCorrection && pixelShiftMotionCorrectionMethod && pixelShiftStddevFactorGreen && pixelShiftStddevFactorRed && pixelShiftStddevFactorBlue && pixelShiftEperIso + && pixelShiftNreadIso && pixelShiftPrnu && pixelShiftSigma && pixelShiftSum && pixelShiftRedBlueWeight && pixelshiftShowMotion && pixelshiftShowMotionMaskOnly + && pixelShiftAutomatic && pixelShiftNonGreenHorizontal && pixelShiftNonGreenVertical && pixelShiftHoleFill && pixelShiftMedian && pixelShiftMedian3 && pixelShiftNonGreenCross && pixelShiftNonGreenCross2 && pixelShiftNonGreenAmaze && pixelShiftGreen && pixelShiftBlur && pixelShiftSmooth && pixelShiftExp0 && pixelShiftLmmse && pixelShiftEqualBright && linenoise && exBlack0 && exBlack1 && exBlack2 && exBlack3 && exTwoGreen; } diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index fc549d91a..fc69a1e48 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -682,6 +682,7 @@ public: public: bool method; + bool imageNum; bool ccSteps; bool exBlack0; bool exBlack1; @@ -691,6 +692,36 @@ public: bool dcbIterations; bool dcbEnhance; bool lmmseIterations; + bool pixelShiftMotion; + bool pixelShiftMotionCorrection; + bool pixelShiftMotionCorrectionMethod; + bool pixelShiftStddevFactorGreen; + bool pixelShiftStddevFactorRed; + bool pixelShiftStddevFactorBlue; + bool pixelShiftEperIso; + bool pixelShiftNreadIso; + bool pixelShiftPrnu; + bool pixelShiftSigma; + bool pixelShiftSum; + bool pixelShiftRedBlueWeight; + bool pixelshiftShowMotion; + bool pixelshiftShowMotionMaskOnly; + bool pixelShiftAutomatic; + bool pixelShiftNonGreenHorizontal; + bool pixelShiftNonGreenVertical; + bool pixelShiftHoleFill; + bool pixelShiftMedian; + bool pixelShiftMedian3; + bool pixelShiftGreen; + bool pixelShiftBlur; + bool pixelShiftSmooth; + bool pixelShiftExp0; + bool pixelShiftLmmse; + bool pixelShiftEqualBright; + bool pixelShiftNonGreenCross; + bool pixelShiftNonGreenCross2; + bool pixelShiftNonGreenAmaze; + //bool allEnhance; bool greenEq; bool linenoise; diff --git a/rtgui/partialpastedlg.cc b/rtgui/partialpastedlg.cc index c0cc2c87c..7cf7de15e 100644 --- a/rtgui/partialpastedlg.cc +++ b/rtgui/partialpastedlg.cc @@ -111,6 +111,7 @@ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title, Gtk::Window* paren raw_linenoise = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_PREPROCESS_LINEDENOISE"))); raw_greenthresh = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_PREPROCESS_GREENEQUIL"))); raw_method = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_RAW_DMETHOD"))); + raw_imagenum = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_RAW_IMAGENUM"))); raw_ccSteps = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_RAW_FALSECOLOR"))); raw_dcb_iterations = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_RAW_DCBITERATIONS"))); raw_dcb_enhance = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_RAW_DCBENHANCE"))); @@ -200,6 +201,7 @@ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title, Gtk::Window* paren vboxes[6]->pack_start (*raw, Gtk::PACK_SHRINK, 2); vboxes[6]->pack_start (*hseps[6], Gtk::PACK_SHRINK, 2); vboxes[6]->pack_start (*raw_method, Gtk::PACK_SHRINK, 2); + vboxes[6]->pack_start (*raw_imagenum, Gtk::PACK_SHRINK, 2); vboxes[6]->pack_start (*raw_ccSteps, Gtk::PACK_SHRINK, 2); vboxes[6]->pack_start (*raw_dcb_iterations, Gtk::PACK_SHRINK, 2); vboxes[6]->pack_start (*raw_dcb_enhance, Gtk::PACK_SHRINK, 2); @@ -338,7 +340,8 @@ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title, Gtk::Window* paren exifchConn = exifch->signal_toggled().connect (sigc::bind (sigc::mem_fun(*meta, &Gtk::CheckButton::set_inconsistent), true)); iptcConn = iptc->signal_toggled().connect (sigc::bind (sigc::mem_fun(*meta, &Gtk::CheckButton::set_inconsistent), true)); - raw_methodConn = raw_method->signal_toggled().connect (sigc::bind (sigc::mem_fun(*raw, &Gtk::CheckButton::set_inconsistent), true)); + raw_methodConn = raw_method->signal_toggled().connect (sigc::bind (sigc::mem_fun(*raw, &Gtk::CheckButton::set_inconsistent), true)); + raw_imagenumConn = raw_imagenum->signal_toggled().connect (sigc::bind (sigc::mem_fun(*raw, &Gtk::CheckButton::set_inconsistent), true)); raw_ccStepsConn = raw_ccSteps->signal_toggled().connect (sigc::bind (sigc::mem_fun(*raw, &Gtk::CheckButton::set_inconsistent), true)); raw_dcb_iterationsConn = raw_dcb_iterations->signal_toggled().connect (sigc::bind (sigc::mem_fun(*raw, &Gtk::CheckButton::set_inconsistent), true)); raw_dcb_enhanceConn = raw_dcb_enhance->signal_toggled().connect (sigc::bind (sigc::mem_fun(*raw, &Gtk::CheckButton::set_inconsistent), true)); @@ -418,6 +421,7 @@ void PartialPasteDlg::rawToggled () { raw_methodConn.block (true); + raw_imagenumConn.block (true); raw_ccStepsConn.block (true); raw_dcb_iterationsConn.block (true); raw_dcb_enhanceConn.block (true); @@ -444,6 +448,7 @@ void PartialPasteDlg::rawToggled () raw->set_inconsistent (false); raw_method->set_active (raw->get_active ()); + raw_imagenum->set_active (raw->get_active ()); raw_ccSteps->set_active (raw->get_active ()); raw_dcb_iterations->set_active (raw->get_active ()); raw_dcb_enhance->set_active (raw->get_active ()); @@ -468,6 +473,7 @@ void PartialPasteDlg::rawToggled () ff_ClipControl->set_active (raw->get_active ()); raw_methodConn.block (false); + raw_imagenumConn.block (false); raw_ccStepsConn.block (false); raw_dcb_iterationsConn.block (false); raw_dcb_enhanceConn.block (false); @@ -847,6 +853,10 @@ void PartialPasteDlg::applyPaste (rtengine::procparams::ProcParams* dstPP, Param filterPE.raw.xtranssensor.method = falsePE.raw.xtranssensor.method; } + if (!raw_imagenum->get_active ()) { + filterPE.raw.bayersensor.imageNum = falsePE.raw.bayersensor.imageNum; + } + if (!raw_ccSteps->get_active ()) { filterPE.raw.bayersensor.ccSteps = falsePE.raw.bayersensor.ccSteps; filterPE.raw.xtranssensor.ccSteps = falsePE.raw.xtranssensor.ccSteps; diff --git a/rtgui/partialpastedlg.h b/rtgui/partialpastedlg.h index 99249eefd..177b0b720 100644 --- a/rtgui/partialpastedlg.h +++ b/rtgui/partialpastedlg.h @@ -107,6 +107,7 @@ public: Gtk::CheckButton* raw_linenoise; Gtk::CheckButton* raw_greenthresh; Gtk::CheckButton* raw_method; + Gtk::CheckButton* raw_imagenum; Gtk::CheckButton* raw_ccSteps; Gtk::CheckButton* raw_dcb_iterations; Gtk::CheckButton* raw_dcb_enhance; @@ -129,7 +130,7 @@ public: sigc::connection coarserotConn, finerotConn, cropConn, resizeConn, prsharpeningConn, perspectiveConn, commonTransConn; sigc::connection exifchConn, iptcConn, icmConn; sigc::connection df_fileConn, df_AutoSelectConn, ff_fileConn, ff_AutoSelectConn, ff_BlurRadiusConn, ff_BlurTypeConn, ff_ClipControlConn; - sigc::connection raw_caredConn, raw_cablueConn, raw_ca_autocorrectConn, raw_hotpix_filtConn, raw_deadpix_filtConn, raw_linenoiseConn, raw_greenthreshConn, raw_ccStepsConn, raw_methodConn, raw_dcb_iterationsConn, raw_lmmse_iterationsConn, raw_dcb_enhanceConn, raw_exposConn, raw_preserConn, raw_blackConn; + sigc::connection raw_caredConn, raw_cablueConn, raw_ca_autocorrectConn, raw_hotpix_filtConn, raw_deadpix_filtConn, raw_linenoiseConn, raw_greenthreshConn, raw_ccStepsConn, raw_methodConn, raw_imagenumConn, raw_dcb_iterationsConn, raw_lmmse_iterationsConn, raw_dcb_enhanceConn, raw_exposConn, raw_preserConn, raw_blackConn; public: PartialPasteDlg (const Glib::ustring &title, Gtk::Window* parent); diff --git a/rtgui/pathutils.cc b/rtgui/pathutils.cc new file mode 100644 index 000000000..21c452d4c --- /dev/null +++ b/rtgui/pathutils.cc @@ -0,0 +1,48 @@ +/* + * This file is part of RawTherapee. + * + * + * RawTherapee is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RawTherapee is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RawTherapee. If not, see . + */ + +#include "pathutils.h" + + +Glib::ustring removeExtension (const Glib::ustring& filename) +{ + + Glib::ustring bname = Glib::path_get_basename(filename); + size_t lastdot = bname.find_last_of ('.'); + size_t lastwhitespace = bname.find_last_of (" \t\f\v\n\r"); + + if (lastdot != bname.npos && (lastwhitespace == bname.npos || lastdot > lastwhitespace)) { + return filename.substr (0, filename.size() - (bname.size() - lastdot)); + } else { + return filename; + } +} + +Glib::ustring getExtension (const Glib::ustring& filename) +{ + + Glib::ustring bname = Glib::path_get_basename(filename); + size_t lastdot = bname.find_last_of ('.'); + size_t lastwhitespace = bname.find_last_of (" \t\f\v\n\r"); + + if (lastdot != bname.npos && (lastwhitespace == bname.npos || lastdot > lastwhitespace)) { + return filename.substr (filename.size() - (bname.size() - lastdot) + 1, filename.npos); + } else { + return ""; + } +} diff --git a/rtgui/pathutils.h b/rtgui/pathutils.h new file mode 100644 index 000000000..ce58103b9 --- /dev/null +++ b/rtgui/pathutils.h @@ -0,0 +1,34 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2004-2010 Gabor Horvath + * + * RawTherapee is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RawTherapee is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RawTherapee. If not, see . + */ +#ifndef __PATH_UTILS_ +#define __PATH_UTILS_ + +#include +#include +#include "../rtengine/rtengine.h" +#include "../rtengine/coord.h" +#include "rtimage.h" +#include +#include + +// Removed from guiutils because used by rawtherapee-cli +Glib::ustring removeExtension (const Glib::ustring& filename); +Glib::ustring getExtension (const Glib::ustring& filename); + +#endif diff --git a/rtgui/preferences.cc b/rtgui/preferences.cc index 5b28d94c1..8130e2dfc 100644 --- a/rtgui/preferences.cc +++ b/rtgui/preferences.cc @@ -22,6 +22,7 @@ #include "splash.h" #include "cachemanager.h" #include "addsetids.h" +#include "../rtengine/icons.h" #include "../rtengine/dfmanager.h" #include "../rtengine/ffmanager.h" #include @@ -1895,13 +1896,13 @@ void Preferences::fillPreferences () if (Glib::file_test (moptions.gimpDir, Glib::FILE_TEST_IS_DIR)) { gimpDir->set_current_folder (moptions.gimpDir); } else { - gimpDir->set_current_folder (""); + gimpDir->set_current_folder (Glib::get_home_dir()); } if (Glib::file_test (moptions.psDir, Glib::FILE_TEST_IS_DIR)) { psDir->set_current_folder (moptions.psDir); } else { - psDir->set_current_folder (""); + psDir->set_current_folder (Glib::get_home_dir()); } #elif defined __APPLE__ @@ -1909,6 +1910,8 @@ void Preferences::fillPreferences () if (Glib::file_test (moptions.psDir, Glib::FILE_TEST_IS_DIR)) { psDir->set_current_folder (moptions.psDir); + } else { + psDir->set_current_folder (Glib::get_home_dir()); } #endif @@ -2069,7 +2072,7 @@ void Preferences::cancelPressed () { // set the initial theme back if (themeFNames.at(theme->get_active_row_number ()).longFName != options.theme) { - RTImage::setPaths(options); + rtengine::setPaths(options); RTImage::updateImages(); switchThemeTo(options.theme); } @@ -2126,7 +2129,7 @@ void Preferences::themeChanged () { moptions.theme = themeFNames.at(theme->get_active_row_number ()).longFName; - RTImage::setPaths(moptions); + rtengine::setPaths(moptions); RTImage::updateImages(); switchThemeTo(moptions.theme); } diff --git a/rtgui/rawcacorrection.cc b/rtgui/rawcacorrection.cc index d619ab112..683dcc97f 100644 --- a/rtgui/rawcacorrection.cc +++ b/rtgui/rawcacorrection.cc @@ -18,7 +18,6 @@ */ #include "rawcacorrection.h" #include "guiutils.h" -#include #include "rtimage.h" using namespace rtengine; @@ -31,7 +30,8 @@ RAWCACorr::RAWCACorr () : FoldableToolPanel(this, "rawcacorrection", M("TP_CHROM Gtk::Image* icablueL = Gtk::manage (new RTImage ("ajd-ca-blue1.png")); Gtk::Image* icablueR = Gtk::manage (new RTImage ("ajd-ca-blue2.png")); - caAutocorrect = Gtk::manage(new Gtk::CheckButton((M("TP_RAWCACORR_AUTO")))); + caAutocorrect = Gtk::manage (new CheckBox(M("TP_RAWCACORR_AUTO"), multiImage)); + caAutocorrect->setCheckBoxListener (this); caStrength = Gtk::manage(new Adjuster (M("TP_RAWCACORR_CASTR"), 2.0, 8.0, 0.5, 6.0)); caStrength->setAdjusterListener (this); @@ -62,40 +62,35 @@ RAWCACorr::RAWCACorr () : FoldableToolPanel(this, "rawcacorrection", M("TP_CHROM pack_start( *caRed, Gtk::PACK_SHRINK, 4); pack_start( *caBlue, Gtk::PACK_SHRINK, 4); - caacsconn = caAutocorrect->signal_toggled().connect ( sigc::mem_fun(*this, &RAWCACorr::caCorrectionChanged), true); } void RAWCACorr::read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited) { disableListener (); - caacsconn.block (true); if(pedited ) { - caAutocorrect->set_inconsistent(!pedited->raw.caCorrection); + caAutocorrect->setEdited(pedited->raw.caCorrection); caStrength->setEditedState( pedited->raw.caAutoStrength ? Edited : UnEdited ); caRed->setEditedState( pedited->raw.caRed ? Edited : UnEdited ); caBlue->setEditedState( pedited->raw.caBlue ? Edited : UnEdited ); } - lastCA = pp->raw.ca_autocorrect; - caStrength->set_sensitive(pp->raw.ca_autocorrect); // disable Red and Blue sliders when caAutocorrect is enabled caRed->set_sensitive(!pp->raw.ca_autocorrect); caBlue->set_sensitive(!pp->raw.ca_autocorrect); - caAutocorrect->set_active(pp->raw.ca_autocorrect); + caAutocorrect->setValue(pp->raw.ca_autocorrect); caStrength->setValue (pp->raw.caautostrength); caRed->setValue (pp->raw.cared); caBlue->setValue (pp->raw.cablue); - caacsconn.block (false); enableListener (); } void RAWCACorr::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pedited) { - pp->raw.ca_autocorrect = caAutocorrect->get_active(); + pp->raw.ca_autocorrect = caAutocorrect->getLastActive(); pp->raw.caautostrength = caStrength->getValue(); pp->raw.cared = caRed->getValue(); pp->raw.cablue = caBlue->getValue(); @@ -125,6 +120,21 @@ void RAWCACorr::adjusterChanged (Adjuster* a, double newval) } } +void RAWCACorr::checkBoxToggled (CheckBox* c, CheckValue newval) +{ + if (c == caAutocorrect) { + if (!batchMode) { + caStrength->set_sensitive(caAutocorrect->getLastActive ()); + // disable Red and Blue sliders when caAutocorrect is enabled + caRed->set_sensitive(!caAutocorrect->getLastActive ()); + caBlue->set_sensitive(!caAutocorrect->getLastActive ()); + } + if (listener) { + listener->panelChanged (EvPreProcessAutoCA, caAutocorrect->getLastActive() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + } + } +} + void RAWCACorr::setBatchMode(bool batchMode) { ToolPanel::setBatchMode (batchMode); @@ -150,52 +160,6 @@ void RAWCACorr::setDefaults(const rtengine::procparams::ProcParams* defParams, c } } -void RAWCACorr::caCorrectionChanged() -{ - if (batchMode) { - if (caAutocorrect->get_inconsistent()) { - caAutocorrect->set_inconsistent (false); - caacsconn.block (true); - caAutocorrect->set_active (false); - caacsconn.block (false); - } else if (lastCA) { - caAutocorrect->set_inconsistent (true); - } - - lastCA = caAutocorrect->get_active (); - - } - - /*else { - // For non batch mode, we disable the red and blue slider if caAutocorrect is true - if (caAutocorrect->get_active ()) { - caRed->set_sensitive(false); - caBlue->set_sensitive(false); - } - else { - caRed->set_sensitive(true); - caBlue->set_sensitive(true); - } - }*/ - - caStrength->set_sensitive(caAutocorrect->get_active ()); - // disable Red and Blue sliders when caAutocorrect is enabled - caRed->set_sensitive(!caAutocorrect->get_active ()); - caBlue->set_sensitive(!caAutocorrect->get_active ()); - - if (caAutocorrect->get_active ()) { - // set caRed and caBlue to 0 as RawImageSource::CA_correct_RT uses this as - // a condition for auto-CA correction. Alternative would be to change - // RawImageSource::CA_correct_RT and pass it ca_autocorrect value - caRed->setValue(0); - caBlue->setValue(0); - } - - if (listener) { - listener->panelChanged (EvPreProcessAutoCA, caAutocorrect->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); - } -} - void RAWCACorr::setAdjusterBehavior (bool caadd) { diff --git a/rtgui/rawcacorrection.h b/rtgui/rawcacorrection.h index 4edf9f9f2..a1873b2ad 100644 --- a/rtgui/rawcacorrection.h +++ b/rtgui/rawcacorrection.h @@ -21,19 +21,17 @@ #include #include "adjuster.h" +#include "checkbox.h" #include "toolpanel.h" -#include "../rtengine/rawimage.h" -class RAWCACorr : public ToolParamBlock, public AdjusterListener, public FoldableToolPanel +class RAWCACorr : public ToolParamBlock, public AdjusterListener, public CheckBoxListener, public FoldableToolPanel { protected: - Gtk::CheckButton* caAutocorrect; + CheckBox* caAutocorrect; Adjuster* caStrength; Adjuster* caRed; Adjuster* caBlue; - bool lastCA; - sigc::connection caacsconn; public: @@ -47,7 +45,7 @@ public: void trimValues (rtengine::procparams::ProcParams* pp); void adjusterChanged (Adjuster* a, double newval); - void caCorrectionChanged (); + void checkBoxToggled (CheckBox* c, CheckValue newval); }; #endif diff --git a/rtgui/rtimage.cc b/rtgui/rtimage.cc index 464123b0f..16e8e3286 100644 --- a/rtgui/rtimage.cc +++ b/rtgui/rtimage.cc @@ -23,51 +23,13 @@ #include #include "options.h" +#include "../rtengine/icons.h" namespace { -std::vector imagePaths; std::map> pixbufCache; -bool loadIconSet(const Glib::ustring& iconSet) -{ - try { - - Glib::KeyFile keyFile; - keyFile.load_from_file (iconSet); - - auto iconSetDir = keyFile.get_string ("General", "Iconset"); - - if (!iconSetDir.empty ()) { - imagePaths.push_back (Glib::build_filename (argv0, "images", iconSetDir, "actions")); - imagePaths.push_back (Glib::build_filename (argv0, "images", iconSetDir)); - imagePaths.push_back (Glib::build_filename (argv0, "images", iconSetDir, "devices")); - imagePaths.push_back (Glib::build_filename (argv0, "images", iconSetDir, "places")); - } - - iconSetDir = keyFile.get_string ("General", "FallbackIconset"); - - if (!iconSetDir.empty ()) { - imagePaths.push_back (Glib::build_filename (argv0, "images", iconSetDir, "actions")); - imagePaths.push_back (Glib::build_filename (argv0, "images", iconSetDir)); - imagePaths.push_back (Glib::build_filename (argv0, "images", iconSetDir, "devices")); - imagePaths.push_back (Glib::build_filename (argv0, "images", iconSetDir, "places")); - } - - return true; - - } catch (const Glib::Exception& exception) { - - if (options.rtSettings.verbose) { - std::cerr << "Failed to load icon set \"" << iconSet << "\": " << exception.what() << std::endl; - } - - return false; - - } -} - } RTImage::RTImage (const Glib::ustring& fileName, const Glib::ustring& rtlFileName) : Gtk::Image() @@ -90,7 +52,7 @@ void RTImage::changeImage (const Glib::ustring& imageName) auto iterator = pixbufCache.find (imageName); if (iterator == pixbufCache.end ()) { - const auto imagePath = findIconAbsolutePath (imageName); + const auto imagePath = rtengine::findIconAbsolutePath (imageName); const auto pixbuf = Gdk::Pixbuf::create_from_file (imagePath); iterator = pixbufCache.emplace (imageName, pixbuf).first; @@ -102,70 +64,18 @@ void RTImage::changeImage (const Glib::ustring& imageName) void RTImage::updateImages() { for (auto& entry : pixbufCache) { - const auto imagePath = findIconAbsolutePath (entry.first); + const auto imagePath = rtengine::findIconAbsolutePath (entry.first); entry.second = Gdk::Pixbuf::create_from_file (imagePath); } } -Glib::ustring RTImage::findIconAbsolutePath (const Glib::ustring& iconName) -{ - try { - - for (const auto& imagePath : imagePaths) { - const auto iconPath = Glib::build_filename(imagePath, iconName); - - if (Glib::file_test(iconPath, Glib::FILE_TEST_IS_REGULAR)) { - return iconPath; - } - } - - } catch(const Glib::Exception&) {} - - if (options.rtSettings.verbose) { - std::cerr << "Icon \"" << iconName << "\" could not be found!" << std::endl; - } - - return Glib::ustring(); -} - -void RTImage::setPaths (const Options& options) -{ - // TODO: Forcing the Dark theme, so reading the icon set files is useless for now... - - /*Glib::ustring iconSet; - - // Either use the system icon set or the one specified in the options. - if (options.useSystemTheme) { - iconSet = Glib::build_filename (argv0, "themes", "system.iconset"); - } else { - iconSet = Glib::build_filename (argv0, "themes", options.theme + ".iconset"); - } - - imagePaths.clear (); - - if (!loadIconSet (iconSet)) { - // If the preferred icon set is unavailable, fall back to the default icon set. - loadIconSet (Glib::build_filename (argv0, "themes", "Default.iconset")); - }*/ - - imagePaths.clear (); - - imagePaths.push_back (Glib::build_filename(argv0, "images", "Dark")); - imagePaths.push_back (Glib::build_filename(argv0, "images", "Dark", "actions")); - imagePaths.push_back (Glib::build_filename(argv0, "images", "Dark", "devices")); - imagePaths.push_back (Glib::build_filename(argv0, "images", "Dark", "places")); - - // The images folder is the second fallback solution. - imagePaths.push_back (Glib::build_filename(argv0, "images")); -} - Glib::RefPtr RTImage::createFromFile (const Glib::ustring& fileName) { Glib::RefPtr pixbuf; try { - const auto filePath = findIconAbsolutePath (fileName); + const auto filePath = rtengine::findIconAbsolutePath (fileName); if (!filePath.empty ()) { pixbuf = Gdk::Pixbuf::create_from_file (filePath); @@ -188,7 +98,7 @@ Cairo::RefPtr RTImage::createFromPng (const Glib::ustring& try { - const auto filePath = findIconAbsolutePath (fileName); + const auto filePath = rtengine::findIconAbsolutePath (fileName); if (!filePath.empty()) { surface = Cairo::ImageSurface::create_from_png (Glib::locale_from_utf8 (filePath)); diff --git a/rtgui/rtimage.h b/rtgui/rtimage.h index cfc7a5fa1..0286d990b 100644 --- a/rtgui/rtimage.h +++ b/rtgui/rtimage.h @@ -34,9 +34,6 @@ public: void changeImage (const Glib::ustring& imageName); static void updateImages (); - static Glib::ustring findIconAbsolutePath (const Glib::ustring& iconName); - static void setPaths (const Options& options); - static Glib::RefPtr createFromFile (const Glib::ustring& fileName); static Cairo::RefPtr createFromPng (const Glib::ustring& fileName); }; diff --git a/rtgui/rtwindow.cc b/rtgui/rtwindow.cc index 3c28cf456..b9209a38b 100644 --- a/rtgui/rtwindow.cc +++ b/rtgui/rtwindow.cc @@ -24,6 +24,7 @@ #include "cursormanager.h" #include "rtimage.h" #include "whitebalance.h" +#include "../rtengine/icons.h" #if defined(__APPLE__) static gboolean @@ -93,7 +94,7 @@ RTWindow::RTWindow () ProfilePanel::init (this); Glib::ustring fName = "rt-logo-small.png"; - Glib::ustring fullPath = RTImage::findIconAbsolutePath(fName); + Glib::ustring fullPath = rtengine::findIconAbsolutePath(fName); try { set_default_icon_from_file (fullPath); diff --git a/rtgui/thumbnail.cc b/rtgui/thumbnail.cc index 62f543d4a..1be475d59 100644 --- a/rtgui/thumbnail.cc +++ b/rtgui/thumbnail.cc @@ -138,7 +138,7 @@ void Thumbnail::_generateThumbnailImage () if ( tpp == nullptr ) { quick = false; - tpp = rtengine::Thumbnail::loadFromRaw (fname, ri, tw, th, 1, pparams.wb.equal, TRUE); + tpp = rtengine::Thumbnail::loadFromRaw (fname, ri, tw, th, 1, pparams.wb.equal, TRUE, pparams.raw.bayersensor.imageNum); } if (tpp) {