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;