diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc
index 7f4ae11f8..f01fa57d2 100644
--- a/rtengine/procparams.cc
+++ b/rtengine/procparams.cc
@@ -8079,6 +8079,8 @@ int PartialProfile::load (const Glib::ustring &fName)
if (fName == DEFPROFILE_INTERNAL) {
return 0;
+ } else if (fName == DEFPROFILE_DYNAMIC) {
+ return -1; // should not happen here
} else {
return pparams->load(fName, pedited);
}
diff --git a/rtgui/CMakeLists.txt b/rtgui/CMakeLists.txt
index 021f6163f..e327cb4b6 100644
--- a/rtgui/CMakeLists.txt
+++ b/rtgui/CMakeLists.txt
@@ -30,7 +30,8 @@ set (BASESOURCEFILES
darkframe.cc flatfield.cc rawcacorrection.cc rawexposure.cc wavelet.cc
dirpyrequalizer.cc hsvequalizer.cc defringe.cc
popupcommon.cc popupbutton.cc popuptogglebutton.cc sharpenedge.cc sharpenmicro.cc colorappearance.cc
- filmsimulation.cc prsharpening.cc)
+ filmsimulation.cc prsharpening.cc
+ dynamicprofile.cc)
include_directories (BEFORE "${CMAKE_CURRENT_BINARY_DIR}")
diff --git a/rtgui/dynamicprofile.cc b/rtgui/dynamicprofile.cc
new file mode 100644
index 000000000..e2cedbb0a
--- /dev/null
+++ b/rtgui/dynamicprofile.cc
@@ -0,0 +1,180 @@
+/* -*- C++ -*-
+ * This file is part of RawTherapee.
+ *
+ * Copyright (c) 2017 Alberto Griggio
+ *
+ * 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 "dynamicprofile.h"
+#include
+
+using namespace rtengine;
+using namespace rtengine::procparams;
+
+DynamicProfileEntry::DynamicProfileEntry():
+ serial_number(0),
+ has_iso(false), iso_min(0), iso_max(1000000),
+ has_fnumber(false), fnumber_min(0.0), fnumber_max(1000.0),
+ has_focallen(false), focallen_min(0.0), focallen_max(1000000.0),
+ has_shutterspeed(false), shutterspeed_min(1000.0), shutterspeed_max(1.0/1000000.0),
+ has_expcomp(false), expcomp_min(-100.0), expcomp_max(100.0),
+ has_make(false), make(""),
+ has_model(false), model(""),
+ has_lens(false), lens(""),
+ profilepath("")
+{
+}
+
+
+bool DynamicProfileEntry::operator<(const DynamicProfileEntry &other) const
+{
+ return serial_number < other.serial_number;
+}
+
+
+bool DynamicProfileEntry::matches(const rtengine::ImageMetaData *im)
+{
+ if (has_iso) {
+ int iso = im->getISOSpeed();
+ if (iso < iso_min || iso > iso_max) {
+ return false;
+ }
+ }
+ if (has_fnumber) {
+ double fnumber = im->getFNumber();
+ if (fnumber < fnumber_min || fnumber > fnumber_max) {
+ return false;
+ }
+ }
+ if (has_focallen) {
+ double focallen = im->getFocalLen();
+ if (focallen < focallen_min || focallen > focallen_max) {
+ return false;
+ }
+ }
+ if (has_shutterspeed) {
+ double shutterspeed = im->getShutterSpeed();
+ if (shutterspeed < shutterspeed_min || shutterspeed > shutterspeed_max){
+ return false;
+ }
+ }
+ if (has_expcomp) {
+ double expcomp = im->getExpComp();
+ if (expcomp < expcomp_min || expcomp > expcomp_max) {
+ return false;
+ }
+ }
+ if (has_make) {
+ if (im->getMake() != make) {
+ return false;
+ }
+ }
+ if (has_model) {
+ if (im->getModel() != model) {
+ return false;
+ }
+ }
+ if (has_lens) {
+ if (im->getLens() != lens) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+bool loadDynamicProfileEntries(std::vector &out)
+{
+ out.clear();
+ Glib::KeyFile kf;
+ if (!kf.load_from_file(
+ Glib::build_filename(Options::rtdir, "dynamicprofile.cfg"))) {
+ return false;
+ }
+ printf("loading dynamic profiles...\n");
+ auto groups = kf.get_groups();
+ for (auto group : groups) {
+ // groups are of the form "entry N", where N is a positive integer
+ if (group.find("entry ") != 0) {
+ return false;
+ }
+ std::istringstream buf(group.c_str() + 6);
+ int serial = 0;
+ if (!(buf >> serial) || !buf.eof()) {
+ return false;
+ }
+ printf(" loading entry %d\n", serial);
+
+ out.emplace_back(DynamicProfileEntry());
+ DynamicProfileEntry &entry = out.back();
+ entry.serial_number = serial;
+ entry.has_iso = kf.get_boolean(group, "has_iso");
+ entry.iso_min = kf.get_integer(group, "iso_min");
+ entry.iso_max = kf.get_integer(group, "iso_max");
+
+ entry.has_fnumber = kf.get_boolean(group, "has_fnumber");
+ entry.fnumber_min = kf.get_double(group, "fnumber_min");
+ entry.fnumber_max = kf.get_double(group, "fnumber_max");
+
+ entry.has_focallen = kf.get_boolean(group, "has_focallen");
+ entry.focallen_min = kf.get_double(group, "focallen_min");
+ entry.focallen_max = kf.get_double(group, "focallen_max");
+
+ entry.has_shutterspeed = kf.get_boolean(group, "has_shutterspeed");
+ entry.shutterspeed_min = kf.get_double(group, "shutterspeed_min");
+ entry.shutterspeed_max = kf.get_double(group, "shutterspeed_max");
+
+ entry.has_expcomp = kf.get_boolean(group, "has_expcomp");
+ entry.expcomp_min = kf.get_double(group, "expcomp_min");
+ entry.expcomp_max = kf.get_double(group, "expcomp_max");
+
+ entry.has_make = kf.get_boolean(group, "has_make");
+ entry.make = kf.get_string(group, "make");
+
+ entry.has_model = kf.get_boolean(group, "has_model");
+ entry.model = kf.get_string(group, "model");
+
+ entry.has_lens = kf.get_boolean(group, "has_lens");
+ entry.lens = kf.get_string(group, "lens");
+
+ entry.profilepath = kf.get_string(group, "profilepath");
+ }
+ std::sort(out.begin(), out.end());
+ return true;
+}
+
+
+PartialProfile *loadDynamicProfile(const ImageMetaData *im)
+{
+ PartialProfile *ret = new PartialProfile(true, true);
+ std::vector entries;
+ if (loadDynamicProfileEntries(entries)) {
+ for (auto &entry : entries) {
+ if (entry.matches(im)) {
+ printf("found matching profile %s\n",
+ entry.profilepath.c_str());
+ PartialProfile p(true, true);
+ if (!p.load(options.findProfilePath(entry.profilepath))) {
+ p.applyTo(ret->pparams);
+ } else {
+ printf("ERROR loading matching profile\n");
+ }
+ p.deleteInstance();
+ }
+ }
+ }
+ return ret;
+}
+
diff --git a/rtgui/dynamicprofile.h b/rtgui/dynamicprofile.h
new file mode 100644
index 000000000..5d8596c77
--- /dev/null
+++ b/rtgui/dynamicprofile.h
@@ -0,0 +1,73 @@
+/* -*- C++ -*-
+ * This file is part of RawTherapee.
+ *
+ * Copyright (c) 2017 Alberto Griggio
+ *
+ * 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 _DYNAMICPROFILE_H_
+#define _DYNAMICPROFILE_H_
+
+#include
+
+#include "options.h"
+
+
+class DynamicProfileEntry {
+public:
+ DynamicProfileEntry();
+ bool matches(const rtengine::ImageMetaData *im);
+ bool operator<(const DynamicProfileEntry &other) const;
+
+ int serial_number;
+
+ bool has_iso;
+ int iso_min;
+ int iso_max;
+
+ bool has_fnumber;
+ double fnumber_min;
+ double fnumber_max;
+
+ bool has_focallen;
+ double focallen_min;
+ double focallen_max;
+
+ bool has_shutterspeed;
+ double shutterspeed_min;
+ double shutterspeed_max;
+
+ bool has_expcomp;
+ double expcomp_min;
+ double expcomp_max;
+
+ bool has_make;
+ std::string make;
+
+ bool has_model;
+ std::string model;
+
+ bool has_lens;
+ std::string lens;
+
+ Glib::ustring profilepath;
+};
+
+
+bool loadDynamicProfileEntries(std::vector &out);
+rtengine::procparams::PartialProfile *loadDynamicProfile(
+ const rtengine::ImageMetaData *im);
+
+
+#endif // _DYNAMICPROFILE_H_
diff --git a/rtgui/main.cc b/rtgui/main.cc
index d6b99fdc6..ee3d3210e 100644
--- a/rtgui/main.cc
+++ b/rtgui/main.cc
@@ -37,6 +37,7 @@
#include "rtimage.h"
#include "version.h"
#include "extprog.h"
+#include "dynamicprofile.h"
#ifndef WIN32
#include
@@ -710,7 +711,7 @@ int processLineParams( int argc, char **argv )
rawParams = new rtengine::procparams::PartialProfile(true, true);
Glib::ustring profPath = options.findProfilePath(options.defProfRaw);
- if (options.is_defProfRawMissing() || profPath.empty() || rawParams->load(profPath == DEFPROFILE_INTERNAL ? DEFPROFILE_INTERNAL : Glib::build_filename(profPath, options.defProfRaw.substr(5) + paramFileExtension))) {
+ 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;
@@ -721,7 +722,7 @@ int processLineParams( int argc, char **argv )
imgParams = new rtengine::procparams::PartialProfile(true);
profPath = options.findProfilePath(options.defProfImg);
- if (options.is_defProfImgMissing() || profPath.empty() || imgParams->load(profPath == DEFPROFILE_INTERNAL ? DEFPROFILE_INTERNAL : Glib::build_filename(profPath, options.defProfImg.substr(5) + paramFileExtension))) {
+ 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;
@@ -793,9 +794,19 @@ int processLineParams( int argc, char **argv )
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);
}
diff --git a/rtgui/options.cc b/rtgui/options.cc
index 5c0c032eb..2f71a7106 100644
--- a/rtgui/options.cc
+++ b/rtgui/options.cc
@@ -241,6 +241,10 @@ Glib::ustring Options::findProfilePath (Glib::ustring &profName)
return profName;
}
+ if (profName == DEFPROFILE_DYNAMIC) {
+ return profName;
+ }
+
Glib::ustring p = profName.substr (0, 4);
if (p == "${U}") {
diff --git a/rtgui/options.h b/rtgui/options.h
index 43f64a4df..efc649417 100644
--- a/rtgui/options.h
+++ b/rtgui/options.h
@@ -39,6 +39,8 @@
#define DEFPROFILE_IMG "Neutral"
// Profile name to use for internal values' profile
#define DEFPROFILE_INTERNAL "Neutral"
+// Special name for the Dynamic profile
+#define DEFPROFILE_DYNAMIC "Dynamic"
class SaveFormat
{
diff --git a/rtgui/profilestore.cc b/rtgui/profilestore.cc
index 6b1171d23..9f32b735f 100644
--- a/rtgui/profilestore.cc
+++ b/rtgui/profilestore.cc
@@ -26,7 +26,7 @@ ProfileStore profileStore;
using namespace rtengine;
using namespace rtengine::procparams;
-ProfileStore::ProfileStore () : parseMutex(nullptr), storeState(STORESTATE_NOTINITIALIZED), internalDefaultProfile(nullptr), internalDefaultEntry(nullptr)
+ProfileStore::ProfileStore () : parseMutex(nullptr), storeState(STORESTATE_NOTINITIALIZED), internalDefaultProfile(nullptr), internalDefaultEntry(nullptr), internalDynamicEntry(nullptr)
{
internalDefaultProfile = new AutoPartialProfile();
internalDefaultProfile->set(true);
@@ -63,6 +63,8 @@ ProfileStore::~ProfileStore ()
partProfiles.clear ();
clearFileList();
delete internalDefaultProfile;
+ delete internalDefaultEntry;
+ delete internalDynamicEntry;
lock.release();
delete parseMutex;
parseMutex = nullptr;
@@ -140,6 +142,10 @@ void ProfileStore::_parseProfiles ()
entries.push_back(internalDefaultEntry);
partProfiles[internalDefaultEntry] = internalDefaultProfile;
+ if (!internalDynamicEntry) {
+ internalDynamicEntry = new ProfileStoreEntry(Glib::ustring("(") + M("PROFILEPANEL_PDYNAMIC") + Glib::ustring(")"), PSET_FILE, 0, 0);
+ // do not add it to the entries. This is here only for the preferences dialog
+ }
// Check if the default profiles has been found.
if (findEntryFromFullPathU(options.defProfRaw) == nullptr) {
@@ -273,7 +279,7 @@ const ProfileStoreEntry* ProfileStore::findEntryFromFullPathU(Glib::ustring path
return nullptr;
}
- if (path == DEFPROFILE_INTERNAL) {
+ if (path == DEFPROFILE_INTERNAL || path == DEFPROFILE_DYNAMIC) {
return internalDefaultEntry;
}
@@ -603,7 +609,6 @@ void ProfileStoreComboBox::refreshProfileList_ (Gtk::TreeModel::Row *parentRow,
*/
void ProfileStoreComboBox::updateProfileList ()
{
-
// clear items
clear();
refTreeModel.clear();
@@ -622,6 +627,7 @@ void ProfileStoreComboBox::updateProfileList ()
// special case for the Internal default entry
addRow(profileStore.getInternalDefaultPSE());
}
+ addRow(profileStore.getInternalDynamicPSE());
// releasing the profilestore's entry list mutex
profileStore.releaseFileList();
@@ -710,6 +716,11 @@ Gtk::TreeIter ProfileStoreComboBox::findRowFromFullPath (Glib::ustring path)
return row;
}
+ if (path == DEFPROFILE_DYNAMIC) {
+ row = findRowFromEntry(profileStore.getInternalDynamicPSE());
+ return row;
+ }
+
// removing the filename
Glib::ustring fName = Glib::path_get_basename(path);
@@ -758,6 +769,10 @@ Glib::ustring ProfileStoreComboBox::getFullPathFromActiveRow()
return Glib::ustring(DEFPROFILE_INTERNAL);
}
+ if (currEntry == profileStore.getInternalDynamicPSE()) {
+ return Glib::ustring(DEFPROFILE_DYNAMIC);
+ }
+
path = Glib::build_filename(profileStore.getPathFromId(currEntry->parentFolderId), currEntry->label);
}
diff --git a/rtgui/profilestore.h b/rtgui/profilestore.h
index ab45a0867..5e5591e15 100644
--- a/rtgui/profilestore.h
+++ b/rtgui/profilestore.h
@@ -144,6 +144,7 @@ private:
StoreState storeState;
rtengine::procparams::AutoPartialProfile *internalDefaultProfile;
ProfileStoreEntry *internalDefaultEntry;
+ ProfileStoreEntry *internalDynamicEntry;
/** Alphabetically ordered list of folder and files through Gtk::Label sub-class;
* ready to be used in Menu and Combobox
@@ -198,6 +199,11 @@ public:
return internalDefaultEntry;
}
+ const ProfileStoreEntry* getInternalDynamicPSE()
+ {
+ return internalDynamicEntry;
+ }
+
void addListener(ProfileStoreListener *listener);
void removeListener(ProfileStoreListener *listener);
diff --git a/rtgui/thumbnail.cc b/rtgui/thumbnail.cc
index 03ef49a5e..b560c5f7b 100644
--- a/rtgui/thumbnail.cc
+++ b/rtgui/thumbnail.cc
@@ -30,6 +30,7 @@
#include "profilestore.h"
#include "batchqueue.h"
#include "extprog.h"
+#include "dynamicprofile.h"
using namespace rtengine::procparams;
@@ -217,7 +218,23 @@ rtengine::procparams::ProcParams* Thumbnail::createProcParamsForUpdate(bool retu
const CacheImageData* cfs = getCacheImageData();
Glib::ustring defaultPparamsPath = options.findProfilePath(defProf);
- if (!options.CPBPath.empty() && !defaultPparamsPath.empty() && (!hasProcParams() || forceCPB) && cfs && cfs->exifValid) {
+ if (defProf == DEFPROFILE_DYNAMIC && (!hasProcParams() || forceCPB) && cfs && cfs->exifValid) {
+ rtengine::ImageMetaData* imageMetaData;
+ if (getType() == FT_Raw) {
+ rtengine::RawMetaDataLocation metaData = rtengine::Thumbnail::loadMetaDataFromRaw(fname);
+ imageMetaData = rtengine::ImageMetaData::fromFile (fname, &metaData);
+ } else {
+ imageMetaData = rtengine::ImageMetaData::fromFile (fname, nullptr);
+ }
+ PartialProfile *pp = loadDynamicProfile(imageMetaData);
+ if (options.paramsLoadLocation == PLL_Input) {
+ pp->pparams->save(fname + paramFileExtension);
+ } else {
+ pp->pparams->save(getCacheFileName ("profiles", paramFileExtension));
+ }
+ pp->deleteInstance();
+ delete pp;
+ } else if (defProf != DEFPROFILE_DYNAMIC && !options.CPBPath.empty() && !defaultPparamsPath.empty() && (!hasProcParams() || forceCPB) && cfs && cfs->exifValid) {
// First generate the communication file, with general values and EXIF metadata
rtengine::ImageMetaData* imageMetaData;