1 /*
2  *  This file is part of RawTherapee.
3  *
4  *  Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
5  *
6  *  RawTherapee is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  RawTherapee is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with RawTherapee.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <map>
21 #include <iterator>
22 #include <iostream>
23 
24 #include <locale.h>
25 
26 #include <glib/gstdio.h>
27 
28 #include <giomm.h>
29 #include <sstream>
30 #include <fstream>
31 
32 #include "curves.h"
33 #include "procparams.h"
34 #include "rtengine.h"
35 #include "metadata.h"
36 #include "halffloat.h"
37 #include "base64.h"
38 
39 #include "../rtgui/multilangmgr.h"
40 #include "../rtgui/options.h"
41 #include "../rtgui/paramsedited.h"
42 #include "../rtgui/ppversion.h"
43 #include "../rtgui/version.h"
44 
45 using namespace std;
46 
47 namespace rtengine { namespace procparams {
48 
49 namespace {
50 
compress(const std::string & src)51 std::vector<uint8_t> compress(const std::string &src)
52 {
53     auto s = Gio::MemoryOutputStream::create(nullptr, 0, g_realloc, g_free);
54     auto c = Gio::ZlibCompressor::create(Gio::ZLIB_COMPRESSOR_FORMAT_RAW, -1);
55     std::vector<uint8_t> res;
56     {
57         auto stream = Gio::ConverterOutputStream::create(s, c);
58         stream->set_close_base_stream(true);
59         gsize n;
60         if (!stream->write_all(src, n)) {
61             return res;
62         }
63     }
64     char *data = static_cast<char *>(s->get_data());
65     for (size_t i = 0, n = s->get_data_size(); i < n; ++i) {
66         res.push_back(data[i]);
67     }
68     return res;
69 }
70 
71 
decompress(const std::vector<uint8_t> & src)72 std::string decompress(const std::vector<uint8_t> &src)
73 {
74     auto s = Gio::MemoryOutputStream::create(nullptr, 0, g_realloc, g_free);
75     auto c = Gio::ZlibDecompressor::create(Gio::ZLIB_COMPRESSOR_FORMAT_RAW);
76     std::vector<char> res;
77     {
78         auto stream = Gio::ConverterOutputStream::create(s, c);
79         stream->set_close_base_stream(true);
80         constexpr gsize chunk = 512;
81         size_t i = 0;
82         while (i < src.size()) {
83             gsize count = std::min(src.size() - i, chunk);
84             auto n = stream->write(&(src[i]), count);
85             if (n < 0) {
86                 return "";
87             } else if (n == 0) {
88                 break;
89             }
90             i += n;
91         }
92         // gsize n;
93         // if (!stream->write_all(&(src[0]), src.size(), n)) {
94         //     return "";
95         // }
96     }
97     char *data = static_cast<char *>(s->get_data());
98     for (size_t i = 0, n = s->get_data_size(); i < n; ++i) {
99         res.push_back(data[i]);
100     }
101     res.push_back(0);
102     return std::string(&(res[0]));
103 }
104 
105 class ConversionError: public std::exception {
what() const106     const char *what() const noexcept { return "base64 error"; }
107 };
108 
to_xmp(const Glib::ustring & data)109 std::string to_xmp(const Glib::ustring &data)
110 {
111     auto res = compress(data.raw());
112     return base64encode(res);
113 }
114 
115 
from_xmp(const std::string & data)116 Glib::ustring from_xmp(const std::string &data)
117 {
118     try {
119         auto buf = base64decode(data);
120         return decompress(buf);
121     } catch (std::exception &e) {
122         throw ConversionError();
123     }
124 }
125 
unpack_list(const std::string & data)126 std::vector<double> unpack_list(const std::string &data)
127 {
128     std::vector<double> ret;
129     if (data.empty()) {
130         return ret;
131     }
132     std::vector<uint8_t> buf;
133     try {
134         buf = base64decode(data);
135     } catch (std::exception &exc) {
136         throw ConversionError();
137     }
138     Exiv2::byte *p = reinterpret_cast<Exiv2::byte *>(&(buf[0]));
139     for (size_t i = 0; i < buf.size(); i += sizeof(uint16_t)) {
140         uint16_t v = Exiv2::getUShort(p + i, Exiv2::littleEndian);
141         float f = DNG_HalfToFloat(v);
142         ret.push_back(f);
143     }
144     return ret;
145 }
146 
147 
pack_list(const std::vector<double> & data)148 std::string pack_list(const std::vector<double> &data)
149 {
150     if (data.empty()) {
151         return "";
152     }
153     std::vector<uint8_t> bytes(data.size() * sizeof(uint16_t));
154     auto p = &(bytes[0]);
155     size_t off = 0;
156     for (auto f : data) {
157         uint16_t v = DNG_FloatToHalf(f);
158         long o = Exiv2::us2Data(p + off, v, Exiv2::littleEndian);
159         off += o;
160     }
161     return base64encode(bytes);
162 }
163 
164 } // namespace
165 
166 
167 const short SpotParams::minRadius = 2;
168 const short SpotParams::maxRadius = 200;
169 
170 //-----------------------------------------------------------------------------
171 // KeyFile
172 //-----------------------------------------------------------------------------
173 
has_group(const Glib::ustring & grp) const174 bool KeyFile::has_group(const Glib::ustring &grp) const
175 {
176     return kf_.has_group(GRP(grp));
177 }
178 
179 
has_key(const Glib::ustring & grp,const Glib::ustring & key) const180 bool KeyFile::has_key(const Glib::ustring &grp, const Glib::ustring &key) const
181 {
182     return kf_.has_key(GRP(grp), key);
183 }
184 
185 
get_keys(const Glib::ustring & grp) const186 Glib::ArrayHandle<Glib::ustring> KeyFile::get_keys(const Glib::ustring &grp) const
187 {
188     return kf_.get_keys(GRP(grp));
189 }
190 
191 
get_string(const Glib::ustring & grp,const Glib::ustring & key) const192 Glib::ustring KeyFile::get_string(const Glib::ustring &grp, const Glib::ustring &key) const
193 {
194     return kf_.get_string(GRP(grp), key);
195 }
196 
197 
get_integer(const Glib::ustring & grp,const Glib::ustring & key) const198 int KeyFile::get_integer(const Glib::ustring &grp, const Glib::ustring &key) const
199 {
200     return kf_.get_integer(GRP(grp), key);
201 }
202 
203 
get_double(const Glib::ustring & grp,const Glib::ustring & key) const204 double KeyFile::get_double(const Glib::ustring &grp, const Glib::ustring &key) const
205 {
206     return kf_.get_double(GRP(grp), key);
207 }
208 
209 
get_boolean(const Glib::ustring & grp,const Glib::ustring & key) const210 bool KeyFile::get_boolean(const Glib::ustring &grp, const Glib::ustring &key) const
211 {
212     return kf_.get_boolean(GRP(grp), key);
213 }
214 
215 
get_string_list(const Glib::ustring & grp,const Glib::ustring & key) const216 Glib::ArrayHandle<Glib::ustring> KeyFile::get_string_list(const Glib::ustring &grp, const Glib::ustring &key) const
217 {
218     return kf_.get_string_list(GRP(grp), key);
219 }
220 
221 
get_integer_list(const Glib::ustring & grp,const Glib::ustring & key) const222 Glib::ArrayHandle<int> KeyFile::get_integer_list(const Glib::ustring &grp, const Glib::ustring &key) const
223 {
224     return kf_.get_integer_list(GRP(grp), key);
225 }
226 
227 
get_double_list(const Glib::ustring & grp,const Glib::ustring & key) const228 Glib::ArrayHandle<double> KeyFile::get_double_list(const Glib::ustring &grp, const Glib::ustring &key) const
229 {
230     return kf_.get_double_list(GRP(grp), key);
231 }
232 
233 
set_string(const Glib::ustring & grp,const Glib::ustring & key,const Glib::ustring & string)234 void KeyFile::set_string(const Glib::ustring &grp, const Glib::ustring &key, const Glib::ustring &string)
235 {
236     kf_.set_string(GRP(grp), key, string);
237 }
238 
239 
set_boolean(const Glib::ustring & grp,const Glib::ustring & key,bool value)240 void KeyFile::set_boolean(const Glib::ustring &grp, const Glib::ustring &key, bool value)
241 {
242     kf_.set_boolean(GRP(grp), key, value);
243 }
244 
245 
set_integer(const Glib::ustring & grp,const Glib::ustring & key,int value)246 void KeyFile::set_integer(const Glib::ustring &grp, const Glib::ustring &key, int value)
247 {
248     kf_.set_integer(GRP(grp), key, value);
249 }
250 
251 
set_double(const Glib::ustring & grp,const Glib::ustring & key,double value)252 void KeyFile::set_double(const Glib::ustring &grp, const Glib::ustring &key, double value)
253 {
254     kf_.set_double(GRP(grp), key, value);
255 }
256 
257 
set_string_list(const Glib::ustring & grp,const Glib::ustring & key,const Glib::ArrayHandle<Glib::ustring> & list)258 void KeyFile::set_string_list(const Glib::ustring &grp, const Glib::ustring &key, const Glib::ArrayHandle<Glib::ustring> &list)
259 {
260     kf_.set_string_list(GRP(grp), key, list);
261 }
262 
263 
set_integer_list(const Glib::ustring & grp,const Glib::ustring & key,const Glib::ArrayHandle<int> & list)264 void KeyFile::set_integer_list(const Glib::ustring &grp, const Glib::ustring &key, const Glib::ArrayHandle<int> &list)
265 {
266     kf_.set_integer_list(GRP(grp), key, list);
267 }
268 
269 
set_double_list(const Glib::ustring & grp,const Glib::ustring & key,const Glib::ArrayHandle<double> & list)270 void KeyFile::set_double_list(const Glib::ustring &grp, const Glib::ustring &key, const Glib::ArrayHandle<double> &list)
271 {
272     kf_.set_double_list(GRP(grp), key, list);
273 }
274 
275 
load_from_file(const Glib::ustring & fn)276 bool KeyFile::load_from_file(const Glib::ustring &fn)
277 {
278     filename_ = fn;
279     return kf_.load_from_file(fn);
280 }
281 
282 
load_from_data(const Glib::ustring & data)283 bool KeyFile::load_from_data(const Glib::ustring &data)
284 {
285     return kf_.load_from_data(data);
286 }
287 
288 
to_data()289 Glib::ustring KeyFile::to_data()
290 {
291     return kf_.to_data();
292 }
293 
294 
295 namespace {
296 
expandRelativePath(const Glib::ustring & procparams_fname,const Glib::ustring & prefix,Glib::ustring embedded_fname)297 Glib::ustring expandRelativePath(const Glib::ustring &procparams_fname, const Glib::ustring &prefix, Glib::ustring embedded_fname)
298 {
299     if (embedded_fname == "" || !Glib::path_is_absolute(procparams_fname)) {
300         return embedded_fname;
301     }
302 
303     if (prefix != "") {
304         if (embedded_fname.length() < prefix.length() || embedded_fname.substr(0, prefix.length()) != prefix) {
305             return embedded_fname;
306         }
307 
308         embedded_fname = embedded_fname.substr(prefix.length());
309     }
310 
311     if (Glib::path_is_absolute(embedded_fname)) {
312         return prefix + embedded_fname;
313     }
314 
315     Glib::ustring absPath = prefix + Glib::path_get_dirname(procparams_fname) + G_DIR_SEPARATOR_S + embedded_fname;
316     return absPath;
317 }
318 
319 
getFromKeyfile(const KeyFile & keyfile,const Glib::ustring & group_name,const Glib::ustring & key,int & value)320 void getFromKeyfile(
321     const KeyFile& keyfile,
322     const Glib::ustring& group_name,
323     const Glib::ustring& key,
324     int& value
325 )
326 {
327     value = keyfile.get_integer(group_name, key);
328 }
329 
getFromKeyfile(const KeyFile & keyfile,const Glib::ustring & group_name,const Glib::ustring & key,double & value)330 void getFromKeyfile(
331     const KeyFile& keyfile,
332     const Glib::ustring& group_name,
333     const Glib::ustring& key,
334     double& value
335 )
336 {
337     value = keyfile.get_double(group_name, key);
338 }
339 
getFromKeyfile(const KeyFile & keyfile,const Glib::ustring & group_name,const Glib::ustring & key,bool & value)340 void getFromKeyfile(
341     const KeyFile& keyfile,
342     const Glib::ustring& group_name,
343     const Glib::ustring& key,
344     bool& value
345 )
346 {
347     try {
348         value = keyfile.get_boolean(group_name, key);
349     } catch (Glib::KeyFileError &e) {
350         int v = keyfile.get_integer(group_name, key);
351         value = v;
352     }
353 }
354 
getFromKeyfile(const KeyFile & keyfile,const Glib::ustring & group_name,const Glib::ustring & key,Glib::ustring & value)355 void getFromKeyfile(
356     const KeyFile& keyfile,
357     const Glib::ustring& group_name,
358     const Glib::ustring& key,
359     Glib::ustring& value
360 )
361 {
362     value = keyfile.get_string(group_name, key);
363 }
364 
getFromKeyfile(const KeyFile & keyfile,const Glib::ustring & group_name,const Glib::ustring & key,std::vector<double> & value)365 void getFromKeyfile(
366     const KeyFile& keyfile,
367     const Glib::ustring& group_name,
368     const Glib::ustring& key,
369     std::vector<double>& value
370 )
371 {
372     value = keyfile.get_double_list(group_name, key);
373     rtengine::sanitizeCurve(value);
374 }
375 
getFromKeyfile(const KeyFile & keyfile,const Glib::ustring & group_name,const Glib::ustring & key,std::vector<float> & value)376 void getFromKeyfile(
377     const KeyFile& keyfile,
378     const Glib::ustring& group_name,
379     const Glib::ustring& key,
380     std::vector<float>& value
381 )
382 {
383     std::vector<double> tmpval = keyfile.get_double_list(group_name, key);
384     value.assign(tmpval.begin(), tmpval.end());
385 }
386 
getFromKeyfile(const KeyFile & keyfile,const Glib::ustring & group_name,const Glib::ustring & key,std::vector<std::string> & value)387 void getFromKeyfile(
388     const KeyFile& keyfile,
389     const Glib::ustring& group_name,
390     const Glib::ustring& key,
391     std::vector<std::string>& value
392 )
393 {
394     auto tmpval = keyfile.get_string_list(group_name, key);
395     value.assign(tmpval.begin(), tmpval.end());
396 }
397 
398 template<typename T>
assignFromKeyfile(const KeyFile & keyfile,const Glib::ustring & group_name,const Glib::ustring & key,T & value)399 bool assignFromKeyfile(const KeyFile& keyfile, const Glib::ustring& group_name, const Glib::ustring& key, T &value)
400 {
401     try {
402         if (keyfile.has_key(group_name, key)) {
403             getFromKeyfile(keyfile, group_name, key, value);
404 
405             return true;
406         }
407     } catch (Glib::KeyFileError &exc) {
408         auto pl = keyfile.progressListener();
409         if (pl) {
410             pl->error(Glib::ustring::compose("WARNING: %1: %2", keyfile.filename(), exc.what()));
411             return false;
412         } else {
413             throw exc;
414         }
415     }
416 
417     return false;
418 }
419 
420 template<typename T, typename = typename std::enable_if<std::is_enum<T>::value>::type>
assignFromKeyfile(const KeyFile & keyfile,const Glib::ustring & group_name,const Glib::ustring & key,const std::map<std::string,T> & mapping,T & value)421 bool assignFromKeyfile(const KeyFile& keyfile, const Glib::ustring& group_name, const Glib::ustring& key, const std::map<std::string, T>& mapping, T& value)
422 {
423     try {
424         if (keyfile.has_key(group_name, key)) {
425             Glib::ustring v;
426             getFromKeyfile(keyfile, group_name, key, v);
427 
428             const typename std::map<std::string, T>::const_iterator m = mapping.find(v);
429 
430             if (m != mapping.end()) {
431                 value = m->second;
432             } else {
433                 return false;
434             }
435 
436             return true;
437         }
438     } catch (Glib::KeyFileError &exc) {
439         auto pl = keyfile.progressListener();
440         if (pl) {
441             pl->error(Glib::ustring::compose("WARNING: %1: %2", keyfile.filename(), exc.what()));
442             return false;
443         } else {
444             throw exc;
445         }
446     }
447 
448     return false;
449 }
450 
putToKeyfile(const Glib::ustring & group_name,const Glib::ustring & key,int value,KeyFile & keyfile)451 void putToKeyfile(
452     const Glib::ustring& group_name,
453     const Glib::ustring& key,
454     int value,
455     KeyFile& keyfile
456 )
457 {
458     keyfile.set_integer(group_name, key, value);
459 }
460 
putToKeyfile(const Glib::ustring & group_name,const Glib::ustring & key,double value,KeyFile & keyfile)461 void putToKeyfile(
462     const Glib::ustring& group_name,
463     const Glib::ustring& key,
464     double value,
465     KeyFile& keyfile
466 )
467 {
468     keyfile.set_double(group_name, key, value);
469 }
470 
putToKeyfile(const Glib::ustring & group_name,const Glib::ustring & key,bool value,KeyFile & keyfile)471 void putToKeyfile(
472     const Glib::ustring& group_name,
473     const Glib::ustring& key,
474     bool value,
475     KeyFile& keyfile
476 )
477 {
478     keyfile.set_boolean(group_name, key, value);
479 }
480 
putToKeyfile(const Glib::ustring & group_name,const Glib::ustring & key,const Glib::ustring & value,KeyFile & keyfile)481 void putToKeyfile(
482     const Glib::ustring& group_name,
483     const Glib::ustring& key,
484     const Glib::ustring& value,
485     KeyFile& keyfile
486 )
487 {
488     keyfile.set_string(group_name, key, value);
489 }
490 
putToKeyfile(const Glib::ustring & group_name,const Glib::ustring & key,const std::vector<int> & value,KeyFile & keyfile)491 void putToKeyfile(
492     const Glib::ustring& group_name,
493     const Glib::ustring& key,
494     const std::vector<int>& value,
495     KeyFile& keyfile
496 )
497 {
498     const Glib::ArrayHandle<int> list = value;
499     keyfile.set_integer_list(group_name, key, list);
500 }
501 
putToKeyfile(const Glib::ustring & group_name,const Glib::ustring & key,const std::vector<double> & value,KeyFile & keyfile)502 void putToKeyfile(
503     const Glib::ustring& group_name,
504     const Glib::ustring& key,
505     const std::vector<double>& value,
506     KeyFile& keyfile
507 )
508 {
509     const Glib::ArrayHandle<double> list = value;
510     keyfile.set_double_list(group_name, key, list);
511 }
512 
putToKeyfile(const Glib::ustring & group_name,const Glib::ustring & key,const std::vector<std::string> & value,KeyFile & keyfile)513 void putToKeyfile(
514     const Glib::ustring& group_name,
515     const Glib::ustring& key,
516     const std::vector<std::string>& value,
517     KeyFile& keyfile
518 )
519 {
520     const Glib::ArrayHandle<Glib::ustring> list = value;
521     keyfile.set_string_list(group_name, key, list);
522 }
523 
524 template<typename T>
saveToKeyfile(const Glib::ustring & group_name,const Glib::ustring & key,const T & value,KeyFile & keyfile)525 bool saveToKeyfile(
526     const Glib::ustring& group_name,
527     const Glib::ustring& key,
528     const T& value,
529     KeyFile& keyfile
530 )
531 {
532     putToKeyfile(group_name, key, value, keyfile);
533     return true;
534 }
535 
536 template<typename T, typename = typename std::enable_if<std::is_enum<T>::value>::type>
saveToKeyfile(const Glib::ustring & group_name,const Glib::ustring & key,const std::map<T,const char * > & mapping,const T & value,KeyFile & keyfile)537 bool saveToKeyfile(
538     const Glib::ustring& group_name,
539     const Glib::ustring& key,
540     const std::map<T, const char*>& mapping,
541     const T& value,
542     KeyFile& keyfile
543 )
544 {
545     const typename std::map<T, const char*>::const_iterator m = mapping.find(value);
546 
547     if (m != mapping.end()) {
548         keyfile.set_string(group_name, key, m->second);
549         return true;
550     }
551 
552     return false;
553 }
554 
555 
filenameToUri(const Glib::ustring & fname,const Glib::ustring & basedir)556 Glib::ustring filenameToUri(const Glib::ustring &fname, const Glib::ustring &basedir)
557 {
558     if (fname.empty()) {
559         return fname;
560     }
561 
562     const auto stripif =
563         [](std::string &p, const Glib::ustring &d) -> bool
564         {
565             std::string dd = Glib::filename_from_utf8(d + G_DIR_SEPARATOR_S);
566             if (p.substr(0, dd.length()) == dd) {
567                 p = std::string(G_DIR_SEPARATOR_S) + p.substr(dd.length());
568                 return true;
569             }
570             return false;
571         };
572 
573     try {
574         auto fn = Glib::filename_from_utf8(fname);
575         if (Glib::path_is_absolute(fname)) {
576             if (stripif(fn, argv0)) {
577                 return Glib::filename_to_uri(fn, "S");
578             } else if (stripif(fn, options.rtdir)) {
579                 return Glib::filename_to_uri(fn, "U");
580             } else if (Glib::path_get_dirname(fname) == basedir) {
581                 fn = Glib::filename_to_utf8(Glib::path_get_basename(fname));
582                 return Glib::filename_to_uri(fn, "B");
583             } else {
584                 return Glib::filename_to_uri(fn);
585             }
586         } else {
587             return Glib::filename_to_uri(Glib::ustring(G_DIR_SEPARATOR_S) + fn, "R");
588         }
589     } catch (Glib::ConvertError &) {
590         return fname;
591     }
592 }
593 
594 
filenameFromUri(const Glib::ustring & uri,const Glib::ustring & basedir)595 Glib::ustring filenameFromUri(const Glib::ustring &uri, const Glib::ustring &basedir)
596 {
597     if (uri.empty()) {
598         return uri;
599     }
600 
601     try {
602         // Glib::filename_from_uri seems to have troubles on windows (leads to
603         // crashes), so we use the C function directly
604         gchar *h = nullptr;
605         gchar *fn = g_filename_from_uri(uri.c_str(), &h, nullptr);
606         if (!fn) {
607             return uri;
608         }
609         std::string f(fn);
610         g_free(fn);
611         if (h) {
612             std::string hn(h);
613             g_free(h);
614             f = f.substr(1);
615             if (hn == "U") {
616                 f = Glib::build_filename(Glib::filename_from_utf8(options.rtdir), f);
617             } else if (hn == "S") {
618                 f = Glib::build_filename(Glib::filename_from_utf8(argv0), f);
619             } else if (hn == "B") {
620                 f = Glib::build_filename(Glib::filename_from_utf8(basedir), f);
621             } else if (hn != "R") {
622                 return uri;
623             }
624         }
625         Glib::ustring ret = Glib::filename_to_utf8(f);
626         return ret;
627     } catch (Glib::ConvertError &e) {
628         return uri;
629     }
630 }
631 
632 } // namespace
633 
634 
635 
Shape()636 AreaMask::Shape::Shape():
637     mode(ADD),
638     feather(0.),
639     blur(0.)
640 {
641 }
642 
Rectangle()643 AreaMask::Rectangle::Rectangle():
644     x(0.),
645     y(0.),
646     width(100.),
647     height(100.),
648     angle(0.),
649     roundness(0.)
650 {
651 }
652 
653 
Gradient()654 AreaMask::Gradient::Gradient():
655     x(0.),
656     y(0.),
657     strengthStart(100.),
658     strengthEnd(0.),
659     angle(0.)
660 {
661     feather = 25.;
662 }
663 
664 
clone() const665 std::unique_ptr<AreaMask::Shape> AreaMask::Rectangle::clone() const
666 {
667     return std::unique_ptr<Shape>(new Rectangle(*this));
668 }
669 
670 
Knot()671 AreaMask::Polygon::Knot::Knot():
672     x(0.),
673     y(0.),
674     roundness(0.)
675 {
676 }
677 
setPos(CoordD & pos)678 void AreaMask::Polygon::Knot::setPos(CoordD &pos)
679 {
680     x = pos.x;
681     y = pos.y;
682 }
683 
684 
clone() const685 std::unique_ptr<AreaMask::Shape> AreaMask::Polygon::clone() const
686 {
687     return std::unique_ptr<Shape>(new Polygon(*this));
688 }
689 
clone() const690 std::unique_ptr<AreaMask::Shape> AreaMask::Gradient::clone() const
691 {
692     return std::unique_ptr<Shape>(new Gradient(*this));
693 }
694 
operator ==(const Shape & other) const695 bool AreaMask::Shape::operator==(const Shape &other) const
696 {
697     return mode == other.mode
698            && feather == other.feather
699            && blur == other.blur;
700 }
701 
702 
operator !=(const Shape & other) const703 bool AreaMask::Shape::operator!=(const Shape &other) const
704 {
705     return !(*this == other);
706 }
707 
708 
operator ==(const Knot & other) const709 bool AreaMask::Polygon::Knot::operator==(const Knot &other) const
710 {
711     return
712         x == other.x
713         && y == other.y
714         && roundness == other.roundness;
715 }
716 
717 
toImgSpace(double v,int imSize)718 int AreaMask::Shape::toImgSpace(double v, int imSize)
719 {
720     double s2 = imSize / 2.;
721     return s2 + v / 100. * s2;
722 }
723 
724 
toParamRange(int v,int imSize)725 double AreaMask::Shape::toParamRange(int v, int imSize)
726 {
727     double s2 = imSize / 2.;
728     return (double(v) - s2) * 100. / s2;
729 }
730 
731 
knots_to_list(std::vector<double> & out) const732 void AreaMask::Polygon::knots_to_list(std::vector<double> &out) const
733 {
734     if (knots.empty()) {
735         out.clear();
736         return;
737     }
738 
739     out.resize(knots.size() * 3);
740 
741     for (size_t i = 0, j = 0; i < knots.size() ; ++i) {
742         out[j++] = knots[i].x;
743         out[j++] = knots[i].y;
744         out[j++] = knots[i].roundness;
745     }
746 }
747 
748 
knots_from_list(const std::vector<double> & v)749 void AreaMask::Polygon::knots_from_list(const std::vector<double> &v)
750 {
751     size_t size = v.size() / 3;
752     knots.resize(size);
753 
754     for (size_t i = 0, j = 0; i < size ; ++i) {
755         knots[i].x = v.at(j++);
756         knots[i].y = v.at(j++);
757         knots[i].roundness = v.at(j++);
758     }
759 }
760 
761 
operator !=(const Knot & other) const762 bool AreaMask::Polygon::Knot::operator!=(const Knot &other) const
763 {
764     return !(*this == other);
765 }
766 
767 
operator ==(const Shape & other) const768 bool AreaMask::Rectangle::operator==(const Shape &other) const
769 {
770     const Rectangle *o = dynamic_cast<const Rectangle *>(&other);
771     if (!o) {
772         return false;
773     }
774 
775     return
776         x == o->x
777         && y == o->y
778         && width == o->width
779         && height == o->height
780         && angle == o->angle
781         && roundness == o->roundness
782         && AreaMask::Shape::operator==(other);
783 }
784 
785 
operator !=(const Shape & other) const786 bool AreaMask::Rectangle::operator!=(const Shape &other) const
787 {
788     return !(*this == other);
789 }
790 
791 
operator ==(const Shape & other) const792 bool AreaMask::Polygon::operator==(const Shape &other) const
793 {
794     const Polygon *o = dynamic_cast<const Polygon *>(&other);
795     if (!o) {
796         return false;
797     }
798 
799     return
800         knots == o->knots
801         && AreaMask::Shape::operator==(other);
802 }
803 
804 
operator !=(const Shape & other) const805 bool AreaMask::Polygon::operator!=(const Shape &other) const
806 {
807     return !(*this == other);
808 }
809 
810 
operator ==(const Shape & other) const811 bool AreaMask::Gradient::operator==(const Shape &other) const
812 {
813     const Gradient *o = dynamic_cast<const Gradient *>(&other);
814     if (!o) {
815         return false;
816     }
817 
818     return
819         x == o->x
820         && y == o->y
821         && strengthStart == o->strengthStart
822         && strengthEnd == o->strengthEnd
823         && angle == o->angle
824         && AreaMask::Shape::operator==(other);
825 }
826 
827 
operator !=(const Shape & other) const828 bool AreaMask::Gradient::operator!=(const Shape &other) const
829 {
830     return !(*this == other);
831 }
832 
833 
AreaMask()834 AreaMask::AreaMask():
835     enabled(false),
836     feather(0),
837     blur(0),
838     contrast{DCT_Linear},
839     shapes{}
840 {
841 }
842 
843 
operator ==(const AreaMask & other) const844 bool AreaMask::operator==(const AreaMask &other) const
845 {
846     return enabled == other.enabled
847         && feather == other.feather
848         && blur == other.blur
849         && contrast == other.contrast
850         && shapes == other.shapes;
851 }
852 
853 
operator !=(const AreaMask & other) const854 bool AreaMask::operator!=(const AreaMask &other) const
855 {
856     return !(*this == other);
857 }
858 
859 
isTrivial() const860 bool AreaMask::isTrivial() const
861 {
862     AreaMask n;
863     n.enabled = true;
864     return (!enabled || *this == n);
865 }
866 
867 
AreaMask(const AreaMask & other)868 AreaMask::AreaMask(const AreaMask &other):
869     enabled(other.enabled),
870     feather(other.feather),
871     blur(other.blur),
872     contrast(other.contrast)
873 {
874     for (const auto &s : other.shapes) {
875         shapes.emplace_back(s->clone());
876     }
877 }
878 
879 
operator =(const AreaMask & other)880 AreaMask &AreaMask::operator=(const AreaMask &other)
881 {
882     enabled = other.enabled;
883     feather = other.feather;
884     blur = other.blur;
885     contrast = other.contrast;
886 
887     shapes.clear();
888     for (const auto &s : other.shapes) {
889         shapes.emplace_back(s->clone());
890     }
891     return *this;
892 }
893 
894 
DeltaEMask()895 DeltaEMask::DeltaEMask():
896     enabled(false),
897     L(0.0),
898     C(0.0),
899     H(0.0),
900     range(1.0),
901     decay(1),
902     weight_L(50),
903     weight_C(75),
904     weight_H(100)
905 {
906 }
907 
908 
operator ==(const DeltaEMask & other) const909 bool DeltaEMask::operator==(const DeltaEMask &other) const
910 {
911     return enabled == other.enabled
912         && L == other.L
913         && C == other.C
914         && H == other.H
915         && range == other.range
916         && decay == other.decay
917         && weight_L == other.weight_L
918         && weight_C == other.weight_C
919         && weight_H == other.weight_H;
920 }
921 
922 
operator !=(const DeltaEMask & other) const923 bool DeltaEMask::operator!=(const DeltaEMask &other) const
924 {
925     return !(*this == other);
926 }
927 
928 
Stroke()929 DrawnMask::Stroke::Stroke():
930     x(-1),
931     y(-1),
932     radius(0),
933     hardness(1),
934     erase(false)
935 {
936 }
937 
938 
operator ==(const Stroke & other) const939 bool DrawnMask::Stroke::operator==(const Stroke &other) const
940 {
941     return x == other.x
942         && y == other.y
943         && radius == other.radius
944         && erase == other.erase
945         && hardness == other.hardness;
946 }
947 
948 
operator !=(const Stroke & other) const949 bool DrawnMask::Stroke::operator!=(const Stroke &other) const
950 {
951     return !(*this == other);
952 }
953 
954 
DrawnMask()955 DrawnMask::DrawnMask():
956     enabled(false),
957     feather(0.0),
958     transparency(0),
959     smoothness(0),
960     contrast{DCT_Linear},
961     strokes(),
962     mode(INTERSECT)
963 {
964 }
965 
966 
operator ==(const DrawnMask & other) const967 bool DrawnMask::operator==(const DrawnMask &other) const
968 {
969     return enabled == other.enabled
970         && feather == other.feather
971         && transparency == other.transparency
972         && smoothness == other.smoothness
973         && contrast == other.contrast
974         && strokes == other.strokes
975         && mode == other.mode;
976 }
977 
978 
operator !=(const DrawnMask & other) const979 bool DrawnMask::operator!=(const DrawnMask &other) const
980 {
981     return !(*this == other);
982 }
983 
984 
isTrivial() const985 bool DrawnMask::isTrivial() const
986 {
987     return !enabled;
988 }
989 
990 
strokes_to_list(std::vector<double> & out) const991 void DrawnMask::strokes_to_list(std::vector<double> &out) const
992 {
993     if (strokes.empty()) {
994         return;
995     }
996 
997     const auto same =
998         [](const Stroke &a, const Stroke &b) -> bool
999         {
1000             return a.radius == b.radius
1001                 && a.erase == b.erase
1002                 && a.hardness == b.hardness;
1003         };
1004 
1005     auto cur = strokes[0];
1006     size_t pos = 0;
1007     while (true) {
1008         int n = 0;
1009         while (pos + n < strokes.size() && same(strokes[pos + n], cur)) {
1010             ++n;
1011         }
1012         out.push_back(n);
1013         out.push_back(cur.radius);
1014         out.push_back(int(!cur.erase));
1015         out.push_back(cur.hardness);
1016         for (int i = 0; i < n; ++i) {
1017             out.push_back(strokes[pos].x);
1018             out.push_back(strokes[pos].y);
1019             ++pos;
1020         }
1021         if (pos >= strokes.size()) {
1022             break;
1023         } else {
1024             cur = strokes[pos];
1025         }
1026     }
1027 }
1028 
1029 
strokes_from_list(const std::vector<double> & v)1030 void DrawnMask::strokes_from_list(const std::vector<double> &v)
1031 {
1032     strokes.clear();
1033     size_t pos = 0;
1034     while (pos + 4 < v.size()) {
1035         int n = v[pos++];
1036         Stroke s;
1037         s.radius = v[pos++];
1038         s.erase = !bool(v[pos++]);
1039         s.hardness = v[pos++];
1040         for (int i = 0; i < n && pos + 1 < v.size(); ++i) {
1041             strokes.push_back(s);
1042             strokes.back().x = v[pos++];
1043             strokes.back().y = v[pos++];
1044         }
1045     }
1046 }
1047 
1048 
ParametricMask()1049 ParametricMask::ParametricMask():
1050     enabled(false),
1051     blur(0),
1052     hue{
1053         FCT_MinMaxCPoints,
1054             0.166666667,
1055             1.,
1056             0.35,
1057             0.35,
1058             0.8287775246,
1059             1.,
1060             0.35,
1061             0.35
1062     },
1063     chromaticity{
1064         FCT_MinMaxCPoints,
1065             0.,
1066             1.,
1067             0.35,
1068             0.35,
1069             1.,
1070             1.,
1071             0.35,
1072             0.35
1073             },
1074     lightness{
1075         FCT_MinMaxCPoints,
1076             0.,
1077             1.,
1078             0.35,
1079             0.35,
1080             1.,
1081             1.,
1082             0.35,
1083             0.35
1084             },
1085     lightnessDetail(0),
1086     contrastThreshold(0)
1087 {
1088 }
1089 
1090 
operator ==(const ParametricMask & other) const1091 bool ParametricMask::operator==(const ParametricMask &other) const
1092 {
1093     return enabled == other.enabled
1094         && blur == other.blur
1095         && hue == other.hue
1096         && chromaticity == other.chromaticity
1097         && lightness == other.lightness
1098         && lightnessDetail == other.lightnessDetail
1099         && contrastThreshold == other.contrastThreshold;
1100 }
1101 
1102 
operator !=(const ParametricMask & other) const1103 bool ParametricMask::operator!=(const ParametricMask &other) const
1104 {
1105     return !(*this == other);
1106 }
1107 
1108 
Mask()1109 Mask::Mask():
1110     enabled(true),
1111     inverted(false),
1112     parametricMask(),
1113     areaMask(),
1114     deltaEMask(),
1115     drawnMask(),
1116     name("")
1117 {
1118 }
1119 
1120 
operator ==(const Mask & other) const1121 bool Mask::operator==(const Mask &other) const
1122 {
1123     return enabled == other.enabled
1124         && inverted == other.inverted
1125         && parametricMask == other.parametricMask
1126         && areaMask == other.areaMask
1127         && deltaEMask == other.deltaEMask
1128         && drawnMask == other.drawnMask
1129         && name == other.name;
1130 }
1131 
1132 
operator !=(const Mask & other) const1133 bool Mask::operator!=(const Mask &other) const
1134 {
1135     return !(*this == other);
1136 }
1137 
1138 
1139 namespace {
1140 
str2mode(const Glib::ustring & mode)1141 AreaMask::Shape::Mode str2mode(const Glib::ustring &mode)
1142 {
1143     if (mode == "subtract") {
1144         return AreaMask::Shape::SUBTRACT;
1145     } else if (mode == "intersect") {
1146         return AreaMask::Shape::INTERSECT;
1147     } else {
1148         return AreaMask::Shape::ADD;
1149     }
1150 }
1151 
1152 
mode2str(AreaMask::Shape::Mode mode)1153 Glib::ustring mode2str(AreaMask::Shape::Mode mode)
1154 {
1155     switch (mode) {
1156     case AreaMask::Shape::ADD: return "add";
1157     case AreaMask::Shape::SUBTRACT: return "subtract";
1158     case AreaMask::Shape::INTERSECT: return "intersect";
1159     default:
1160         assert(false);
1161         return "";
1162     }
1163 }
1164 
str2type(const Glib::ustring & type)1165 AreaMask::Shape::Type str2type(const Glib::ustring &type)
1166 {
1167     if (type == "rectangle") {
1168         return AreaMask::Shape::RECTANGLE;
1169     } else if (type == "gradient") {
1170         return AreaMask::Shape::GRADIENT;
1171     } else {
1172         return AreaMask::Shape::POLYGON;
1173     }
1174 }
1175 
1176 
type2str(AreaMask::Shape::Type type)1177 Glib::ustring type2str(AreaMask::Shape::Type type)
1178 {
1179     switch (type) {
1180     case AreaMask::Shape::RECTANGLE: return "rectangle";
1181     case AreaMask::Shape::GRADIENT: return "gradient";
1182     case AreaMask::Shape::POLYGON: return "polygon";
1183     default:
1184         assert(false);
1185         return "";
1186     }
1187 }
1188 
1189 } // namespace
1190 
1191 
load(int ppVersion,const KeyFile & keyfile,const Glib::ustring & group_name,const Glib::ustring & prefix,const Glib::ustring & suffix)1192 bool Mask::load(int ppVersion, const KeyFile &keyfile, const Glib::ustring &group_name, const Glib::ustring &prefix, const Glib::ustring &suffix)
1193 {
1194     bool ret = false;
1195     ret |= assignFromKeyfile(keyfile, group_name, prefix + "MaskEnabled" + suffix, enabled);
1196     ret |= assignFromKeyfile(keyfile, group_name, prefix + "MaskInverted" + suffix, inverted);
1197     ret |= assignFromKeyfile(keyfile, group_name, prefix + "MaskName" + suffix, name);
1198     if (ppVersion < 1008) {
1199         parametricMask.enabled = true;
1200     } else {
1201         ret |= assignFromKeyfile(keyfile, group_name, prefix + "ParametricMaskEnabled" + suffix, parametricMask.enabled);
1202     }
1203     ret |= assignFromKeyfile(keyfile, group_name, prefix + "HueMask" + suffix, parametricMask.hue);
1204     if (assignFromKeyfile(keyfile, group_name, prefix + "ChromaticityMask" + suffix, parametricMask.chromaticity)) {
1205         if (ppVersion < 1023) {
1206             for (size_t i = 1; i < parametricMask.chromaticity.size(); i += 4) {
1207                 auto &x = parametricMask.chromaticity[i];
1208                 x = lin2log(log2lin(x, 10.0), 50.0);
1209             }
1210         }
1211         ret = true;
1212     }
1213     ret |= assignFromKeyfile(keyfile, group_name, prefix + "LightnessMask" + suffix, parametricMask.lightness);
1214     ret |= assignFromKeyfile(keyfile, group_name, prefix + "LightnessMaskDetail" + suffix, parametricMask.lightnessDetail);
1215     ret |= assignFromKeyfile(keyfile, group_name, prefix + "ContrastThresholdMask" + suffix, parametricMask.contrastThreshold);
1216     if (ppVersion < 1008) {
1217         ret |= assignFromKeyfile(keyfile, group_name, prefix + "MaskBlur" + suffix, parametricMask.blur);
1218         areaMask.blur = parametricMask.blur;
1219     } else {
1220         ret |= assignFromKeyfile(keyfile, group_name, prefix + "ParametricMaskBlur" + suffix, parametricMask.blur);
1221         ret |= assignFromKeyfile(keyfile, group_name, prefix + "AreaMaskBlur" + suffix, areaMask.blur);
1222     }
1223     ret |= assignFromKeyfile(keyfile, group_name, prefix + "AreaMaskEnabled" + suffix, areaMask.enabled);
1224     ret |= assignFromKeyfile(keyfile, group_name, prefix + "AreaMaskFeather" + suffix, areaMask.feather);
1225     ret |= assignFromKeyfile(keyfile, group_name, prefix + "AreaMaskContrast" + suffix, areaMask.contrast);
1226     if (areaMask.contrast.empty() || areaMask.contrast[0] < DCT_Linear || areaMask.contrast[0] >= DCT_Unchanged) {
1227         areaMask.contrast = {DCT_Linear};
1228     }
1229     std::vector<std::unique_ptr<AreaMask::Shape>> s;
1230     for (int i = 0; ; ++i) {
1231         Glib::ustring type;
1232         Glib::ustring mode;
1233         bool found = true; // skipping the entire element if one key is missing
1234         std::string n = i ? std::string("_") + std::to_string(i) + "_" : "";
1235         if (ppVersion >= 1014) {
1236             found &= assignFromKeyfile(keyfile, group_name, prefix + "AreaMask" + n + "Type" + suffix, type);
1237         }
1238         if (ppVersion < 1014 || found) {
1239             std::unique_ptr<AreaMask::Shape> shape;
1240             if (ppVersion < 1014 || str2type(type) == AreaMask::Shape::Type::RECTANGLE) {
1241                 AreaMask::Rectangle *rect = new AreaMask::Rectangle();
1242                 found &= assignFromKeyfile(keyfile, group_name, prefix + "AreaMask" + n + "X" + suffix, rect->x);
1243                 found &= assignFromKeyfile(keyfile, group_name, prefix + "AreaMask" + n + "Y" + suffix, rect->y);
1244                 found &= assignFromKeyfile(keyfile, group_name, prefix + "AreaMask" + n + "Width" + suffix, rect->width);
1245                 found &= assignFromKeyfile(keyfile, group_name, prefix + "AreaMask" + n + "Height" + suffix, rect->height);
1246                 found &= assignFromKeyfile(keyfile, group_name, prefix + "AreaMask" + n + "Angle" + suffix, rect->angle);
1247                 found &= assignFromKeyfile(keyfile, group_name, prefix + "AreaMask" + n + "Roundness" + suffix, rect->roundness);
1248                 if (found) {
1249                     shape.reset(rect);
1250                 } else {
1251                     delete rect;
1252                 }
1253             } else if (str2type(type) == AreaMask::Shape::Type::POLYGON) {
1254                 AreaMask::Polygon *poly = new AreaMask::Polygon();
1255                 if (ppVersion < 1019) {
1256                     std::vector<float> v; // important: use vector<float> to avoid calling rtengine::sanitizeCurve -- need to think of a better way
1257                     if ((found &= assignFromKeyfile(keyfile, group_name, prefix + "AreaMask" + n + "Knots" + suffix, v))) {
1258                         ret = true;
1259                         std::vector<double> vv(v.begin(), v.end());
1260                         poly->knots_from_list(vv);
1261                     }
1262                 } else {
1263                     Glib::ustring raw;
1264                     if ((found &= assignFromKeyfile(keyfile, group_name, prefix + "AreaMask" + n + "Knots" + suffix, raw))) {
1265                         ret = true;
1266                         std::vector<double> vv = unpack_list(raw);//.raw());
1267                         poly->knots_from_list(vv);
1268                     }
1269                 }
1270                 if (found) {
1271                     shape.reset(poly);
1272                 } else {
1273                     delete poly;
1274                 }
1275             } else if (str2type(type) == AreaMask::Shape::Type::GRADIENT) {
1276                 AreaMask::Gradient *gradient = new AreaMask::Gradient();
1277                 found &= assignFromKeyfile(keyfile, group_name, prefix + "AreaMask" + n + "X" + suffix, gradient->x);
1278                 found &= assignFromKeyfile(keyfile, group_name, prefix + "AreaMask" + n + "Y" + suffix, gradient->y);
1279                 found &= assignFromKeyfile(keyfile, group_name, prefix + "AreaMask" + n + "StrengthStart" + suffix, gradient->strengthStart);
1280                 found &= assignFromKeyfile(keyfile, group_name, prefix + "AreaMask" + n + "StrengthEnd" + suffix, gradient->strengthEnd);
1281                 found &= assignFromKeyfile(keyfile, group_name, prefix + "AreaMask" + n + "Angle" + suffix, gradient->angle);
1282                 if (found) {
1283                     shape.reset(gradient);
1284                 } else {
1285                     delete gradient;
1286                 }
1287             }
1288             found &= assignFromKeyfile(keyfile, group_name, prefix + "AreaMask" + n + "Mode" + suffix, mode);
1289             if (ppVersion >= 1015) {
1290                 found &= assignFromKeyfile(keyfile, group_name, prefix + "AreaMask" + n + "ShapeFeather" + suffix, shape->feather);
1291                 found &= assignFromKeyfile(keyfile, group_name, prefix + "AreaMask" + n + "ShapeBlur" + suffix, shape->blur);
1292             }
1293             if (found) {
1294                 shape->mode = str2mode(mode);
1295                 s.emplace_back(std::move(shape));
1296                 ret = true;
1297             } else {
1298                 break;
1299             }
1300         } else {
1301             break;
1302         }
1303     }
1304     if (!s.empty()) {
1305         areaMask.shapes = std::move(s);
1306     }
1307 
1308     ret |= assignFromKeyfile(keyfile, group_name, prefix + "DeltaEMaskEnabled" + suffix, deltaEMask.enabled);
1309     ret |= assignFromKeyfile(keyfile, group_name, prefix + "DeltaEMaskL" + suffix, deltaEMask.L);
1310     ret |= assignFromKeyfile(keyfile, group_name, prefix + "DeltaEMaskC" + suffix, deltaEMask.C);
1311     ret |= assignFromKeyfile(keyfile, group_name, prefix + "DeltaEMaskH" + suffix, deltaEMask.H);
1312     ret |= assignFromKeyfile(keyfile, group_name, prefix + "DeltaEMaskRange" + suffix, deltaEMask.range);
1313     ret |= assignFromKeyfile(keyfile, group_name, prefix + "DeltaEMaskDecay" + suffix, deltaEMask.decay);
1314     ret |= assignFromKeyfile(keyfile, group_name, prefix + "DeltaEMaskWeightL" + suffix, deltaEMask.weight_L);
1315     ret |= assignFromKeyfile(keyfile, group_name, prefix + "DeltaEMaskWeightC" + suffix, deltaEMask.weight_C);
1316     ret |= assignFromKeyfile(keyfile, group_name, prefix + "DeltaEMaskWeightH" + suffix, deltaEMask.weight_H);
1317     ret |= assignFromKeyfile(keyfile, group_name, prefix + "DrawnMaskEnabled" + suffix, drawnMask.enabled);
1318     ret |= assignFromKeyfile(keyfile, group_name, prefix + "DrawnMaskFeather" + suffix, drawnMask.feather);
1319     ret |= assignFromKeyfile(keyfile, group_name, prefix + "DrawnMaskTransparency" + suffix, drawnMask.transparency);
1320     ret |= assignFromKeyfile(keyfile, group_name, prefix + "DrawnMaskSmoothness" + suffix, drawnMask.smoothness);
1321     ret |= assignFromKeyfile(keyfile, group_name, prefix + "DrawnMaskContrast" + suffix, drawnMask.contrast);
1322     if (ppVersion < 1019) {
1323         bool addmode = false;
1324         if (assignFromKeyfile(keyfile, group_name, prefix + "DrawnMaskAddMode" + suffix, addmode)) {
1325             ret = true;
1326             drawnMask.mode = addmode ? DrawnMask::ADD : DrawnMask::INTERSECT;
1327         }
1328     } else {
1329         int mode = 0;
1330         if (assignFromKeyfile(keyfile, group_name, prefix + "DrawnMaskMode" + suffix, mode)) {
1331             ret = true;
1332             drawnMask.mode = DrawnMask::Mode(LIM(mode, 0, 2));
1333         }
1334     }
1335     if (ppVersion < 1019) {
1336         std::vector<float> v; // important: use vector<float> to avoid calling rtengine::sanitizeCurve -- need to think of a better way
1337         if (assignFromKeyfile(keyfile, group_name, prefix + "DrawnMaskStrokes" + suffix, v)) {
1338             ret = true;
1339             std::vector<double> vv(v.begin(), v.end());
1340             drawnMask.strokes_from_list(vv);
1341         }
1342     } else {
1343         Glib::ustring raw;
1344         if (assignFromKeyfile(keyfile, group_name, prefix + "DrawnMaskStrokes" + suffix, raw)) {
1345             ret = true;
1346             std::vector<double> vv = unpack_list(raw);
1347             drawnMask.strokes_from_list(vv);
1348             if (ppVersion < 1022 && !drawnMask.strokes.empty()) {
1349                 std::vector<DrawnMask::Stroke> stmp;
1350                 stmp.swap(drawnMask.strokes);
1351                 drawnMask.strokes.push_back(stmp[0]);
1352                 for (size_t i = 1; i < stmp.size(); ++i) {
1353                     auto &p = drawnMask.strokes.back();
1354                     auto &s = stmp[i];
1355                     if (p.radius != s.radius && p.radius > 0 && s.radius > 0 &&
1356                         p.hardness == s.hardness && p.erase == s.erase) {
1357                         drawnMask.strokes.push_back(DrawnMask::Stroke());
1358                     }
1359                     drawnMask.strokes.push_back(s);
1360                 }
1361             }
1362         }
1363     }
1364 
1365     return ret;
1366 }
1367 
1368 
save(KeyFile & keyfile,const Glib::ustring & group_name,const Glib::ustring & prefix,const Glib::ustring & suffix) const1369 void Mask::save(KeyFile &keyfile, const Glib::ustring &group_name, const Glib::ustring &prefix, const Glib::ustring &suffix) const
1370 {
1371     putToKeyfile(group_name, prefix + "MaskEnabled" + suffix, enabled, keyfile);
1372     putToKeyfile(group_name, prefix + "MaskInverted" + suffix, inverted, keyfile);
1373     putToKeyfile(group_name, prefix + "MaskName" + suffix, name, keyfile);
1374     putToKeyfile(group_name, prefix + "ParametricMaskEnabled" + suffix, parametricMask.enabled, keyfile);
1375     putToKeyfile(group_name, prefix + "HueMask" + suffix, parametricMask.hue, keyfile);
1376     putToKeyfile(group_name, prefix + "ChromaticityMask" + suffix, parametricMask.chromaticity, keyfile);
1377     putToKeyfile(group_name, prefix + "LightnessMask" + suffix, parametricMask.lightness, keyfile);
1378     putToKeyfile(group_name, prefix + "LightnessMaskDetail" + suffix, parametricMask.lightnessDetail, keyfile);
1379     putToKeyfile(group_name, prefix + "ContrastThresholdMask" + suffix, parametricMask.contrastThreshold, keyfile);
1380     putToKeyfile(group_name, prefix + "ParametricMaskBlur" + suffix, parametricMask.blur, keyfile);
1381     putToKeyfile(group_name, prefix + "AreaMaskEnabled" + suffix, areaMask.enabled, keyfile);
1382     putToKeyfile(group_name, prefix + "AreaMaskFeather" + suffix, areaMask.feather, keyfile);
1383     putToKeyfile(group_name, prefix + "AreaMaskBlur" + suffix, areaMask.blur, keyfile);
1384     putToKeyfile(group_name, prefix + "AreaMaskContrast" + suffix, areaMask.contrast, keyfile);
1385 
1386     for (size_t i = 0; i < areaMask.shapes.size(); ++i) {
1387         auto &a = areaMask.shapes[i];
1388         std::string n = i ? std::string("_") + std::to_string(i) + "_" : "";
1389         putToKeyfile(group_name, prefix + "AreaMask" + n + "Type" + suffix, type2str(a->getType()), keyfile);
1390         switch (a->getType()) {
1391         case AreaMask::Shape::Type::POLYGON:
1392         {
1393             auto poly = static_cast<AreaMask::Polygon*>(a.get());
1394             std::vector<double> v;
1395             poly->knots_to_list(v);
1396             putToKeyfile(group_name, prefix + "AreaMask" + n + "Knots" + suffix, pack_list(v), keyfile);
1397             break;
1398         }
1399         case AreaMask::Shape::Type::GRADIENT:
1400         {
1401             auto gradient = static_cast<AreaMask::Gradient*>(a.get());
1402             putToKeyfile(group_name, prefix + "AreaMask" + n + "X" + suffix, gradient->x, keyfile);
1403             putToKeyfile(group_name, prefix + "AreaMask" + n + "Y" + suffix, gradient->y, keyfile);
1404             putToKeyfile(group_name, prefix + "AreaMask" + n + "StrengthStart" + suffix, gradient->strengthStart, keyfile);
1405             putToKeyfile(group_name, prefix + "AreaMask" + n + "StrengthEnd" + suffix, gradient->strengthEnd, keyfile);
1406             putToKeyfile(group_name, prefix + "AreaMask" + n + "Angle" + suffix, gradient->angle, keyfile);
1407             break;
1408         }
1409         case AreaMask::Shape::Type::RECTANGLE:
1410         default:
1411         {
1412             auto rect = static_cast<AreaMask::Rectangle*>(a.get());
1413             putToKeyfile(group_name, prefix + "AreaMask" + n + "X" + suffix, rect->x, keyfile);
1414             putToKeyfile(group_name, prefix + "AreaMask" + n + "Y" + suffix, rect->y, keyfile);
1415             putToKeyfile(group_name, prefix + "AreaMask" + n + "Width" + suffix, rect->width, keyfile);
1416             putToKeyfile(group_name, prefix + "AreaMask" + n + "Height" + suffix, rect->height, keyfile);
1417             putToKeyfile(group_name, prefix + "AreaMask" + n + "Angle" + suffix, rect->angle, keyfile);
1418             putToKeyfile(group_name, prefix + "AreaMask" + n + "Roundness" + suffix, rect->roundness, keyfile);
1419         }
1420         }
1421         putToKeyfile(group_name, prefix + "AreaMask" + n + "Mode" + suffix, mode2str(a->mode), keyfile);
1422         putToKeyfile(group_name, prefix + "AreaMask" + n + "ShapeFeather" + suffix, a->feather, keyfile);
1423         putToKeyfile(group_name, prefix + "AreaMask" + n + "ShapeBlur" + suffix, a->blur, keyfile);
1424     }
1425 
1426     putToKeyfile(group_name, prefix + "DeltaEMaskEnabled" + suffix, deltaEMask.enabled, keyfile);
1427     putToKeyfile(group_name, prefix + "DeltaEMaskL" + suffix, deltaEMask.L, keyfile);
1428     putToKeyfile(group_name, prefix + "DeltaEMaskC" + suffix, deltaEMask.C, keyfile);
1429     putToKeyfile(group_name, prefix + "DeltaEMaskH" + suffix, deltaEMask.H, keyfile);
1430     putToKeyfile(group_name, prefix + "DeltaEMaskRange" + suffix, deltaEMask.range, keyfile);
1431     putToKeyfile(group_name, prefix + "DeltaEMaskDecay" + suffix, deltaEMask.decay, keyfile);
1432     putToKeyfile(group_name, prefix + "DeltaEMaskWeightL" + suffix, deltaEMask.weight_L, keyfile);
1433     putToKeyfile(group_name, prefix + "DeltaEMaskWeightC" + suffix, deltaEMask.weight_C, keyfile);
1434     putToKeyfile(group_name, prefix + "DeltaEMaskWeightH" + suffix, deltaEMask.weight_H, keyfile);
1435     putToKeyfile(group_name, prefix + "DrawnMaskEnabled" + suffix, drawnMask.enabled, keyfile);
1436     putToKeyfile(group_name, prefix + "DrawnMaskFeather" + suffix, drawnMask.feather, keyfile);
1437     putToKeyfile(group_name, prefix + "DrawnMaskTransparency" + suffix, drawnMask.transparency, keyfile);
1438     putToKeyfile(group_name, prefix + "DrawnMaskSmoothness" + suffix, drawnMask.smoothness, keyfile);
1439     putToKeyfile(group_name, prefix + "DrawnMaskContrast" + suffix, drawnMask.contrast, keyfile);
1440     putToKeyfile(group_name, prefix + "DrawnMaskMode" + suffix, int(drawnMask.mode), keyfile);
1441     std::vector<double> v;
1442     drawnMask.strokes_to_list(v);
1443     putToKeyfile(group_name, prefix + "DrawnMaskStrokes" + suffix, pack_list(v), keyfile);
1444 }
1445 
1446 
ExposureParams()1447 ExposureParams::ExposureParams():
1448     enabled(true),
1449     hrmode(HR_OFF),
1450     expcomp(0),
1451     black(0)
1452 {
1453 }
1454 
1455 
operator ==(const ExposureParams & other) const1456 bool ExposureParams::operator==(const ExposureParams &other) const
1457 {
1458     return enabled == other.enabled
1459         && hrmode == other.hrmode
1460         && expcomp == other.expcomp
1461         && black == other.black;
1462 }
1463 
1464 
operator !=(const ExposureParams & other) const1465 bool ExposureParams::operator!=(const ExposureParams &other) const
1466 {
1467     return !(*this == other);
1468 }
1469 
1470 
SaturationParams()1471 SaturationParams::SaturationParams():
1472     enabled(false),
1473     saturation(0),
1474     vibrance(0)
1475 {
1476 }
1477 
1478 
operator ==(const SaturationParams & other) const1479 bool SaturationParams::operator==(const SaturationParams &other) const
1480 {
1481     return enabled == other.enabled
1482         && saturation == other.saturation
1483         && vibrance == other.vibrance;
1484 }
1485 
1486 
operator !=(const SaturationParams & other) const1487 bool SaturationParams::operator!=(const SaturationParams &other) const
1488 {
1489     return !(*this == other);
1490 }
1491 
1492 
ToneCurveParams()1493 ToneCurveParams::ToneCurveParams():
1494     enabled(false),
1495     contrast(0),
1496     curve{
1497         DCT_Linear
1498     },
1499     curve2{
1500         DCT_Linear
1501     },
1502     curveMode(ToneCurveParams::TcMode::STD),
1503     curveMode2(ToneCurveParams::TcMode::STD),
1504     histmatching(false),
1505     fromHistMatching(false),
1506     saturation{
1507         FCT_Linear
1508     },
1509     perceptualStrength(100)
1510 {
1511 }
1512 
1513 
operator ==(const ToneCurveParams & other) const1514 bool ToneCurveParams::operator ==(const ToneCurveParams& other) const
1515 {
1516     return enabled == other.enabled
1517         && contrast == other.contrast
1518         && curve == other.curve
1519         && curve2 == other.curve2
1520         && curveMode == other.curveMode
1521         && curveMode2 == other.curveMode2
1522         && histmatching == other.histmatching
1523         && fromHistMatching == other.fromHistMatching
1524         && saturation == other.saturation
1525         && perceptualStrength == other.perceptualStrength;
1526 }
1527 
1528 
operator !=(const ToneCurveParams & other) const1529 bool ToneCurveParams::operator !=(const ToneCurveParams& other) const
1530 {
1531     return !(*this == other);
1532 }
1533 
1534 
LabCurveParams()1535 LabCurveParams::LabCurveParams() :
1536     enabled(false),
1537     brightness(0),
1538     contrast(0),
1539     chromaticity(0),
1540     lcurve{
1541         DCT_Linear
1542     },
1543     acurve{
1544         DCT_Linear
1545     },
1546     bcurve{
1547         DCT_Linear
1548     }
1549 {
1550 }
1551 
operator ==(const LabCurveParams & other) const1552 bool LabCurveParams::operator ==(const LabCurveParams& other) const
1553 {
1554     return
1555         enabled == other.enabled
1556         && brightness == other.brightness
1557         && contrast == other.contrast
1558         && chromaticity == other.chromaticity
1559         && lcurve == other.lcurve
1560         && acurve == other.acurve
1561         && bcurve == other.bcurve;
1562 }
1563 
operator !=(const LabCurveParams & other) const1564 bool LabCurveParams::operator !=(const LabCurveParams& other) const
1565 {
1566     return !(*this == other);
1567 }
1568 
RGBCurvesParams()1569 RGBCurvesParams::RGBCurvesParams() :
1570     enabled(false),
1571     rcurve{
1572         DCT_Linear
1573     },
1574     gcurve{
1575         DCT_Linear
1576     },
1577     bcurve{
1578         DCT_Linear
1579     }
1580 {
1581 }
1582 
operator ==(const RGBCurvesParams & other) const1583 bool RGBCurvesParams::operator ==(const RGBCurvesParams& other) const
1584 {
1585     return
1586         enabled == other.enabled
1587         && rcurve == other.rcurve
1588         && gcurve == other.gcurve
1589         && bcurve == other.bcurve;
1590 }
1591 
operator !=(const RGBCurvesParams & other) const1592 bool RGBCurvesParams::operator !=(const RGBCurvesParams& other) const
1593 {
1594     return !(*this == other);
1595 }
1596 
1597 
Region()1598 LocalContrastParams::Region::Region():
1599     contrast(0),
1600     curve{
1601         FCT_MinMaxCPoints,
1602         0.0,
1603         0.5,
1604         0.0,
1605         0.0,
1606         1.0,
1607         0.5,
1608         0.0,
1609         0.0
1610     }
1611 {
1612 }
1613 
1614 
operator ==(const Region & other) const1615 bool LocalContrastParams::Region::operator==(const Region &other) const
1616 {
1617     return contrast == other.contrast
1618         && curve == other.curve;
1619 }
1620 
1621 
operator !=(const Region & other) const1622 bool LocalContrastParams::Region::operator!=(const Region &other) const
1623 {
1624     return !(*this == other);
1625 }
1626 
1627 
LocalContrastParams()1628 LocalContrastParams::LocalContrastParams():
1629     enabled(false),
1630     regions{Region()},
1631     labmasks{Mask()},
1632     showMask(-1)
1633 {
1634 }
1635 
1636 
operator ==(const LocalContrastParams & other) const1637 bool LocalContrastParams::operator==(const LocalContrastParams &other) const
1638 {
1639     return
1640         enabled == other.enabled
1641         && regions == other.regions
1642         && labmasks == other.labmasks
1643         && showMask == other.showMask;
1644 }
1645 
1646 
operator !=(const LocalContrastParams & other) const1647 bool LocalContrastParams::operator!=(const LocalContrastParams &other) const
1648 {
1649     return !(*this == other);
1650 }
1651 
1652 
SharpeningParams()1653 SharpeningParams::SharpeningParams() :
1654     enabled(false),
1655     contrast(20.0),
1656     radius(0.5),
1657     amount(200),
1658     threshold(20, 80, 2000, 1200, false),
1659     edgesonly(false),
1660     edges_radius(1.9),
1661     edges_tolerance(1800),
1662     halocontrol(false),
1663     halocontrol_amount(85),
1664     method("rld"),
1665     deconvamount(100),
1666     deconvradius(0.75),
1667     deconvAutoRadius(true),
1668     deconvCornerBoost(0.0),
1669     deconvCornerLatitude(25)
1670 {
1671 }
1672 
operator ==(const SharpeningParams & other) const1673 bool SharpeningParams::operator ==(const SharpeningParams& other) const
1674 {
1675     return
1676         enabled == other.enabled
1677         && contrast == other.contrast
1678         && radius == other.radius
1679         && amount == other.amount
1680         && threshold == other.threshold
1681         && edgesonly == other.edgesonly
1682         && edges_radius == other.edges_radius
1683         && edges_tolerance == other.edges_tolerance
1684         && halocontrol == other.halocontrol
1685         && halocontrol_amount == other.halocontrol_amount
1686         && method == other.method
1687         && deconvamount == other.deconvamount
1688         && deconvradius == other.deconvradius
1689         && deconvAutoRadius == other.deconvAutoRadius
1690         && deconvCornerBoost == other.deconvCornerBoost
1691         && deconvCornerLatitude == other.deconvCornerLatitude;
1692 }
1693 
operator !=(const SharpeningParams & other) const1694 bool SharpeningParams::operator !=(const SharpeningParams& other) const
1695 {
1696     return !(*this == other);
1697 }
1698 
1699 
WBParams()1700 WBParams::WBParams() :
1701     enabled(true),
1702     method(CAMERA),
1703     temperature(6504),
1704     green(1.0),
1705     equal(1.0),
1706     mult{1.0, 1.0, 1.0}
1707 {
1708 }
1709 
operator ==(const WBParams & other) const1710 bool WBParams::operator ==(const WBParams& other) const
1711 {
1712     return
1713         enabled == other.enabled
1714         && method == other.method
1715         && temperature == other.temperature
1716         && green == other.green
1717         && equal == other.equal
1718         && mult == other.mult;
1719 }
1720 
operator !=(const WBParams & other) const1721 bool WBParams::operator !=(const WBParams& other) const
1722 {
1723     return !(*this == other);
1724 }
1725 
1726 
DefringeParams()1727 DefringeParams::DefringeParams() :
1728     enabled(false),
1729     radius(2.0),
1730     threshold(13),
1731     huecurve{
1732         FCT_MinMaxCPoints,
1733         0.166666667,
1734         0.,
1735         0.35,
1736         0.35,
1737         0.347,
1738         0.,
1739         0.35,
1740         0.35,
1741         0.513667426,
1742         0,
1743         0.35,
1744         0.35,
1745         0.668944571,
1746         0.,
1747         0.35,
1748         0.35,
1749         0.8287775246,
1750         0.97835991,
1751         0.35,
1752         0.35,
1753         0.9908883827,
1754         0.,
1755         0.35,
1756         0.35
1757     }
1758 {
1759 }
1760 
operator ==(const DefringeParams & other) const1761 bool DefringeParams::operator ==(const DefringeParams& other) const
1762 {
1763     return
1764         enabled == other.enabled
1765         && radius == other.radius
1766         && threshold == other.threshold
1767         && huecurve == other.huecurve;
1768 }
1769 
operator !=(const DefringeParams & other) const1770 bool DefringeParams::operator !=(const DefringeParams& other) const
1771 {
1772     return !(*this == other);
1773 }
1774 
ImpulseDenoiseParams()1775 ImpulseDenoiseParams::ImpulseDenoiseParams() :
1776     enabled(false),
1777     thresh(50)
1778 {
1779 }
1780 
operator ==(const ImpulseDenoiseParams & other) const1781 bool ImpulseDenoiseParams::operator ==(const ImpulseDenoiseParams& other) const
1782 {
1783     return
1784         enabled == other.enabled
1785         && thresh == other.thresh;
1786 }
1787 
operator !=(const ImpulseDenoiseParams & other) const1788 bool ImpulseDenoiseParams::operator !=(const ImpulseDenoiseParams& other) const
1789 {
1790     return !(*this == other);
1791 }
1792 
DenoiseParams()1793 DenoiseParams::DenoiseParams() :
1794     enabled(false),
1795     colorSpace(ColorSpace::RGB),
1796     aggressive(false),
1797     gamma(1.7),
1798     luminance(0),
1799     luminanceDetail(0),
1800     luminanceDetailThreshold(0),
1801     chrominanceMethod(ChrominanceMethod::AUTOMATIC),
1802     chrominanceAutoFactor(1),
1803     chrominance(15),
1804     chrominanceRedGreen(0),
1805     chrominanceBlueYellow(0),
1806     smoothingEnabled(false),
1807     guidedChromaRadius(3),
1808     nlDetail(80),
1809     nlStrength(0)
1810 {
1811 }
1812 
1813 
operator ==(const DenoiseParams & other) const1814 bool DenoiseParams::operator ==(const DenoiseParams& other) const
1815 {
1816     return
1817         enabled == other.enabled
1818         && colorSpace == other.colorSpace
1819         && aggressive == other.aggressive
1820         && gamma == other.gamma
1821         && luminance == other.luminance
1822         && luminanceDetail == other.luminanceDetail
1823         && luminanceDetailThreshold == other.luminanceDetailThreshold
1824         && chrominanceMethod == other.chrominanceMethod
1825         && chrominanceAutoFactor == other.chrominanceAutoFactor
1826         && chrominance == other.chrominance
1827         && chrominanceRedGreen == other.chrominanceRedGreen
1828         && chrominanceBlueYellow == other.chrominanceBlueYellow
1829         && smoothingEnabled == other.smoothingEnabled
1830         && guidedChromaRadius == other.guidedChromaRadius
1831         && nlDetail == other.nlDetail
1832         && nlStrength == other.nlStrength;
1833 }
1834 
1835 
operator !=(const DenoiseParams & other) const1836 bool DenoiseParams::operator !=(const DenoiseParams& other) const
1837 {
1838     return !(*this == other);
1839 }
1840 
1841 
Region()1842 TextureBoostParams::Region::Region():
1843     strength(0.5),
1844     detailThreshold(1.0),
1845     iterations(1)
1846 {
1847 }
1848 
1849 
operator ==(const Region & other) const1850 bool TextureBoostParams::Region::operator==(const Region &other) const
1851 {
1852     return strength == other.strength
1853         && detailThreshold == other.detailThreshold
1854         && iterations == other.iterations;
1855 }
1856 
1857 
operator !=(const Region & other) const1858 bool TextureBoostParams::Region::operator!=(const Region &other) const
1859 {
1860     return !(*this == other);
1861 }
1862 
SpotEntry()1863 SpotEntry::SpotEntry() :
1864     radius(25),
1865     feather(1.f),
1866     opacity(1.f),
1867     detail(2)
1868 {
1869 }
getFeatherRadius() const1870 float SpotEntry::getFeatherRadius() const
1871 {
1872     return radius * (1.f + feather);
1873 }
1874 
operator ==(const SpotEntry & other) const1875 bool SpotEntry::operator ==(const SpotEntry& other) const
1876 {
1877     return other.sourcePos == sourcePos
1878         && other.targetPos == targetPos
1879         && other.radius == radius
1880         && other.feather == feather
1881         && other.opacity == opacity
1882         && other.detail == detail;
1883 }
1884 
operator !=(const SpotEntry & other) const1885 bool SpotEntry::operator !=(const SpotEntry& other) const
1886 {
1887     return !(*this == other);
1888 }
1889 
1890 
SpotParams()1891 SpotParams::SpotParams() :
1892     enabled(false)
1893 {
1894     entries.clear ();
1895 }
1896 
operator ==(const SpotParams & other) const1897 bool SpotParams::operator ==(const SpotParams& other) const
1898 {
1899     return enabled == other.enabled && entries == other.entries;
1900 }
1901 
operator !=(const SpotParams & other) const1902 bool SpotParams::operator !=(const SpotParams& other) const
1903 {
1904     return !(*this == other);
1905 }
1906 
TextureBoostParams()1907 TextureBoostParams::TextureBoostParams() :
1908     enabled(false),
1909     regions{Region()},
1910     labmasks{Mask()},
1911     showMask(-1)
1912 {
1913 }
1914 
operator ==(const TextureBoostParams & other) const1915 bool TextureBoostParams::operator ==(const TextureBoostParams& other) const
1916 {
1917     return
1918         enabled == other.enabled
1919         && regions == other.regions
1920         && labmasks == other.labmasks
1921         && showMask == other.showMask;
1922 }
1923 
operator !=(const TextureBoostParams & other) const1924 bool TextureBoostParams::operator !=(const TextureBoostParams& other) const
1925 {
1926     return !(*this == other);
1927 }
1928 
1929 
LogEncodingParams()1930 LogEncodingParams::LogEncodingParams():
1931     enabled(false),
1932     autocompute(false),
1933     autogain(false),
1934     gain(0.0),
1935     targetGray(18.0),
1936     blackEv(-13.5),
1937     whiteEv(2.5),
1938     regularization(60)
1939 {
1940 }
1941 
operator ==(const LogEncodingParams & other) const1942 bool LogEncodingParams::operator ==(const LogEncodingParams& other) const
1943 {
1944     return
1945         enabled == other.enabled
1946         && autocompute == other.autocompute
1947         && autogain == other.autogain
1948         && gain == other.gain
1949         && blackEv == other.blackEv
1950         && whiteEv == other.whiteEv
1951         && targetGray == other.targetGray
1952         && regularization == other.regularization;
1953 }
1954 
operator !=(const LogEncodingParams & other) const1955 bool LogEncodingParams::operator !=(const LogEncodingParams& other) const
1956 {
1957     return !(*this == other);
1958 }
1959 
1960 
FattalToneMappingParams()1961 FattalToneMappingParams::FattalToneMappingParams() :
1962     enabled(false),
1963     threshold(30),
1964     amount(20),
1965     satcontrol(false)
1966 {
1967 }
1968 
operator ==(const FattalToneMappingParams & other) const1969 bool FattalToneMappingParams::operator ==(const FattalToneMappingParams& other) const
1970 {
1971     return
1972         enabled == other.enabled
1973         && threshold == other.threshold
1974         && amount == other.amount
1975         && satcontrol == other.satcontrol;
1976 }
1977 
operator !=(const FattalToneMappingParams & other) const1978 bool FattalToneMappingParams::operator !=(const FattalToneMappingParams& other) const
1979 {
1980     return !(*this == other);
1981 }
1982 
1983 
ToneEqualizerParams()1984 ToneEqualizerParams::ToneEqualizerParams():
1985     enabled(false),
1986     bands{0,0,0,0,0},
1987     regularization(4),
1988     show_colormap(false)
1989 {
1990 }
1991 
1992 
operator ==(const ToneEqualizerParams & other) const1993 bool ToneEqualizerParams::operator ==(const ToneEqualizerParams& other) const
1994 {
1995     return
1996         enabled == other.enabled
1997         && bands == other.bands
1998         && regularization == other.regularization
1999         && show_colormap == other.show_colormap;
2000 }
2001 
2002 
operator !=(const ToneEqualizerParams & other) const2003 bool ToneEqualizerParams::operator !=(const ToneEqualizerParams& other) const
2004 {
2005     return !(*this == other);
2006 }
2007 
2008 
CropParams()2009 CropParams::CropParams() :
2010     enabled(false),
2011     x(-1),
2012     y(-1),
2013     w(15000),
2014     h(15000),
2015     fixratio(true),
2016     ratio("As Image"),
2017     orientation("As Image"),
2018     guide("Frame")
2019 {
2020 }
2021 
operator ==(const CropParams & other) const2022 bool CropParams::operator ==(const CropParams& other) const
2023 {
2024     return
2025         enabled == other.enabled
2026         && x == other.x
2027         && y == other.y
2028         && w == other.w
2029         && h == other.h
2030         && fixratio == other.fixratio
2031         && ratio == other.ratio
2032         && orientation == other.orientation
2033         && guide == other.guide;
2034 }
2035 
operator !=(const CropParams & other) const2036 bool CropParams::operator !=(const CropParams& other) const
2037 {
2038     return !(*this == other);
2039 }
2040 
mapToResized(int resizedWidth,int resizedHeight,int scale,int & x1,int & x2,int & y1,int & y2) const2041 void CropParams::mapToResized(int resizedWidth, int resizedHeight, int scale, int& x1, int& x2, int& y1, int& y2) const
2042 {
2043     x1 = 0, x2 = resizedWidth, y1 = 0, y2 = resizedHeight;
2044 
2045     if (enabled) {
2046         x1 = min(resizedWidth - 1, max(0, x / scale));
2047         y1 = min(resizedHeight - 1, max(0, y / scale));
2048         x2 = min(resizedWidth, max(0, (x + w) / scale));
2049         y2 = min(resizedHeight, max(0, (y + h) / scale));
2050     }
2051 }
2052 
CoarseTransformParams()2053 CoarseTransformParams::CoarseTransformParams() :
2054     rotate(0),
2055     hflip(false),
2056     vflip(false)
2057 {
2058 }
2059 
operator ==(const CoarseTransformParams & other) const2060 bool CoarseTransformParams::operator ==(const CoarseTransformParams& other) const
2061 {
2062     return
2063         rotate == other.rotate
2064         && hflip == other.hflip
2065         && vflip == other.vflip;
2066 }
2067 
operator !=(const CoarseTransformParams & other) const2068 bool CoarseTransformParams::operator !=(const CoarseTransformParams& other) const
2069 {
2070     return !(*this == other);
2071 }
2072 
CommonTransformParams()2073 CommonTransformParams::CommonTransformParams() :
2074     autofill(true)
2075 {
2076 }
2077 
operator ==(const CommonTransformParams & other) const2078 bool CommonTransformParams::operator ==(const CommonTransformParams& other) const
2079 {
2080     return autofill == other.autofill;
2081 }
2082 
operator !=(const CommonTransformParams & other) const2083 bool CommonTransformParams::operator !=(const CommonTransformParams& other) const
2084 {
2085     return !(*this == other);
2086 }
2087 
RotateParams()2088 RotateParams::RotateParams() :
2089     enabled(false),
2090     degree(0.0)
2091 {
2092 }
2093 
operator ==(const RotateParams & other) const2094 bool RotateParams::operator ==(const RotateParams& other) const
2095 {
2096     return enabled == other.enabled && degree == other.degree;
2097 }
2098 
operator !=(const RotateParams & other) const2099 bool RotateParams::operator !=(const RotateParams& other) const
2100 {
2101     return !(*this == other);
2102 }
2103 
DistortionParams()2104 DistortionParams::DistortionParams() :
2105     enabled(false),
2106     amount(0.0),
2107     autocompute(false)
2108 {
2109 }
2110 
operator ==(const DistortionParams & other) const2111 bool DistortionParams::operator ==(const DistortionParams& other) const
2112 {
2113     return enabled == other.enabled && amount == other.amount
2114         && autocompute == other.autocompute;
2115 }
2116 
operator !=(const DistortionParams & other) const2117 bool DistortionParams::operator !=(const DistortionParams& other) const
2118 {
2119     return !(*this == other);
2120 }
2121 
LensProfParams()2122 LensProfParams::LensProfParams() :
2123     lcMode(LcMode::NONE),
2124     useDist(true),
2125     useVign(true),
2126     useCA(false)
2127 {
2128 }
2129 
operator ==(const LensProfParams & other) const2130 bool LensProfParams::operator ==(const LensProfParams& other) const
2131 {
2132     return
2133         lcMode == other.lcMode
2134         && lcpFile == other.lcpFile
2135         && useCA == other.useCA
2136         && lfCameraMake == other.lfCameraMake
2137         && lfCameraModel == other.lfCameraModel
2138         && lfLens == other.lfLens;
2139 }
2140 
operator !=(const LensProfParams & other) const2141 bool LensProfParams::operator !=(const LensProfParams& other) const
2142 {
2143     return !(*this == other);
2144 }
2145 
useLensfun() const2146 bool LensProfParams::useLensfun() const
2147 {
2148     return lcMode == LcMode::LENSFUNAUTOMATCH || lcMode == LcMode::LENSFUNMANUAL;
2149 }
2150 
lfAutoMatch() const2151 bool LensProfParams::lfAutoMatch() const
2152 {
2153     return lcMode == LcMode::LENSFUNAUTOMATCH;
2154 }
2155 
useLcp() const2156 bool LensProfParams::useLcp() const
2157 {
2158     return lcMode == LcMode::LCP && lcpFile.length() > 0;
2159 }
2160 
lfManual() const2161 bool LensProfParams::lfManual() const
2162 {
2163     return lcMode == LcMode::LENSFUNMANUAL;
2164 }
2165 
2166 
useExif() const2167 bool LensProfParams::useExif() const
2168 {
2169     return lcMode == LcMode::EXIF;
2170 }
2171 
2172 
needed() const2173 bool LensProfParams::needed() const
2174 {
2175     return useLensfun() || useLcp() || useExif();
2176 }
2177 
2178 
getMethodStrings() const2179 const std::vector<const char*>& LensProfParams::getMethodStrings() const
2180 {
2181     static const std::vector<const char*> method_strings = {
2182         "none",
2183         "lfauto",
2184         "lfmanual",
2185         "lcp",
2186         "exif"
2187     };
2188     return method_strings;
2189 }
2190 
getMethodString(LcMode mode) const2191 Glib::ustring LensProfParams::getMethodString(LcMode mode) const
2192 {
2193     return getMethodStrings()[toUnderlying(mode)];
2194 }
2195 
getMethodNumber(const Glib::ustring & mode) const2196 LensProfParams::LcMode LensProfParams::getMethodNumber(const Glib::ustring& mode) const
2197 {
2198     for (std::vector<const char*>::size_type i = 0; i < getMethodStrings().size(); ++i) {
2199         if (getMethodStrings()[i] == mode) {
2200             return static_cast<LcMode>(i);
2201         }
2202     }
2203 
2204     return LcMode::NONE;
2205 }
2206 
PerspectiveParams()2207 PerspectiveParams::PerspectiveParams() :
2208     enabled(false),
2209     horizontal(0.0),
2210     vertical(0.0),
2211     angle(0.0),
2212     shear(0.0),
2213     flength(0),
2214     cropfactor(1),
2215     aspect(1),
2216     control_lines()
2217 {
2218 }
2219 
operator ==(const PerspectiveParams & other) const2220 bool PerspectiveParams::operator ==(const PerspectiveParams& other) const
2221 {
2222     return
2223         enabled == other.enabled
2224         && horizontal == other.horizontal
2225         && vertical == other.vertical
2226         && angle == other.angle
2227         && shear == other.shear
2228         && flength == other.flength
2229         && cropfactor == other.cropfactor
2230         && aspect == other.aspect
2231         && control_lines == other.control_lines;
2232 }
2233 
operator !=(const PerspectiveParams & other) const2234 bool PerspectiveParams::operator !=(const PerspectiveParams& other) const
2235 {
2236     return !(*this == other);
2237 }
2238 
GradientParams()2239 GradientParams::GradientParams() :
2240     enabled(false),
2241     degree(0.0),
2242     feather(25),
2243     strength(0.60),
2244     centerX(0),
2245     centerY(0)
2246 {
2247 }
2248 
operator ==(const GradientParams & other) const2249 bool GradientParams::operator ==(const GradientParams& other) const
2250 {
2251     return
2252         enabled == other.enabled
2253         && degree == other.degree
2254         && feather == other.feather
2255         && strength == other.strength
2256         && centerX == other.centerX
2257         && centerY == other.centerY;
2258 }
2259 
operator !=(const GradientParams & other) const2260 bool GradientParams::operator !=(const GradientParams& other) const
2261 {
2262     return !(*this == other);
2263 }
2264 
PCVignetteParams()2265 PCVignetteParams::PCVignetteParams() :
2266     enabled(false),
2267     strength(0.60),
2268     feather(50),
2269     roundness(50),
2270     centerX(0),
2271     centerY(0)
2272 {
2273 }
2274 
operator ==(const PCVignetteParams & other) const2275 bool PCVignetteParams::operator ==(const PCVignetteParams& other) const
2276 {
2277     return
2278         enabled == other.enabled
2279         && strength == other.strength
2280         && feather == other.feather
2281         && roundness == other.roundness
2282         && centerX == other.centerX
2283         && centerY == other.centerY;
2284 }
2285 
operator !=(const PCVignetteParams & other) const2286 bool PCVignetteParams::operator !=(const PCVignetteParams& other) const
2287 {
2288     return !(*this == other);
2289 }
2290 
VignettingParams()2291 VignettingParams::VignettingParams() :
2292     enabled(false),
2293     amount(0),
2294     radius(50),
2295     strength(1),
2296     centerX(0),
2297     centerY(0)
2298 {
2299 }
2300 
operator ==(const VignettingParams & other) const2301 bool VignettingParams::operator ==(const VignettingParams& other) const
2302 {
2303     return
2304         enabled == other.enabled
2305         && amount == other.amount
2306         && radius == other.radius
2307         && strength == other.strength
2308         && centerX == other.centerX
2309         && centerY == other.centerY;
2310 }
2311 
operator !=(const VignettingParams & other) const2312 bool VignettingParams::operator !=(const VignettingParams& other) const
2313 {
2314     return !(*this == other);
2315 }
2316 
ChannelMixerParams()2317 ChannelMixerParams::ChannelMixerParams() :
2318     enabled(false),
2319     mode(RGB_MATRIX),
2320     red{1000, 0, 0},
2321     green{0, 1000, 0},
2322     blue{0, 0, 1000},
2323     hue_tweak{0, 0, 0},
2324     sat_tweak{0, 0, 0}
2325 {
2326 }
2327 
operator ==(const ChannelMixerParams & other) const2328 bool ChannelMixerParams::operator ==(const ChannelMixerParams& other) const
2329 {
2330     if (enabled != other.enabled || mode != other.mode) {
2331         return false;
2332     }
2333 
2334     for (unsigned int i = 0; i < 3; ++i) {
2335         if (
2336             red[i] != other.red[i]
2337             || green[i] != other.green[i]
2338             || blue[i] != other.blue[i]
2339             || hue_tweak[i] != other.hue_tweak[i]
2340             || sat_tweak[i] != other.sat_tweak[i]
2341         ) {
2342             return false;
2343         }
2344     }
2345 
2346     return true;
2347 }
2348 
operator !=(const ChannelMixerParams & other) const2349 bool ChannelMixerParams::operator !=(const ChannelMixerParams& other) const
2350 {
2351     return !(*this == other);
2352 }
2353 
BlackWhiteParams()2354 BlackWhiteParams::BlackWhiteParams() :
2355     enabled(false),
2356     filter("None"),
2357     setting("RGB-Rel"),
2358     mixerRed(33),
2359     mixerGreen(33),
2360     mixerBlue(33),
2361     gammaRed(0),
2362     gammaGreen(0),
2363     gammaBlue(0),
2364     colorCast(0, 0, false)
2365 {
2366 }
2367 
operator ==(const BlackWhiteParams & other) const2368 bool BlackWhiteParams::operator ==(const BlackWhiteParams& other) const
2369 {
2370     return
2371         enabled == other.enabled
2372         && filter == other.filter
2373         && setting == other.setting
2374         && mixerRed == other.mixerRed
2375         && mixerGreen == other.mixerGreen
2376         && mixerBlue == other.mixerBlue
2377         && gammaRed == other.gammaRed
2378         && gammaGreen == other.gammaGreen
2379         && gammaBlue == other.gammaBlue
2380         && colorCast == other.colorCast;
2381 }
2382 
operator !=(const BlackWhiteParams & other) const2383 bool BlackWhiteParams::operator !=(const BlackWhiteParams& other) const
2384 {
2385     return !(*this == other);
2386 }
2387 
2388 
HSLEqualizerParams()2389 HSLEqualizerParams::HSLEqualizerParams():
2390     enabled(false),
2391     hCurve{FCT_Linear},
2392     sCurve{FCT_Linear},
2393     lCurve{FCT_Linear},
2394     smoothing(0)
2395 {
2396 }
2397 
2398 
operator ==(const HSLEqualizerParams & other) const2399 bool HSLEqualizerParams::operator==(const HSLEqualizerParams &other) const
2400 {
2401     return enabled == other.enabled
2402         && hCurve == other.hCurve
2403         && sCurve == other.sCurve
2404         && lCurve == other.lCurve
2405         && smoothing == other.smoothing;
2406 }
2407 
2408 
operator !=(const HSLEqualizerParams & other) const2409 bool HSLEqualizerParams::operator!=(const HSLEqualizerParams &other) const
2410 {
2411     return !(*this == other);
2412 }
2413 
2414 
CACorrParams()2415 CACorrParams::CACorrParams() :
2416     enabled(false),
2417     red(0.0),
2418     blue(0.0)
2419 {
2420 }
2421 
operator ==(const CACorrParams & other) const2422 bool CACorrParams::operator ==(const CACorrParams& other) const
2423 {
2424     return
2425         enabled == other.enabled
2426         && red == other.red
2427         && blue == other.blue;
2428 }
2429 
operator !=(const CACorrParams & other) const2430 bool CACorrParams::operator !=(const CACorrParams& other) const
2431 {
2432     return !(*this == other);
2433 }
2434 
ResizeParams()2435 ResizeParams::ResizeParams() :
2436     enabled(false),
2437     scale(1.0),
2438     appliesTo("Cropped area"),
2439     dataspec(3),
2440     width(900),
2441     height(900),
2442     allowUpscaling(false),
2443     ppi(300),
2444     unit(PX)
2445 {
2446 }
2447 
operator ==(const ResizeParams & other) const2448 bool ResizeParams::operator ==(const ResizeParams& other) const
2449 {
2450     return
2451         enabled == other.enabled
2452         && scale == other.scale
2453         && appliesTo == other.appliesTo
2454         && dataspec == other.dataspec
2455         && width == other.width
2456         && height == other.height
2457         && allowUpscaling == other.allowUpscaling
2458         && ppi == other.ppi
2459         && unit == other.unit;
2460 }
2461 
operator !=(const ResizeParams & other) const2462 bool ResizeParams::operator !=(const ResizeParams& other) const
2463 {
2464     return !(*this == other);
2465 }
2466 
2467 
get_width() const2468 int ResizeParams::get_width() const
2469 {
2470     switch (unit) {
2471     case PX: return width;
2472     case CM: return std::round(ppi * (width / 2.54));
2473     case INCHES: return std::round(ppi * width);
2474     default:
2475         assert(false);
2476         return width;
2477     }
2478 }
2479 
2480 
get_height() const2481 int ResizeParams::get_height() const
2482 {
2483     switch (unit) {
2484     case PX: return height;
2485     case CM: return std::round(ppi * (height / 2.54));
2486     case INCHES: return std::round(ppi * height);
2487     default:
2488         assert(false);
2489         return height;
2490     }
2491 }
2492 
2493 
2494 const Glib::ustring ColorManagementParams::NoICMString = Glib::ustring("No ICM: sRGB output");
2495 const Glib::ustring ColorManagementParams::NoProfileString = Glib::ustring("(none)");
2496 
ColorManagementParams()2497 ColorManagementParams::ColorManagementParams() :
2498     inputProfile("(cameraICC)"),
2499     toneCurve(false),
2500     applyLookTable(false),
2501     applyBaselineExposureOffset(true),
2502     applyHueSatMap(true),
2503     dcpIlluminant(0),
2504     workingProfile("ProPhoto"),
2505     outputProfile(options.rtSettings.srgb),
2506     outputIntent(RI_RELATIVE),
2507     outputBPC(true)
2508 {
2509 }
2510 
operator ==(const ColorManagementParams & other) const2511 bool ColorManagementParams::operator ==(const ColorManagementParams& other) const
2512 {
2513     return
2514         inputProfile == other.inputProfile
2515         && toneCurve == other.toneCurve
2516         && applyLookTable == other.applyLookTable
2517         && applyBaselineExposureOffset == other.applyBaselineExposureOffset
2518         && applyHueSatMap == other.applyHueSatMap
2519         && dcpIlluminant == other.dcpIlluminant
2520         && workingProfile == other.workingProfile
2521         && outputProfile == other.outputProfile
2522         && outputIntent == other.outputIntent
2523         && outputBPC == other.outputBPC;
2524 }
2525 
operator !=(const ColorManagementParams & other) const2526 bool ColorManagementParams::operator !=(const ColorManagementParams& other) const
2527 {
2528     return !(*this == other);
2529 }
2530 
2531 
FilmSimulationParams()2532 FilmSimulationParams::FilmSimulationParams() :
2533     enabled(false),
2534     strength(100)
2535 {
2536 }
2537 
operator ==(const FilmSimulationParams & other) const2538 bool FilmSimulationParams::operator ==(const FilmSimulationParams& other) const
2539 {
2540     return
2541         enabled == other.enabled
2542         && clutFilename == other.clutFilename
2543         && strength == other.strength;
2544 }
2545 
operator !=(const FilmSimulationParams & other) const2546 bool FilmSimulationParams::operator !=(const FilmSimulationParams& other) const
2547 {
2548     return !(*this == other);
2549 }
2550 
2551 
SoftLightParams()2552 SoftLightParams::SoftLightParams() :
2553     enabled(false),
2554     strength(30)
2555 {
2556 }
2557 
operator ==(const SoftLightParams & other) const2558 bool SoftLightParams::operator ==(const SoftLightParams& other) const
2559 {
2560     return
2561         enabled == other.enabled
2562         && strength == other.strength;
2563 }
2564 
operator !=(const SoftLightParams & other) const2565 bool SoftLightParams::operator !=(const SoftLightParams& other) const
2566 {
2567     return !(*this == other);
2568 }
2569 
2570 
DehazeParams()2571 DehazeParams::DehazeParams() :
2572     enabled(false),
2573     strength{
2574         FCT_MinMaxCPoints,
2575         0.0,
2576         0.75,
2577         0.0,
2578         0.0,
2579         1.0,
2580         0.75,
2581         0.0,
2582         0.0
2583     },
2584     showDepthMap(false),
2585     depth(25),
2586     luminance(false),
2587     blackpoint(0)
2588 {
2589 }
2590 
operator ==(const DehazeParams & other) const2591 bool DehazeParams::operator ==(const DehazeParams& other) const
2592 {
2593     return
2594         enabled == other.enabled
2595         && strength == other.strength
2596         && showDepthMap == other.showDepthMap
2597         && depth == other.depth
2598         && luminance == other.luminance
2599         && blackpoint == other.blackpoint;
2600 }
2601 
operator !=(const DehazeParams & other) const2602 bool DehazeParams::operator !=(const DehazeParams& other) const
2603 {
2604     return !(*this == other);
2605 }
2606 
2607 
GrainParams()2608 GrainParams::GrainParams():
2609     enabled(false),
2610     iso(400),
2611     strength(25),
2612     scale(100)
2613 {
2614 }
2615 
operator ==(const GrainParams & other) const2616 bool GrainParams::operator==(const GrainParams &other) const
2617 {
2618     return enabled == other.enabled
2619         && iso == other.iso
2620         && strength == other.strength
2621         && scale == other.scale;
2622 }
2623 
operator !=(const GrainParams & other) const2624 bool GrainParams::operator!=(const GrainParams &other) const
2625 {
2626     return !(*this == other);
2627 }
2628 
2629 
Region()2630 SmoothingParams::Region::Region():
2631     mode(Mode::GUIDED),
2632     channel(Channel::RGB),
2633     radius(0),
2634     sigma(0),
2635     epsilon(0),
2636     iterations(1),
2637     falloff(1),
2638     nldetail(50),
2639     nlstrength(0)
2640 {
2641 }
2642 
2643 
operator ==(const Region & other) const2644 bool SmoothingParams::Region::operator==(const Region &other) const
2645 {
2646     return mode == other.mode
2647         && channel == other.channel
2648         && radius == other.radius
2649         && sigma == other.sigma
2650         && epsilon == other.epsilon
2651         && iterations == other.iterations
2652         && falloff == other.falloff
2653         && nlstrength == other.nlstrength
2654         && nldetail == other.nldetail;
2655 }
2656 
2657 
operator !=(const Region & other) const2658 bool SmoothingParams::Region::operator!=(const Region &other) const
2659 {
2660     return !(*this == other);
2661 }
2662 
2663 
SmoothingParams()2664 SmoothingParams::SmoothingParams():
2665     enabled(false),
2666     regions{Region()},
2667     labmasks{Mask()},
2668     showMask(-1)
2669 {
2670 }
2671 
2672 
operator ==(const SmoothingParams & other) const2673 bool SmoothingParams::operator==(const SmoothingParams &other) const
2674 {
2675     return enabled == other.enabled
2676         && regions == other.regions
2677         && labmasks == other.labmasks
2678         && showMask == other.showMask;
2679 }
2680 
2681 
operator !=(const SmoothingParams & other) const2682 bool SmoothingParams::operator!=(const SmoothingParams &other) const
2683 {
2684     return !(*this == other);
2685 }
2686 
2687 
Region()2688 ColorCorrectionParams::Region::Region():
2689     a(0),
2690     b(0),
2691     abscale(1),
2692     inSaturation(0),
2693     outSaturation(0),
2694     slope{1,1,1},
2695     offset{0,0,0},
2696     power{1,1,1},
2697     pivot{1,1,1},
2698     hue{0,0,0},
2699     sat{0,0,0},
2700     factor{0,0,0},
2701     rgbluminance(false),
2702     mode(ColorCorrectionParams::Mode::YUV)
2703 {
2704 }
2705 
2706 
operator ==(const Region & other) const2707 bool ColorCorrectionParams::Region::operator==(const Region &other) const
2708 {
2709     return a == other.a
2710         && b == other.b
2711         && abscale == other.abscale
2712         && inSaturation == other.inSaturation
2713         && outSaturation == other.outSaturation
2714         && slope == other.slope
2715         && offset == other.offset
2716         && power == other.power
2717         && pivot == other.pivot
2718         && hue == other.hue
2719         && sat == other.sat
2720         && factor == other.factor
2721         && rgbluminance == other.rgbluminance
2722         && mode == other.mode;
2723 }
2724 
2725 
operator !=(const Region & other) const2726 bool ColorCorrectionParams::Region::operator!=(const Region &other) const
2727 {
2728     return !(*this == other);
2729 }
2730 
2731 
ColorCorrectionParams()2732 ColorCorrectionParams::ColorCorrectionParams():
2733     enabled(false),
2734     regions{Region()},
2735     labmasks{Mask()},
2736     showMask(-1)
2737 {
2738 }
2739 
2740 
operator ==(const ColorCorrectionParams & other) const2741 bool ColorCorrectionParams::operator==(const ColorCorrectionParams &other) const
2742 {
2743     return enabled == other.enabled
2744         && regions == other.regions
2745         && labmasks == other.labmasks
2746         && showMask == other.showMask;
2747 }
2748 
2749 
operator !=(const ColorCorrectionParams & other) const2750 bool ColorCorrectionParams::operator!=(const ColorCorrectionParams &other) const
2751 {
2752     return !(*this == other);
2753 }
2754 
2755 
BayerSensor()2756 RAWParams::BayerSensor::BayerSensor() :
2757     method(Method::RCD),
2758     border(4),
2759     imageNum(0),
2760     ccSteps(0),
2761     black0(0.0),
2762     black1(0.0),
2763     black2(0.0),
2764     black3(0.0),
2765     twogreen(true),
2766     linenoise(0),
2767     linenoiseDirection(LineNoiseDirection::BOTH),
2768     greenthresh(0),
2769     dcb_iterations(2),
2770     lmmse_iterations(2),
2771     dualDemosaicAutoContrast(true),
2772     dualDemosaicContrast(20),
2773     pixelShiftMotionCorrectionMethod(PSMotionCorrectionMethod::AUTO),
2774     pixelShiftEperIso(0.0),
2775     pixelShiftSigma(1.0),
2776     pixelShiftShowMotion(false),
2777     pixelShiftShowMotionMaskOnly(false),
2778     pixelShiftHoleFill(true),
2779     pixelShiftMedian(false),
2780     pixelShiftGreen(true),
2781     pixelShiftBlur(true),
2782     pixelShiftSmoothFactor(0.7),
2783     pixelShiftEqualBright(false),
2784     pixelShiftEqualBrightChannel(false),
2785     pixelShiftNonGreenCross(true),
2786     pixelShiftDemosaicMethod(getPSDemosaicMethodString(PSDemosaicMethod::AMAZE)),
2787     dcb_enhance(true),
2788     pdafLinesFilter(false),
2789     dynamicRowNoiseFilter(false),
2790     enable_black(false),
2791     enable_preproc(false)
2792 {
2793 }
2794 
operator ==(const BayerSensor & other) const2795 bool RAWParams::BayerSensor::operator ==(const BayerSensor& other) const
2796 {
2797     return
2798         method == other.method
2799         && border == other.border
2800         && imageNum == other.imageNum
2801         && ccSteps == other.ccSteps
2802         && black0 == other.black0
2803         && black1 == other.black1
2804         && black2 == other.black2
2805         && black3 == other.black3
2806         && twogreen == other.twogreen
2807         && linenoise == other.linenoise
2808         && linenoiseDirection == other.linenoiseDirection
2809         && greenthresh == other.greenthresh
2810         && dcb_iterations == other.dcb_iterations
2811         && lmmse_iterations == other.lmmse_iterations
2812         && dualDemosaicAutoContrast == other.dualDemosaicAutoContrast
2813         && dualDemosaicContrast == other.dualDemosaicContrast
2814         && pixelShiftMotionCorrectionMethod == other.pixelShiftMotionCorrectionMethod
2815         && pixelShiftEperIso == other.pixelShiftEperIso
2816         && pixelShiftSigma == other.pixelShiftSigma
2817         && pixelShiftShowMotion == other.pixelShiftShowMotion
2818         && pixelShiftShowMotionMaskOnly == other.pixelShiftShowMotionMaskOnly
2819         && pixelShiftHoleFill == other.pixelShiftHoleFill
2820         && pixelShiftMedian == other.pixelShiftMedian
2821         && pixelShiftGreen == other.pixelShiftGreen
2822         && pixelShiftBlur == other.pixelShiftBlur
2823         && pixelShiftSmoothFactor == other.pixelShiftSmoothFactor
2824         && pixelShiftEqualBright == other.pixelShiftEqualBright
2825         && pixelShiftEqualBrightChannel == other.pixelShiftEqualBrightChannel
2826         && pixelShiftNonGreenCross == other.pixelShiftNonGreenCross
2827         && pixelShiftDemosaicMethod == other.pixelShiftDemosaicMethod
2828         && dcb_enhance == other.dcb_enhance
2829         && pdafLinesFilter == other.pdafLinesFilter
2830         && dynamicRowNoiseFilter == other.dynamicRowNoiseFilter
2831         && enable_black == other.enable_black
2832         && enable_preproc == other.enable_preproc;
2833 }
2834 
operator !=(const BayerSensor & other) const2835 bool RAWParams::BayerSensor::operator !=(const BayerSensor& other) const
2836 {
2837     return !(*this == other);
2838 }
2839 
setPixelShiftDefaults()2840 void RAWParams::BayerSensor::setPixelShiftDefaults()
2841 {
2842     pixelShiftMotionCorrectionMethod = RAWParams::BayerSensor::PSMotionCorrectionMethod::AUTO;
2843     pixelShiftEperIso = 0.0;
2844     pixelShiftSigma = 1.0;
2845     pixelShiftHoleFill = true;
2846     pixelShiftMedian = false;
2847     pixelShiftGreen = true;
2848     pixelShiftBlur = true;
2849     pixelShiftSmoothFactor = 0.7;
2850     pixelShiftEqualBright = false;
2851     pixelShiftEqualBrightChannel = false;
2852     pixelShiftNonGreenCross = true;
2853     pixelShiftDemosaicMethod = getPSDemosaicMethodString(PSDemosaicMethod::AMAZE);
2854 }
2855 
getMethodStrings()2856 const std::vector<const char*>& RAWParams::BayerSensor::getMethodStrings()
2857 {
2858     static const std::vector<const char*> method_strings {
2859         "amaze",
2860         "rcd",
2861         "lmmse",
2862         "igv",
2863         "amazebilinear",
2864         "rcdbilinear",
2865         "vng4",
2866         "fast",
2867         "mono",
2868         "pixelshift",
2869         "none"
2870         // "amazevng4",
2871         // "rcdvng4",
2872         // "dcb",
2873         // "dcbbilinear",
2874         // "dcbvng4",
2875         // "ahd",
2876         // "eahd",
2877         // "hphd"
2878     };
2879     return method_strings;
2880 }
2881 
getMethodString(Method method)2882 Glib::ustring RAWParams::BayerSensor::getMethodString(Method method)
2883 {
2884     size_t i = toUnderlying(method);
2885     auto &v = getMethodStrings();
2886     return i < v.size() ? v[i] : "";
2887 }
2888 
getPSDemosaicMethodStrings()2889 const std::vector<const char*>& RAWParams::BayerSensor::getPSDemosaicMethodStrings()
2890 {
2891     static const std::vector<const char*> method_strings {
2892         "amaze",
2893         "amazevng4",
2894         "lmmse"
2895     };
2896     return method_strings;
2897 }
2898 
getPSDemosaicMethodString(PSDemosaicMethod method)2899 Glib::ustring RAWParams::BayerSensor::getPSDemosaicMethodString(PSDemosaicMethod method)
2900 {
2901     return getPSDemosaicMethodStrings()[toUnderlying(method)];
2902 }
2903 
2904 
2905 
XTransSensor()2906 RAWParams::XTransSensor::XTransSensor() :
2907     method(Method::THREE_PASS),
2908     dualDemosaicAutoContrast(true),
2909     dualDemosaicContrast(20),
2910     border(7),
2911     ccSteps(0),
2912     blackred(0.0),
2913     blackgreen(0.0),
2914     blackblue(0.0),
2915     enable_black(false)
2916 {
2917 }
2918 
operator ==(const XTransSensor & other) const2919 bool RAWParams::XTransSensor::operator ==(const XTransSensor& other) const
2920 {
2921     return
2922         method == other.method
2923         && dualDemosaicAutoContrast == other.dualDemosaicAutoContrast
2924         && dualDemosaicContrast == other.dualDemosaicContrast
2925         && border == other.border
2926         && ccSteps == other.ccSteps
2927         && blackred == other.blackred
2928         && blackgreen == other.blackgreen
2929         && blackblue == other.blackblue
2930         && enable_black == other.enable_black;
2931 }
2932 
operator !=(const XTransSensor & other) const2933 bool RAWParams::XTransSensor::operator !=(const XTransSensor& other) const
2934 {
2935     return !(*this == other);
2936 }
2937 
getMethodStrings()2938 const std::vector<const char*>& RAWParams::XTransSensor::getMethodStrings()
2939 {
2940     static const std::vector<const char*> method_strings {
2941         "4-pass",
2942         "3-pass (best)",
2943         "2-pass",
2944         "1-pass (medium)",
2945         "fast",
2946         "mono",
2947         "none"
2948     };
2949     return method_strings;
2950 }
2951 
getMethodString(Method method)2952 Glib::ustring RAWParams::XTransSensor::getMethodString(Method method)
2953 {
2954     return getMethodStrings()[toUnderlying(method)];
2955 }
2956 
RAWParams()2957 RAWParams::RAWParams() :
2958     df_autoselect(false),
2959     ff_AutoSelect(false),
2960     ff_BlurRadius(32),
2961     ff_BlurType(getFlatFieldBlurTypeString(FlatFieldBlurType::AREA)),
2962     ff_AutoClipControl(false),
2963     ff_clipControl(0),
2964     ff_embedded(false),
2965     ca_autocorrect(false),
2966     ca_avoidcolourshift(true),
2967     caautoiterations(2),
2968     cared(0.0),
2969     cablue(0.0),
2970     expos(1.0),
2971     hotPixelFilter(false),
2972     deadPixelFilter(false),
2973     hotdeadpix_thresh(100),
2974     enable_darkframe(false),
2975     enable_flatfield(false),
2976     enable_ca(false),
2977     enable_hotdeadpix(false),
2978     enable_whitepoint(false)
2979 {
2980 }
2981 
operator ==(const RAWParams & other) const2982 bool RAWParams::operator ==(const RAWParams& other) const
2983 {
2984     return
2985         bayersensor == other.bayersensor
2986         && xtranssensor == other.xtranssensor
2987         && dark_frame == other.dark_frame
2988         && df_autoselect == other.df_autoselect
2989         && ff_file == other.ff_file
2990         && ff_AutoSelect == other.ff_AutoSelect
2991         && ff_BlurRadius == other.ff_BlurRadius
2992         && ff_BlurType == other.ff_BlurType
2993         && ff_AutoClipControl == other.ff_AutoClipControl
2994         && ff_clipControl == other.ff_clipControl
2995         && ff_embedded == other.ff_embedded
2996         && ca_autocorrect == other.ca_autocorrect
2997         && ca_avoidcolourshift == other.ca_avoidcolourshift
2998         && caautoiterations == other.caautoiterations
2999         && cared == other.cared
3000         && cablue == other.cablue
3001         && expos == other.expos
3002         && hotPixelFilter == other.hotPixelFilter
3003         && deadPixelFilter == other.deadPixelFilter
3004         && hotdeadpix_thresh == other.hotdeadpix_thresh
3005         && enable_darkframe == other.enable_darkframe
3006         && enable_flatfield == other.enable_flatfield
3007         && enable_ca == other.enable_ca
3008         && enable_hotdeadpix == other.enable_hotdeadpix
3009         && enable_whitepoint == other.enable_whitepoint;
3010 }
3011 
operator !=(const RAWParams & other) const3012 bool RAWParams::operator !=(const RAWParams& other) const
3013 {
3014     return !(*this == other);
3015 }
3016 
getFlatFieldBlurTypeStrings()3017 const std::vector<const char*>& RAWParams::getFlatFieldBlurTypeStrings()
3018 {
3019     static const std::vector<const char*> blur_type_strings {
3020         "Area Flatfield",
3021         "Vertical Flatfield",
3022         "Horizontal Flatfield",
3023         "V+H Flatfield"
3024     };
3025     return blur_type_strings;
3026 }
3027 
getFlatFieldBlurTypeString(FlatFieldBlurType type)3028 Glib::ustring RAWParams::getFlatFieldBlurTypeString(FlatFieldBlurType type)
3029 {
3030     return getFlatFieldBlurTypeStrings()[toUnderlying(type)];
3031 }
3032 
3033 
FilmNegativeParams()3034 FilmNegativeParams::FilmNegativeParams() :
3035     enabled(false),
3036     redRatio(1.36),
3037     greenExp(1.5),
3038     blueRatio(0.86),
3039     redBase(0),
3040     greenBase(0),
3041     blueBase(0)
3042 {
3043 }
3044 
operator ==(const FilmNegativeParams & other) const3045 bool FilmNegativeParams::operator ==(const FilmNegativeParams& other) const
3046 {
3047     return
3048         enabled == other.enabled
3049         && redRatio == other.redRatio
3050         && greenExp == other.greenExp
3051         && blueRatio == other.blueRatio
3052         && redBase == other.redBase
3053         && greenBase == other.greenBase
3054         && blueBase == other.blueBase;
3055 }
3056 
operator !=(const FilmNegativeParams & other) const3057 bool FilmNegativeParams::operator !=(const FilmNegativeParams& other) const
3058 {
3059     return !(*this == other);
3060 }
3061 
3062 
3063 namespace {
3064 
3065 const std::map<Glib::ustring, Glib::ustring> exif_keys = {
3066     {"Copyright", "Exif.Image.Copyright"},
3067     {"Artist", "Exif.Image.Artist"},
3068     {"ImageDescription", "Exif.Image.ImageDescription"},
3069     {"Exif.UserComment", "Exif.Photo.UserComment"},
3070     {"ISOSpeed", "Exif.Photo.ISOSpeedRatings"},
3071     {"FNumber", "Exif.Photo.FNumber"},
3072     {"ShutterSpeed", "Exif.Photo.ExposureTime"},
3073     {"FocalLength", "Exif.Photo.FocalLength"},
3074     {"ExpComp", "Exif.Photo.ExposureBiasValue"},
3075     {"Flash", "Exif.Photo.Flash"},
3076     {"Make", "Exif.Image.Make"},
3077     {"Model", "Exif.Image.Model"},
3078     {"Lens", "Exif.Photo.LensModel"},
3079     {"DateTime", "Exif.Photo.DateTimeOriginal"},
3080     {"XResolution", "Exif.Image.XResolution"},
3081     {"YResolution", "Exif.Image.YResolution"}
3082 };
3083 
3084 const std::map<Glib::ustring, Glib::ustring> iptc_keys = {
3085     {"Title", "Iptc.Application2.ObjectName"},
3086     {"Category", "Iptc.Application2.Category"},
3087     {"SupplementalCategories", "Iptc.Application2.SuppCategory"},
3088     {"Keywords", "Iptc.Application2.Keywords"},
3089     {"Instructions", "Iptc.Application2.SpecialInstructions"},
3090     {"DateCreated", "Iptc.Application2.DateCreated"},
3091     {"Creator", "Iptc.Application2.Byline"},
3092     {"CreatorJobTitle", "Iptc.Application2.BylineTitle"},
3093     {"City", "Iptc.Application2.City"},
3094     {"Province", "Iptc.Application2.ProvinceState"},
3095     {"Country", "Iptc.Application2.CountryName"},
3096     {"TransReference", "Iptc.Application2.TransmissionReference"},
3097     {"Headline", "Iptc.Application2.Headline"},
3098     {"Credit", "Iptc.Application2.Credit"},
3099     {"Source", "Iptc.Application2.Source"},
3100     {"Copyright", "Iptc.Application2.Copyright"},
3101     {"Caption", "Iptc.Application2.Caption"},
3102     {"CaptionWriter", "Iptc.Application2.Writer"}
3103 };
3104 
3105 } // namespace
3106 
3107 
3108 std::vector<std::string> MetaDataParams::basicExifKeys = {
3109     "Exif.Image.Copyright",
3110     "Exif.Image.Artist",
3111     "Exif.Image.ImageDescription",
3112     "Exif.Photo.UserComment",
3113     "Exif.Image.Make",
3114     "Exif.Image.Model",
3115     "Exif.Photo.LensModel",
3116     "Exif.Photo.FNumber",
3117     "Exif.Photo.ExposureTime",
3118     "Exif.Photo.FocalLength",
3119     "Exif.Photo.ISOSpeedRatings",
3120     "Exif.Photo.ExposureBiasValue",
3121     "Exif.Photo.Flash",
3122     "Exif.Photo.DateTimeOriginal",
3123     "Exif.Image.XResolution",
3124     "Exif.Image.YResolution"
3125 };
3126 
3127 
MetaDataParams()3128 MetaDataParams::MetaDataParams():
3129     mode(MetaDataParams::EDIT),
3130     exifKeys{},
3131     exif{},
3132     iptc{}
3133 {
3134     exifKeys = basicExifKeys;
3135 }
3136 
3137 
operator ==(const MetaDataParams & other) const3138 bool MetaDataParams::operator==(const MetaDataParams &other) const
3139 {
3140     return mode == other.mode
3141         && exifKeys == other.exifKeys
3142         && exif == other.exif
3143         && iptc == other.iptc;
3144 }
3145 
operator !=(const MetaDataParams & other) const3146 bool MetaDataParams::operator!=(const MetaDataParams &other) const
3147 {
3148     return !(*this == other);
3149 }
3150 
3151 
ProcParams()3152 ProcParams::ProcParams()
3153 {
3154     setDefaults();
3155 }
3156 
setDefaults()3157 void ProcParams::setDefaults()
3158 {
3159     exposure = ExposureParams();
3160     saturation = SaturationParams();
3161     toneCurve = ToneCurveParams();
3162     labCurve = LabCurveParams();
3163     rgbCurves = RGBCurvesParams();
3164     localContrast = LocalContrastParams();
3165     sharpening = SharpeningParams();
3166     prsharpening = SharpeningParams();
3167     prsharpening.contrast = 25.0;
3168     prsharpening.method = "usm";
3169     wb = WBParams();
3170     defringe = DefringeParams();
3171     impulseDenoise = ImpulseDenoiseParams();
3172     denoise = DenoiseParams();
3173     textureBoost = TextureBoostParams();
3174     fattal = FattalToneMappingParams();
3175     logenc = LogEncodingParams();
3176     toneEqualizer = ToneEqualizerParams();
3177     crop = CropParams();
3178     coarse = CoarseTransformParams();
3179     commonTrans = CommonTransformParams();
3180     rotate = RotateParams();
3181     distortion = DistortionParams();
3182     lensProf = LensProfParams();
3183     perspective = PerspectiveParams();
3184     gradient = GradientParams();
3185     pcvignette = PCVignetteParams();
3186     vignetting = VignettingParams();
3187     chmixer = ChannelMixerParams();
3188     blackwhite = BlackWhiteParams();
3189     hsl = HSLEqualizerParams();
3190     cacorrection = CACorrParams();
3191     resize = ResizeParams();
3192     icm = ColorManagementParams();
3193     filmSimulation = FilmSimulationParams();
3194     softlight = SoftLightParams();
3195     dehaze = DehazeParams();
3196     grain = GrainParams();
3197     smoothing = SmoothingParams();
3198     colorcorrection = ColorCorrectionParams();
3199     raw = RAWParams();
3200     metadata = MetaDataParams();
3201     filmNegative = FilmNegativeParams();
3202     // exif.clear();
3203     // iptc.clear();
3204 
3205     spot = SpotParams();
3206 
3207     rank = -1;
3208     colorlabel = 0;
3209     inTrash = false;
3210 
3211     ppVersion = PPVERSION;
3212 }
3213 
3214 
save(ProgressListener * pl,const Glib::ustring & fname,const Glib::ustring & fname2,const ParamsEdited * pedited)3215 int ProcParams::save(ProgressListener *pl, const Glib::ustring& fname, const Glib::ustring& fname2, const ParamsEdited *pedited)
3216 {
3217     if (fname.empty() && fname2.empty()) {
3218         return 0;
3219     }
3220 
3221     Glib::ustring sPParams;
3222 
3223     try {
3224         KeyFile keyFile;
3225         int ret = save(pl, keyFile, pedited, fname);
3226         if (ret != 0) {
3227             return ret;
3228         }
3229 
3230         sPParams = keyFile.to_data();
3231     } catch (Glib::KeyFileError &exc) {
3232         if (pl) {
3233             pl->error(Glib::ustring::compose(M("PROCPARAMS_SAVE_ERROR"), fname, exc.what()));
3234         }
3235     }
3236 
3237     if (sPParams.empty()) {
3238         return 1;
3239     }
3240 
3241     int error1, error2;
3242     error1 = write(pl, fname, sPParams);
3243 
3244     if (!fname2.empty()) {
3245 
3246         error2 = write(pl, fname2, sPParams);
3247         // If at least one file has been saved, it's a success
3248         return error1 & error2;
3249     } else {
3250         return error1;
3251     }
3252 }
3253 
3254 
saveEmbedded(ProgressListener * pl,const Glib::ustring & fname)3255 int ProcParams::saveEmbedded(ProgressListener *pl, const Glib::ustring &fname)
3256 {
3257     if (fname.empty()) {
3258         return 0;
3259     }
3260 
3261     Glib::ustring sPParams;
3262 
3263     try {
3264         KeyFile keyFile;
3265         int ret = save(pl, keyFile, nullptr, fname);
3266         if (ret != 0) {
3267             return ret;
3268         }
3269 
3270         sPParams = keyFile.to_data();
3271     } catch (Glib::KeyFileError &exc) {
3272         if (pl) {
3273             pl->error(Glib::ustring::compose(M("PROCPARAMS_SAVE_ERROR"), fname, exc.what()));
3274         }
3275     }
3276 
3277     if (sPParams.empty()) {
3278         return 1;
3279     }
3280 
3281     try {
3282         // Exiv2Metadata md(fname, false);
3283         // md.load();
3284         // md.xmpData()["Xmp.ART.arp"] = to_xmp(sPParams);
3285         // md.saveToImage(pl, fname, true);
3286         Exiv2Metadata::embedProcParamsData(fname, to_xmp(sPParams));
3287         return 0;
3288     } catch (std::exception &exc) {
3289         if (pl) {
3290             pl->error(Glib::ustring::compose(M("PROCPARAMS_SAVE_ERROR"), fname, exc.what()));
3291         }
3292         return 1;
3293     }
3294 }
3295 
3296 
3297 
save(ProgressListener * pl,bool save_general,KeyFile & keyFile,const ParamsEdited * pedited,const Glib::ustring & fname) const3298 int ProcParams::save(ProgressListener *pl, bool save_general,
3299                      KeyFile &keyFile, const ParamsEdited *pedited,
3300                      const Glib::ustring &fname) const
3301 {
3302 #define RELEVANT_(n) (!pedited || pedited->n)
3303     try {
3304         Glib::ustring basedir = Glib::path_get_dirname(fname);
3305 
3306 // Version
3307         if (save_general) {
3308             keyFile.set_string("Version", "AppVersion", RTVERSION);
3309             keyFile.set_integer("Version", "Version", PPVERSION);
3310 
3311             if (RELEVANT_(general)) {
3312                 if (rank >= 0) {
3313                     saveToKeyfile("General", "Rank", rank, keyFile);
3314                 }
3315                 saveToKeyfile("General", "ColorLabel", colorlabel, keyFile);
3316                 saveToKeyfile("General", "InTrash", inTrash, keyFile);
3317             }
3318         }
3319 
3320 // Exposure
3321         if (RELEVANT_(exposure)) {
3322             saveToKeyfile("Exposure", "Enabled", exposure.enabled, keyFile);
3323             saveToKeyfile("Exposure", "Compensation", exposure.expcomp, keyFile);
3324             saveToKeyfile("Exposure", "Black", exposure.black, keyFile);
3325             Glib::ustring hr = "Off";
3326             switch (exposure.hrmode) {
3327             case ExposureParams::HR_OFF: hr = "Off"; break;
3328             case ExposureParams::HR_BLEND: hr = "Blend"; break;
3329             case ExposureParams::HR_COLOR: hr = "Color"; break;
3330             case ExposureParams::HR_COLORSOFT: hr = "ColorBlend"; break;
3331             }
3332             saveToKeyfile("Exposure", "HLRecovery", hr, keyFile);
3333         }
3334 
3335 // Brightness, Contrast, Saturation
3336         if (RELEVANT_(saturation)) {
3337             saveToKeyfile("Saturation", "Enabled", saturation.enabled, keyFile);
3338             saveToKeyfile("Saturation", "Saturation", saturation.saturation, keyFile);
3339             saveToKeyfile("Saturation", "Vibrance", saturation.vibrance, keyFile);
3340         }
3341 
3342 // Tone curve
3343         if (RELEVANT_(toneCurve)) {
3344             saveToKeyfile("ToneCurve", "Enabled", toneCurve.enabled, keyFile);
3345             saveToKeyfile("ToneCurve", "Contrast", toneCurve.contrast, keyFile);
3346             saveToKeyfile("ToneCurve", "HistogramMatching", toneCurve.histmatching, keyFile);
3347             saveToKeyfile("ToneCurve", "CurveFromHistogramMatching", toneCurve.fromHistMatching, keyFile);
3348 
3349             const std::map<ToneCurveParams::TcMode, const char*> tc_mapping = {
3350                 {ToneCurveParams::TcMode::STD, "Standard"},
3351                 {ToneCurveParams::TcMode::FILMLIKE, "FilmLike"},
3352                 {ToneCurveParams::TcMode::SATANDVALBLENDING, "SatAndValueBlending"},
3353                 {ToneCurveParams::TcMode::WEIGHTEDSTD, "WeightedStd"},
3354                 {ToneCurveParams::TcMode::LUMINANCE, "Luminance"},
3355                 {ToneCurveParams::TcMode::PERCEPTUAL, "Perceptual"}
3356             };
3357 
3358             saveToKeyfile("ToneCurve", "CurveMode", tc_mapping, toneCurve.curveMode, keyFile);
3359             saveToKeyfile("ToneCurve", "CurveMode2", tc_mapping, toneCurve.curveMode2, keyFile);
3360 
3361             saveToKeyfile("ToneCurve", "Curve", toneCurve.curve, keyFile);
3362             saveToKeyfile("ToneCurve", "Curve2", toneCurve.curve2, keyFile);
3363             saveToKeyfile("ToneCurve", "Saturation", toneCurve.saturation, keyFile);
3364             saveToKeyfile("ToneCurve", "PerceptualStrength", toneCurve.perceptualStrength, keyFile);
3365         }
3366 
3367 // Local contrast
3368         if (RELEVANT_(localContrast)) {
3369             saveToKeyfile("Local Contrast", "Enabled", localContrast.enabled, keyFile);
3370             for (size_t j = 0; j < localContrast.regions.size(); ++j) {
3371                 std::string n = j ? std::string("_") + std::to_string(j) : std::string("");
3372                 auto &r = localContrast.regions[j];
3373                 putToKeyfile("Local Contrast", Glib::ustring("Contrast") + n, r.contrast, keyFile);
3374                 putToKeyfile("Local Contrast", Glib::ustring("Curve") + n, r.curve, keyFile);
3375                 localContrast.labmasks[j].save(keyFile, "Local Contrast", "", n);
3376             }
3377             saveToKeyfile("Local Contrast", "showMask", localContrast.showMask, keyFile);
3378         }
3379 
3380 
3381 // Channel mixer
3382         if (RELEVANT_(chmixer)) {
3383             saveToKeyfile("Channel Mixer", "Enabled", chmixer.enabled, keyFile);
3384             saveToKeyfile("Channel Mixer", "Mode", int(chmixer.mode), keyFile);
3385             Glib::ArrayHandle<int> rmix(chmixer.red, 3, Glib::OWNERSHIP_NONE);
3386             keyFile.set_integer_list("Channel Mixer", "Red", rmix);
3387             Glib::ArrayHandle<int> gmix(chmixer.green, 3, Glib::OWNERSHIP_NONE);
3388             keyFile.set_integer_list("Channel Mixer", "Green", gmix);
3389             Glib::ArrayHandle<int> bmix(chmixer.blue, 3, Glib::OWNERSHIP_NONE);
3390             keyFile.set_integer_list("Channel Mixer", "Blue", bmix);
3391             Glib::ArrayHandle<int> h(chmixer.hue_tweak, 3, Glib::OWNERSHIP_NONE);
3392             keyFile.set_integer_list("Channel Mixer", "HueTweak", h);
3393             Glib::ArrayHandle<int> s(chmixer.sat_tweak, 3, Glib::OWNERSHIP_NONE);
3394             keyFile.set_integer_list("Channel Mixer", "SatTweak", s);
3395         }
3396 
3397 // Black & White
3398         if (RELEVANT_(blackwhite)) {
3399             saveToKeyfile("Black & White", "Enabled", blackwhite.enabled, keyFile);
3400             saveToKeyfile("Black & White", "Setting", blackwhite.setting, keyFile);
3401             saveToKeyfile("Black & White", "Filter", blackwhite.filter, keyFile);
3402             saveToKeyfile("Black & White", "MixerRed", blackwhite.mixerRed, keyFile);
3403             saveToKeyfile("Black & White", "MixerGreen", blackwhite.mixerGreen, keyFile);
3404             saveToKeyfile("Black & White", "MixerBlue", blackwhite.mixerBlue, keyFile);
3405             saveToKeyfile("Black & White", "GammaRed", blackwhite.gammaRed, keyFile);
3406             saveToKeyfile("Black & White", "GammaGreen", blackwhite.gammaGreen, keyFile);
3407             saveToKeyfile("Black & White", "GammaBlue", blackwhite.gammaBlue, keyFile);
3408             saveToKeyfile("Black & White", "ColorCast", blackwhite.colorCast.toVector(), keyFile);
3409         }
3410 
3411 // HSL equalizer
3412         if (RELEVANT_(hsl)) {
3413             saveToKeyfile("HSL Equalizer", "Enabled", hsl.enabled, keyFile);
3414             saveToKeyfile("HSL Equalizer", "HCurve", hsl.hCurve, keyFile);
3415             saveToKeyfile("HSL Equalizer", "SCurve", hsl.sCurve, keyFile);
3416             saveToKeyfile("HSL Equalizer", "LCurve", hsl.lCurve, keyFile);
3417             saveToKeyfile("HSL Equalizer", "Smoothing", hsl.smoothing, keyFile);
3418         }
3419 
3420 // Luma curve
3421         if (RELEVANT_(labCurve)) {
3422             saveToKeyfile("Luminance Curve", "Enabled", labCurve.enabled, keyFile);
3423             saveToKeyfile("Luminance Curve", "Brightness", labCurve.brightness, keyFile);
3424             saveToKeyfile("Luminance Curve", "Contrast", labCurve.contrast, keyFile);
3425             saveToKeyfile("Luminance Curve", "Chromaticity", labCurve.chromaticity, keyFile);
3426             saveToKeyfile("Luminance Curve", "LCurve", labCurve.lcurve, keyFile);
3427             saveToKeyfile("Luminance Curve", "aCurve", labCurve.acurve, keyFile);
3428             saveToKeyfile("Luminance Curve", "bCurve", labCurve.bcurve, keyFile);
3429         }
3430 
3431 // Sharpening
3432         if (RELEVANT_(sharpening)) {
3433             saveToKeyfile("Sharpening", "Enabled", sharpening.enabled, keyFile);
3434             saveToKeyfile("Sharpening", "Contrast", sharpening.contrast, keyFile);
3435             saveToKeyfile("Sharpening", "Method", sharpening.method, keyFile);
3436             saveToKeyfile("Sharpening", "Radius", sharpening.radius, keyFile);
3437             saveToKeyfile("Sharpening", "Amount", sharpening.amount, keyFile);
3438             saveToKeyfile("Sharpening", "Threshold", sharpening.threshold.toVector(), keyFile);
3439             saveToKeyfile("Sharpening", "OnlyEdges", sharpening.edgesonly, keyFile);
3440             saveToKeyfile("Sharpening", "EdgedetectionRadius", sharpening.edges_radius, keyFile);
3441             saveToKeyfile("Sharpening", "EdgeTolerance", sharpening.edges_tolerance, keyFile);
3442             saveToKeyfile("Sharpening", "HalocontrolEnabled", sharpening.halocontrol, keyFile);
3443             saveToKeyfile("Sharpening", "HalocontrolAmount", sharpening.halocontrol_amount, keyFile);
3444             saveToKeyfile("Sharpening", "DeconvRadius", sharpening.deconvradius, keyFile);
3445             saveToKeyfile("Sharpening", "DeconvAmount", sharpening.deconvamount, keyFile);
3446             saveToKeyfile("Sharpening", "DeconvAutoRadius", sharpening.deconvAutoRadius, keyFile);
3447             saveToKeyfile("Sharpening", "DeconvCornerBoost", sharpening.deconvCornerBoost, keyFile);
3448             saveToKeyfile("Sharpening", "DeconvCornerLatitude", sharpening.deconvCornerLatitude, keyFile);
3449         }
3450 
3451 // WB
3452         if (RELEVANT_(wb)) {
3453             saveToKeyfile("White Balance", "Enabled", wb.enabled, keyFile);
3454             std::string method = "Camera";
3455             switch (wb.method) {
3456             case WBParams::CAMERA:
3457                 method = "Camera";
3458                 break;
3459             case WBParams::AUTO:
3460                 method = "Auto";
3461                 break;
3462             case WBParams::CUSTOM_TEMP:
3463                 method = "CustomTemp";
3464                 break;
3465             case WBParams::CUSTOM_MULT:
3466             default:
3467                 method = "CustomMult";
3468                 break;
3469             }
3470             saveToKeyfile("White Balance", "Setting", method, keyFile);
3471             saveToKeyfile("White Balance", "Temperature", wb.temperature, keyFile);
3472             saveToKeyfile("White Balance", "Green", wb.green, keyFile);
3473             saveToKeyfile("White Balance", "Equal", wb.equal, keyFile);
3474             std::vector<double> m(wb.mult.begin(), wb.mult.end());
3475             saveToKeyfile("White Balance", "Multipliers", m, keyFile);
3476         }
3477 
3478 
3479 // Impulse denoise
3480         if (RELEVANT_(impulseDenoise)) {
3481             saveToKeyfile("Impulse Denoising", "Enabled", impulseDenoise.enabled, keyFile);
3482             saveToKeyfile("Impulse Denoising", "Threshold", impulseDenoise.thresh, keyFile);
3483         }
3484 
3485 // Defringe
3486         if (RELEVANT_(defringe)) {
3487             saveToKeyfile("Defringing", "Enabled", defringe.enabled, keyFile);
3488             saveToKeyfile("Defringing", "Radius", defringe.radius, keyFile);
3489             saveToKeyfile("Defringing", "Threshold", defringe.threshold, keyFile);
3490             saveToKeyfile("Defringing", "HueCurve", defringe.huecurve, keyFile);
3491         }
3492 
3493 // Dehaze
3494         if (RELEVANT_(dehaze)) {
3495             saveToKeyfile("Dehaze", "Enabled", dehaze.enabled, keyFile);
3496             saveToKeyfile("Dehaze", "Strength", dehaze.strength, keyFile);
3497             saveToKeyfile("Dehaze", "ShowDepthMap", dehaze.showDepthMap, keyFile);
3498             saveToKeyfile("Dehaze", "Depth", dehaze.depth, keyFile);
3499             saveToKeyfile("Dehaze", "Luminance", dehaze.luminance, keyFile);
3500             saveToKeyfile("Dehaze", "Blackpoint", dehaze.blackpoint, keyFile);
3501         }
3502 
3503 // Denoising
3504         if (RELEVANT_(denoise)) {
3505             saveToKeyfile("Denoise", "Enabled", denoise.enabled, keyFile);
3506             saveToKeyfile("Denoise", "ColorSpace", denoise.colorSpace == DenoiseParams::ColorSpace::LAB ? Glib::ustring("LAB") : Glib::ustring("RGB"), keyFile);
3507             saveToKeyfile("Denoise", "Aggressive", denoise.aggressive, keyFile);
3508             saveToKeyfile("Denoise", "Gamma", denoise.gamma, keyFile);
3509             saveToKeyfile("Denoise", "Luminance", denoise.luminance, keyFile);
3510             saveToKeyfile("Denoise", "LuminanceDetail", denoise.luminanceDetail, keyFile);
3511             saveToKeyfile("Denoise", "LuminanceDetailThreshold", denoise.luminanceDetailThreshold, keyFile);
3512             saveToKeyfile("Denoise", "ChrominanceMethod", int(denoise.chrominanceMethod), keyFile);
3513             saveToKeyfile("Denoise", "ChrominanceAutoFactor", denoise.chrominanceAutoFactor, keyFile);
3514             saveToKeyfile("Denoise", "Chrominance", denoise.chrominance, keyFile);
3515             saveToKeyfile("Denoise", "ChrominanceRedGreen", denoise.chrominanceRedGreen, keyFile);
3516             saveToKeyfile("Denoise", "ChrominanceBlueYellow", denoise.chrominanceBlueYellow, keyFile);
3517             saveToKeyfile("Denoise", "SmoothingEnabled", denoise.smoothingEnabled, keyFile);
3518             saveToKeyfile("Denoise", "GuidedChromaRadius", denoise.guidedChromaRadius, keyFile);
3519             saveToKeyfile("Denoise", "NLDetail", denoise.nlDetail, keyFile);
3520             saveToKeyfile("Denoise", "NLStrength", denoise.nlStrength, keyFile);
3521         }
3522 
3523 // TextureBoost
3524         if (RELEVANT_(textureBoost)) {
3525             saveToKeyfile("TextureBoost", "Enabled", textureBoost.enabled, keyFile);
3526             for (size_t j = 0; j < textureBoost.regions.size(); ++j) {
3527                 std::string n = j ? std::string("_") + std::to_string(j) : std::string("");
3528                 auto &r = textureBoost.regions[j];
3529                 putToKeyfile("TextureBoost", Glib::ustring("Strength") + n, r.strength, keyFile);
3530                 putToKeyfile("TextureBoost", Glib::ustring("DetailThreshold") + n, r.detailThreshold, keyFile);
3531                 putToKeyfile("TextureBoost", Glib::ustring("Iterations") + n, r.iterations, keyFile);
3532                 textureBoost.labmasks[j].save(keyFile, "TextureBoost", "", n);
3533             }
3534             saveToKeyfile("TextureBoost", "showMask", textureBoost.showMask, keyFile);
3535         }
3536 
3537 // Fattal
3538         if (RELEVANT_(fattal)) {
3539             saveToKeyfile("FattalToneMapping", "Enabled", fattal.enabled, keyFile);
3540             saveToKeyfile("FattalToneMapping", "Threshold", fattal.threshold, keyFile);
3541             saveToKeyfile("FattalToneMapping", "Amount", fattal.amount, keyFile);
3542             saveToKeyfile("FattalToneMapping", "SaturationControl", fattal.satcontrol, keyFile);
3543         }
3544 
3545 // Log encoding
3546         if (RELEVANT_(logenc)) {
3547             saveToKeyfile("LogEncoding", "Enabled", logenc.enabled, keyFile);
3548             saveToKeyfile("LogEncoding", "Auto", logenc.autocompute, keyFile);
3549             saveToKeyfile("LogEncoding", "AutoGain", logenc.autogain, keyFile);
3550             saveToKeyfile("LogEncoding", "Gain", logenc.gain, keyFile);
3551             saveToKeyfile("LogEncoding", "TargetGray", logenc.targetGray, keyFile);
3552             saveToKeyfile("LogEncoding", "BlackEv", logenc.blackEv, keyFile);
3553             saveToKeyfile("LogEncoding", "WhiteEv", logenc.whiteEv, keyFile);
3554             saveToKeyfile("LogEncoding", "Regularization", logenc.regularization, keyFile);
3555         }
3556 
3557 // ToneEqualizer
3558         if (RELEVANT_(toneEqualizer)) {
3559             saveToKeyfile("ToneEqualizer", "Enabled", toneEqualizer.enabled, keyFile);
3560             for (size_t i = 0; i < toneEqualizer.bands.size(); ++i) {
3561                 saveToKeyfile("ToneEqualizer", "Band" + std::to_string(i), toneEqualizer.bands[i], keyFile);
3562             }
3563             saveToKeyfile("ToneEqualizer", "Regularization", toneEqualizer.regularization, keyFile);
3564         }
3565 
3566 // Crop
3567         if (RELEVANT_(crop)) {
3568             saveToKeyfile("Crop", "Enabled", crop.enabled, keyFile);
3569             saveToKeyfile("Crop", "X", crop.x, keyFile);
3570             saveToKeyfile("Crop", "Y", crop.y, keyFile);
3571             saveToKeyfile("Crop", "W", crop.w, keyFile);
3572             saveToKeyfile("Crop", "H", crop.h, keyFile);
3573             saveToKeyfile("Crop", "FixedRatio", crop.fixratio, keyFile);
3574             saveToKeyfile("Crop", "Ratio", crop.ratio, keyFile);
3575             saveToKeyfile("Crop", "Orientation", crop.orientation, keyFile);
3576             saveToKeyfile("Crop", "Guide", crop.guide, keyFile);
3577         }
3578 
3579 // Coarse transformation
3580         if (RELEVANT_(coarse)) {
3581             saveToKeyfile("Coarse Transformation", "Rotate", coarse.rotate, keyFile);
3582             saveToKeyfile("Coarse Transformation", "HorizontalFlip", coarse.hflip, keyFile);
3583             saveToKeyfile("Coarse Transformation", "VerticalFlip", coarse.vflip, keyFile);
3584         }
3585 
3586 // Common properties for transformations
3587         if (RELEVANT_(commonTrans)) {
3588             saveToKeyfile("Common Properties for Transformations", "AutoFill", commonTrans.autofill, keyFile);
3589         }
3590 
3591 // Rotation
3592         if (RELEVANT_(rotate)) {
3593             saveToKeyfile("Rotation", "Enabled", rotate.enabled, keyFile);
3594             saveToKeyfile("Rotation", "Degree", rotate.degree, keyFile);
3595         }
3596 
3597 // Distortion
3598         if (RELEVANT_(distortion)) {
3599             saveToKeyfile("Distortion", "Enabled", distortion.enabled, keyFile);
3600             saveToKeyfile("Distortion", "Amount", distortion.amount, keyFile);
3601             saveToKeyfile("Distortion", "Auto", distortion.autocompute, keyFile);
3602         }
3603 
3604 // Lens profile
3605         if (RELEVANT_(lensProf)) {
3606             saveToKeyfile("LensProfile", "LcMode", lensProf.getMethodString(lensProf.lcMode), keyFile);
3607             saveToKeyfile("LensProfile", "LCPFile", filenameToUri(lensProf.lcpFile, basedir), keyFile);
3608             saveToKeyfile("LensProfile", "UseDistortion", lensProf.useDist, keyFile);
3609             saveToKeyfile("LensProfile", "UseVignette", lensProf.useVign, keyFile);
3610             saveToKeyfile("LensProfile", "UseCA", lensProf.useCA, keyFile);
3611             saveToKeyfile("LensProfile", "LFCameraMake", lensProf.lfCameraMake, keyFile);
3612             saveToKeyfile("LensProfile", "LFCameraModel", lensProf.lfCameraModel, keyFile);
3613             saveToKeyfile("LensProfile", "LFLens", lensProf.lfLens, keyFile);
3614         }
3615 
3616 // Perspective correction
3617         if (RELEVANT_(perspective)) {
3618             saveToKeyfile("Perspective", "Enabled", perspective.enabled, keyFile);
3619             saveToKeyfile("Perspective", "Horizontal", perspective.horizontal, keyFile);
3620             saveToKeyfile("Perspective", "Vertical", perspective.vertical, keyFile);
3621             saveToKeyfile("Perspective", "Angle", perspective.angle, keyFile);
3622             saveToKeyfile("Perspective", "Shear", perspective.shear, keyFile);
3623             saveToKeyfile("Perspective", "FocalLength", perspective.flength, keyFile);
3624             saveToKeyfile("Perspective", "CropFactor", perspective.cropfactor, keyFile);
3625             saveToKeyfile("Perspective", "Aspect", perspective.aspect, keyFile);
3626             saveToKeyfile("Perspective", "ControlLines", perspective.control_lines, keyFile);
3627         }
3628 
3629 // Gradient
3630         if (RELEVANT_(gradient)) {
3631             saveToKeyfile("Gradient", "Enabled", gradient.enabled, keyFile);
3632             saveToKeyfile("Gradient", "Degree", gradient.degree, keyFile);
3633             saveToKeyfile("Gradient", "Feather", gradient.feather, keyFile);
3634             saveToKeyfile("Gradient", "Strength", gradient.strength, keyFile);
3635             saveToKeyfile("Gradient", "CenterX", gradient.centerX, keyFile);
3636             saveToKeyfile("Gradient", "CenterY", gradient.centerY, keyFile);
3637         }
3638 
3639 // Post-crop vignette
3640         if (RELEVANT_(pcvignette)) {
3641             saveToKeyfile("PCVignette", "Enabled", pcvignette.enabled, keyFile);
3642             saveToKeyfile("PCVignette", "Strength", pcvignette.strength, keyFile);
3643             saveToKeyfile("PCVignette", "Feather", pcvignette.feather, keyFile);
3644             saveToKeyfile("PCVignette", "Roundness", pcvignette.roundness, keyFile);
3645             saveToKeyfile("PCVignette", "CenterX", pcvignette.centerX, keyFile);
3646             saveToKeyfile("PCVignette", "CenterY", pcvignette.centerY, keyFile);
3647         }
3648 
3649 // C/A correction
3650         if (RELEVANT_(cacorrection)) {
3651             saveToKeyfile("CACorrection", "Enabled", cacorrection.enabled, keyFile);
3652             saveToKeyfile("CACorrection", "Red", cacorrection.red, keyFile);
3653             saveToKeyfile("CACorrection", "Blue", cacorrection.blue, keyFile);
3654         }
3655 
3656 // Vignetting correction
3657         if (RELEVANT_(vignetting)) {
3658             saveToKeyfile("Vignetting Correction", "Enabled", vignetting.enabled, keyFile);
3659             saveToKeyfile("Vignetting Correction", "Amount", vignetting.amount, keyFile);
3660             saveToKeyfile("Vignetting Correction", "Radius", vignetting.radius, keyFile);
3661             saveToKeyfile("Vignetting Correction", "Strength", vignetting.strength, keyFile);
3662             saveToKeyfile("Vignetting Correction", "CenterX", vignetting.centerX, keyFile);
3663             saveToKeyfile("Vignetting Correction", "CenterY", vignetting.centerY, keyFile);
3664         }
3665 
3666 // Resize
3667         if (RELEVANT_(resize)) {
3668             saveToKeyfile("Resize", "Enabled", resize.enabled, keyFile);
3669             saveToKeyfile("Resize", "Scale", resize.scale, keyFile);
3670             saveToKeyfile("Resize", "AppliesTo", resize.appliesTo, keyFile);
3671             saveToKeyfile("Resize", "DataSpecified", resize.dataspec, keyFile);
3672             saveToKeyfile("Resize", "Width", resize.width, keyFile);
3673             saveToKeyfile("Resize", "Height", resize.height, keyFile);
3674             saveToKeyfile("Resize", "AllowUpscaling", resize.allowUpscaling, keyFile);
3675             saveToKeyfile("Resize", "PPI", resize.ppi, keyFile);
3676             const char *u = "px";
3677             switch (resize.unit) {
3678             case ResizeParams::CM: u = "cm"; break;
3679             case ResizeParams::INCHES: u = "in"; break;
3680             default: u = "px"; break;
3681             }
3682             saveToKeyfile("Resize", "Unit", Glib::ustring(u), keyFile);
3683         }
3684 
3685 // Post resize sharpening
3686         if (RELEVANT_(prsharpening)) {
3687             saveToKeyfile("OutputSharpening", "Enabled", prsharpening.enabled, keyFile);
3688             saveToKeyfile("OutputSharpening", "Contrast", prsharpening.contrast, keyFile);
3689             saveToKeyfile("OutputSharpening", "Method", prsharpening.method, keyFile);
3690             saveToKeyfile("OutputSharpening", "Radius", prsharpening.radius, keyFile);
3691             saveToKeyfile("OutputSharpening", "Amount", prsharpening.amount, keyFile);
3692             saveToKeyfile("OutputSharpening", "Threshold", prsharpening.threshold.toVector(), keyFile);
3693             saveToKeyfile("OutputSharpening", "OnlyEdges", prsharpening.edgesonly, keyFile);
3694             saveToKeyfile("OutputSharpening", "EdgedetectionRadius", prsharpening.edges_radius, keyFile);
3695             saveToKeyfile("OutputSharpening", "EdgeTolerance", prsharpening.edges_tolerance, keyFile);
3696             saveToKeyfile("OutputSharpening", "HalocontrolEnabled", prsharpening.halocontrol, keyFile);
3697             saveToKeyfile("OutputSharpening", "HalocontrolAmount", prsharpening.halocontrol_amount, keyFile);
3698             saveToKeyfile("OutputSharpening", "DeconvRadius", prsharpening.deconvradius, keyFile);
3699             saveToKeyfile("OutputSharpening", "DeconvAmount", prsharpening.deconvamount, keyFile);
3700         }
3701 
3702 // Color management
3703         if (RELEVANT_(icm)) {
3704             if (icm.inputProfile.substr(0, 5) == "file:") {
3705                 saveToKeyfile("Color Management", "InputProfile", filenameToUri(icm.inputProfile.substr(5), basedir), keyFile);
3706             } else {
3707                 saveToKeyfile("Color Management", "InputProfile", icm.inputProfile, keyFile);
3708             }
3709             saveToKeyfile("Color Management", "ToneCurve", icm.toneCurve, keyFile);
3710             saveToKeyfile("Color Management", "ApplyLookTable", icm.applyLookTable, keyFile);
3711             saveToKeyfile("Color Management", "ApplyBaselineExposureOffset", icm.applyBaselineExposureOffset, keyFile);
3712             saveToKeyfile("Color Management", "ApplyHueSatMap", icm.applyHueSatMap, keyFile);
3713             saveToKeyfile("Color Management", "DCPIlluminant", icm.dcpIlluminant, keyFile);
3714             saveToKeyfile("Color Management", "WorkingProfile", icm.workingProfile, keyFile);
3715             saveToKeyfile("Color Management", "OutputProfile", icm.outputProfile, keyFile);
3716             saveToKeyfile(
3717                 "Color Management",
3718                 "OutputProfileIntent",
3719                 {
3720                     {RI_PERCEPTUAL, "Perceptual"},
3721                     {RI_RELATIVE, "Relative"},
3722                     {RI_SATURATION, "Saturation"},
3723                     {RI_ABSOLUTE, "Absolute"}
3724 
3725                 },
3726                 icm.outputIntent,
3727                 keyFile
3728                 );
3729             saveToKeyfile("Color Management", "OutputBPC", icm.outputBPC, keyFile);
3730         }
3731 
3732 
3733 // Soft Light
3734         if (RELEVANT_(softlight)) {
3735             saveToKeyfile("SoftLight", "Enabled", softlight.enabled, keyFile);
3736             saveToKeyfile("SoftLight", "Strength", softlight.strength, keyFile);
3737         }
3738 
3739 // Film simulation
3740         if (RELEVANT_(filmSimulation)) {
3741             saveToKeyfile("Film Simulation", "Enabled", filmSimulation.enabled, keyFile);
3742             auto filename = filenameToUri(filmSimulation.clutFilename, basedir);
3743             saveToKeyfile("Film Simulation", "ClutFilename", filename, keyFile);
3744             saveToKeyfile("Film Simulation", "Strength", filmSimulation.strength, keyFile);
3745         }
3746 
3747 // RGB curves
3748         if (RELEVANT_(rgbCurves)) {
3749             saveToKeyfile("RGB Curves", "Enabled", rgbCurves.enabled, keyFile);
3750             saveToKeyfile("RGB Curves", "rCurve", rgbCurves.rcurve, keyFile);
3751             saveToKeyfile("RGB Curves", "gCurve", rgbCurves.gcurve, keyFile);
3752             saveToKeyfile("RGB Curves", "bCurve", rgbCurves.bcurve, keyFile);
3753         }
3754 
3755 // Grain
3756         if (RELEVANT_(grain)) {
3757             saveToKeyfile("Grain", "Enabled", grain.enabled, keyFile);
3758             saveToKeyfile("Grain", "ISO", grain.iso, keyFile);
3759             saveToKeyfile("Grain", "Strength", grain.strength, keyFile);
3760             saveToKeyfile("Grain", "Scale", grain.scale, keyFile);
3761         }
3762 
3763 
3764 // Smoothing
3765         if (RELEVANT_(smoothing)) {
3766             saveToKeyfile("Smoothing", "Enabled", smoothing.enabled, keyFile);
3767             for (size_t j = 0; j < smoothing.regions.size(); ++j) {
3768                 std::string n = std::to_string(j+1);
3769                 auto &r = smoothing.regions[j];
3770                 putToKeyfile("Smoothing", Glib::ustring("Mode_") + n, int(r.mode), keyFile);
3771                 putToKeyfile("Smoothing", Glib::ustring("Channel_") + n, int(r.channel), keyFile);
3772                 putToKeyfile("Smoothing", Glib::ustring("Radius_") + n, r.radius, keyFile);
3773                 putToKeyfile("Smoothing", Glib::ustring("Sigma_") + n, r.sigma, keyFile);
3774                 putToKeyfile("Smoothing", Glib::ustring("Epsilon_") + n, r.epsilon, keyFile);
3775                 putToKeyfile("Smoothing", Glib::ustring("Iterations_") + n, r.iterations, keyFile);
3776                 putToKeyfile("Smoothing", Glib::ustring("Falloff_") + n, r.falloff, keyFile);
3777                 putToKeyfile("Smoothing", Glib::ustring("NLStrength_") + n, r.nlstrength, keyFile);
3778                 putToKeyfile("Smoothing", Glib::ustring("NLDetail_") + n, r.nldetail, keyFile);
3779                 smoothing.labmasks[j].save(keyFile, "Smoothing", "", Glib::ustring("_") + n);
3780             }
3781             saveToKeyfile("Smoothing", "ShowMask", smoothing.showMask, keyFile);
3782         }
3783 
3784 // ColorCorrection
3785         if (RELEVANT_(colorcorrection)) {
3786             saveToKeyfile("ColorCorrection", "Enabled", colorcorrection.enabled, keyFile);
3787             for (size_t j = 0; j < colorcorrection.regions.size(); ++j) {
3788                 std::string n = std::to_string(j+1);
3789                 auto &l = colorcorrection.regions[j];
3790                 Glib::ustring mode = "YUV";
3791                 if (l.mode == ColorCorrectionParams::Mode::RGB) {
3792                     mode = "RGB";
3793                 } else if (l.mode == ColorCorrectionParams::Mode::HSL) {
3794                     mode = "HSL";
3795                 }
3796                 putToKeyfile("ColorCorrection", Glib::ustring("Mode_") + n, mode, keyFile);
3797                 {
3798                     const char *chan[3] = { "Slope", "Offset", "Power" };
3799                     for (int c = 0; c < 3; ++c) {
3800                         Glib::ustring w = chan[c];
3801                         putToKeyfile("ColorCorrection", w + "H" + "_" + n, l.hue[c], keyFile);
3802                         putToKeyfile("ColorCorrection", w + "S" + "_" + n, l.sat[c], keyFile);
3803                         putToKeyfile("ColorCorrection", w + "L" + "_" + n, l.factor[c], keyFile);
3804                     }
3805                 }
3806                 {
3807                     const char *chan[3] = { "R", "G", "B" };
3808                     for (int c = 0; c < 3; ++c) {
3809                         putToKeyfile("ColorCorrection", Glib::ustring("Slope") + chan[c] + "_" + n, l.slope[c], keyFile);
3810                         putToKeyfile("ColorCorrection", Glib::ustring("Offset") + chan[c] + "_" + n, l.offset[c], keyFile);
3811                         putToKeyfile("ColorCorrection", Glib::ustring("Power") + chan[c] + "_" + n, l.power[c], keyFile);
3812                         putToKeyfile("ColorCorrection", Glib::ustring("Pivot") + chan[c] + "_" + n, l.pivot[0], keyFile);
3813                     }
3814                 }
3815                 {
3816                     putToKeyfile("ColorCorrection", Glib::ustring("A_") + n, l.a, keyFile);
3817                     putToKeyfile("ColorCorrection", Glib::ustring("B_") + n, l.b, keyFile);
3818                     putToKeyfile("ColorCorrection", Glib::ustring("ABScale_") + n, l.abscale, keyFile);
3819                     putToKeyfile("ColorCorrection", Glib::ustring("InSaturation_") + n, l.inSaturation, keyFile);
3820                     putToKeyfile("ColorCorrection", Glib::ustring("OutSaturation_") + n, l.outSaturation, keyFile);
3821                     putToKeyfile("ColorCorrection", Glib::ustring("Slope_") + n, l.slope[0], keyFile);
3822                     putToKeyfile("ColorCorrection", Glib::ustring("Offset_") + n, l.offset[0], keyFile);
3823                     putToKeyfile("ColorCorrection", Glib::ustring("Power_") + n, l.power[0], keyFile);
3824                     putToKeyfile("ColorCorrection", Glib::ustring("Pivot_") + n, l.pivot[0], keyFile);
3825                     putToKeyfile("ColorCorrection", Glib::ustring("RGBLuminance_") + n, l.rgbluminance, keyFile);
3826                 }
3827                 colorcorrection.labmasks[j].save(keyFile, "ColorCorrection", "", Glib::ustring("_") + n);
3828             }
3829             saveToKeyfile("ColorCorrection", "showMask", colorcorrection.showMask, keyFile);
3830         }
3831 
3832 // Raw
3833         if (RELEVANT_(darkframe)) {
3834             saveToKeyfile("RAW", "DarkFrameEnabled", raw.enable_darkframe, keyFile);
3835             saveToKeyfile("RAW", "DarkFrame", filenameToUri(raw.dark_frame, basedir), keyFile);
3836             saveToKeyfile("RAW", "DarkFrameAuto", raw.df_autoselect, keyFile);
3837         }
3838         if (RELEVANT_(flatfield)) {
3839             saveToKeyfile("RAW", "FlatFieldEnabled", raw.enable_flatfield, keyFile);
3840             saveToKeyfile("RAW", "FlatFieldFile", filenameToUri(raw.ff_file, basedir), keyFile);
3841             saveToKeyfile("RAW", "FlatFieldAutoSelect", raw.ff_AutoSelect, keyFile);
3842             saveToKeyfile("RAW", "FlatFieldBlurRadius", raw.ff_BlurRadius, keyFile);
3843             saveToKeyfile("RAW", "FlatFieldBlurType", raw.ff_BlurType, keyFile);
3844             saveToKeyfile("RAW", "FlatFieldAutoClipControl", raw.ff_AutoClipControl, keyFile);
3845             saveToKeyfile("RAW", "FlatFieldClipControl", raw.ff_clipControl, keyFile);
3846             saveToKeyfile("RAW", "FlatFieldUseEmbedded", raw.ff_embedded, keyFile);
3847         }
3848         if (RELEVANT_(rawCA)) {
3849             saveToKeyfile("RAW", "CAEnabled", raw.enable_ca, keyFile);
3850             saveToKeyfile("RAW", "CA", raw.ca_autocorrect, keyFile);
3851             saveToKeyfile("RAW", "CAAvoidColourshift", raw.ca_avoidcolourshift, keyFile);
3852             saveToKeyfile("RAW", "CAAutoIterations", raw.caautoiterations, keyFile);
3853             saveToKeyfile("RAW", "CARed", raw.cared, keyFile);
3854             saveToKeyfile("RAW", "CABlue", raw.cablue, keyFile);
3855         }
3856         if (RELEVANT_(hotDeadPixelFilter)) {
3857             saveToKeyfile("RAW", "HotDeadPixelEnabled", raw.enable_hotdeadpix, keyFile);
3858             saveToKeyfile("RAW", "HotPixelFilter", raw.hotPixelFilter, keyFile);
3859             saveToKeyfile("RAW", "DeadPixelFilter", raw.deadPixelFilter, keyFile);
3860             saveToKeyfile("RAW", "HotDeadPixelThresh", raw.hotdeadpix_thresh, keyFile);
3861         }
3862         if (RELEVANT_(demosaic)) {
3863             saveToKeyfile("RAW Bayer", "Method", RAWParams::BayerSensor::getMethodString(raw.bayersensor.method), keyFile);
3864             saveToKeyfile("RAW Bayer", "Border", raw.bayersensor.border, keyFile);
3865             saveToKeyfile("RAW Bayer", "ImageNum", raw.bayersensor.imageNum + 1, keyFile);
3866             saveToKeyfile("RAW Bayer", "CcSteps", raw.bayersensor.ccSteps, keyFile);
3867         }
3868         if (RELEVANT_(rawBlack)) {
3869             saveToKeyfile("RAW Bayer", "PreBlackEnabled", raw.bayersensor.enable_black, keyFile);
3870             saveToKeyfile("RAW Bayer", "PreBlack0", raw.bayersensor.black0, keyFile);
3871             saveToKeyfile("RAW Bayer", "PreBlack1", raw.bayersensor.black1, keyFile);
3872             saveToKeyfile("RAW Bayer", "PreBlack2", raw.bayersensor.black2, keyFile);
3873             saveToKeyfile("RAW Bayer", "PreBlack3", raw.bayersensor.black3, keyFile);
3874             saveToKeyfile("RAW Bayer", "PreTwoGreen", raw.bayersensor.twogreen, keyFile);
3875         }
3876         if (RELEVANT_(rawPreprocessing)) {
3877             saveToKeyfile("RAW Bayer", "PreprocessingEnabled", raw.bayersensor.enable_preproc, keyFile);
3878             saveToKeyfile("RAW Bayer", "LineDenoise", raw.bayersensor.linenoise, keyFile);
3879             saveToKeyfile("RAW Bayer", "LineDenoiseDirection", toUnderlying(raw.bayersensor.linenoiseDirection), keyFile);
3880             saveToKeyfile("RAW Bayer", "GreenEqThreshold", raw.bayersensor.greenthresh, keyFile);
3881         }
3882         if (RELEVANT_(demosaic)) {
3883             // saveToKeyfile("RAW Bayer", "DCBIterations", raw.bayersensor.dcb_iterations, keyFile);
3884             // saveToKeyfile("RAW Bayer", "DCBEnhance", raw.bayersensor.dcb_enhance, keyFile);
3885             saveToKeyfile("RAW Bayer", "LMMSEIterations", raw.bayersensor.lmmse_iterations, keyFile);
3886             saveToKeyfile("RAW Bayer", "DualDemosaicAutoContrast", raw.bayersensor.dualDemosaicAutoContrast, keyFile);
3887             saveToKeyfile("RAW Bayer", "DualDemosaicContrast", raw.bayersensor.dualDemosaicContrast, keyFile);
3888             saveToKeyfile("RAW Bayer", "PixelShiftMotionCorrectionMethod", toUnderlying(raw.bayersensor.pixelShiftMotionCorrectionMethod), keyFile);
3889             saveToKeyfile("RAW Bayer", "PixelShiftEperIso", raw.bayersensor.pixelShiftEperIso, keyFile);
3890             saveToKeyfile("RAW Bayer", "PixelShiftSigma", raw.bayersensor.pixelShiftSigma, keyFile);
3891             saveToKeyfile("RAW Bayer", "PixelShiftShowMotion", raw.bayersensor.pixelShiftShowMotion, keyFile);
3892             saveToKeyfile("RAW Bayer", "PixelShiftShowMotionMaskOnly", raw.bayersensor.pixelShiftShowMotionMaskOnly, keyFile);
3893             saveToKeyfile("RAW Bayer", "pixelShiftHoleFill", raw.bayersensor.pixelShiftHoleFill, keyFile);
3894             saveToKeyfile("RAW Bayer", "pixelShiftMedian", raw.bayersensor.pixelShiftMedian, keyFile);
3895             saveToKeyfile("RAW Bayer", "pixelShiftGreen", raw.bayersensor.pixelShiftGreen, keyFile);
3896             saveToKeyfile("RAW Bayer", "pixelShiftBlur", raw.bayersensor.pixelShiftBlur, keyFile);
3897             saveToKeyfile("RAW Bayer", "pixelShiftSmoothFactor", raw.bayersensor.pixelShiftSmoothFactor, keyFile);
3898             saveToKeyfile("RAW Bayer", "pixelShiftEqualBright", raw.bayersensor.pixelShiftEqualBright, keyFile);
3899             saveToKeyfile("RAW Bayer", "pixelShiftEqualBrightChannel", raw.bayersensor.pixelShiftEqualBrightChannel, keyFile);
3900             saveToKeyfile("RAW Bayer", "pixelShiftNonGreenCross", raw.bayersensor.pixelShiftNonGreenCross, keyFile);
3901             saveToKeyfile("RAW Bayer", "pixelShiftDemosaicMethod", raw.bayersensor.pixelShiftDemosaicMethod, keyFile);
3902         }
3903         if (RELEVANT_(rawPreprocessing)) {
3904             saveToKeyfile("RAW Bayer", "PDAFLinesFilter", raw.bayersensor.pdafLinesFilter, keyFile);
3905             saveToKeyfile("RAW Bayer", "DynamicRowNoiseFilter", raw.bayersensor.dynamicRowNoiseFilter, keyFile);
3906         }
3907         if (RELEVANT_(demosaic)) {
3908             saveToKeyfile("RAW X-Trans", "Method", RAWParams::XTransSensor::getMethodString(raw.xtranssensor.method), keyFile);
3909             saveToKeyfile("RAW X-Trans", "DualDemosaicAutoContrast", raw.xtranssensor.dualDemosaicAutoContrast, keyFile);
3910             saveToKeyfile("RAW X-Trans", "DualDemosaicContrast", raw.xtranssensor.dualDemosaicContrast, keyFile);
3911             saveToKeyfile("RAW X-Trans", "Border", raw.xtranssensor.border, keyFile);
3912             saveToKeyfile("RAW X-Trans", "CcSteps", raw.xtranssensor.ccSteps, keyFile);
3913         }
3914         if (RELEVANT_(rawBlack)) {
3915             saveToKeyfile("RAW X-Trans", "PreBlackEnabled", raw.xtranssensor.enable_black, keyFile);
3916             saveToKeyfile("RAW X-Trans", "PreBlackRed", raw.xtranssensor.blackred, keyFile);
3917             saveToKeyfile("RAW X-Trans", "PreBlackGreen", raw.xtranssensor.blackgreen, keyFile);
3918             saveToKeyfile("RAW X-Trans", "PreBlackBlue", raw.xtranssensor.blackblue, keyFile);
3919         }
3920 
3921 // Raw exposition
3922         if (RELEVANT_(rawWhite)) {
3923             saveToKeyfile("RAW", "PreExposureEnabled", raw.enable_whitepoint, keyFile);
3924             saveToKeyfile("RAW", "PreExposure", raw.expos, keyFile);
3925         }
3926 
3927 // Film negative
3928         if (RELEVANT_(filmNegative)) {
3929             saveToKeyfile("Film Negative", "Enabled", filmNegative.enabled, keyFile);
3930             saveToKeyfile("Film Negative", "RedRatio", filmNegative.redRatio, keyFile);
3931             saveToKeyfile("Film Negative", "GreenExponent", filmNegative.greenExp, keyFile);
3932             saveToKeyfile("Film Negative", "BlueRatio", filmNegative.blueRatio, keyFile);
3933             saveToKeyfile("Film Negative", "RedBase", filmNegative.redBase, keyFile);
3934             saveToKeyfile("Film Negative", "GreenBase", filmNegative.greenBase, keyFile);
3935             saveToKeyfile("Film Negative", "BlueBase", filmNegative.blueBase, keyFile);
3936         }
3937 
3938 // MetaData
3939         if (RELEVANT_(metadata)) {
3940             saveToKeyfile("MetaData", "Mode", metadata.mode, keyFile);
3941             saveToKeyfile("MetaData", "ExifKeys", metadata.exifKeys, keyFile);
3942         }
3943 
3944 // EXIF change list
3945         if (RELEVANT_(exif)) {
3946             std::map<Glib::ustring, Glib::ustring> m;
3947             for (auto &p : exif_keys) {
3948                 m[p.second] = p.first;
3949             }
3950             for (auto &p : metadata.exif) {
3951                 auto it = m.find(p.first);
3952                 if (it != m.end()) {
3953                     keyFile.set_string("Exif", it->second, p.second);
3954                 }
3955             }
3956         }
3957 
3958 // IPTC change list
3959         if (RELEVANT_(iptc)) {
3960             std::map<Glib::ustring, Glib::ustring> m;
3961             for (auto &p : iptc_keys) {
3962                 m[p.second] = p.first;
3963             }
3964             for (auto &p : metadata.iptc) {
3965                 auto it = m.find(p.first);
3966                 if (it != m.end()) {
3967                     Glib::ArrayHandle<Glib::ustring> values = p.second;
3968                     keyFile.set_string_list("IPTC", it->second, values);
3969                 }
3970             }
3971         }
3972     //Spot Removal
3973         if (RELEVANT_(spot)) {
3974             //Spot removal
3975             saveToKeyfile("Spot Removal", "Enabled", spot.enabled, keyFile);
3976             for (size_t i = 0; i < spot.entries.size (); ++i) {
3977                 std::vector<double> entry = {
3978                     double(spot.entries[i].sourcePos.x),
3979                     double(spot.entries[i].sourcePos.y),
3980                     double(spot.entries[i].targetPos.x),
3981                     double(spot.entries[i].targetPos.y),
3982                     double(spot.entries[i].radius),
3983                     double(spot.entries[i].feather),
3984                     double(spot.entries[i].opacity),
3985                     double(spot.entries[i].detail)
3986                 };
3987 
3988                 std::stringstream ss;
3989                 ss << "Spot" << (i + 1);
3990 
3991                 saveToKeyfile("Spot Removal", ss.str(), entry, keyFile);
3992             }
3993         }
3994     } catch (Glib::KeyFileError &exc) {
3995         if (pl) {
3996             pl->error(Glib::ustring::compose(M("PROCPARAMS_SAVE_ERROR"), fname, exc.what()));
3997         }
3998         return 1;
3999     }
4000 
4001     return 0;
4002 #undef RELEVANT_
4003 }
4004 
4005 
save(ProgressListener * pl,KeyFile & keyFile,const ParamsEdited * pedited,const Glib::ustring & fname) const4006 int ProcParams::save(ProgressListener *pl,
4007                      KeyFile &keyFile, const ParamsEdited *pedited,
4008                      const Glib::ustring &fname) const
4009 {
4010     return save(pl, true, keyFile, pedited, fname);
4011 }
4012 
4013 
load(ProgressListener * pl,const Glib::ustring & fname,const ParamsEdited * pedited)4014 int ProcParams::load(ProgressListener *pl,
4015                      const Glib::ustring& fname, const ParamsEdited *pedited)
4016 {
4017     setlocale(LC_NUMERIC, "C");  // to set decimal point to "."
4018 
4019     if (fname.empty()) {
4020         return 1;
4021     }
4022 
4023     KeyFile keyFile;
4024     keyFile.setProgressListener(pl);
4025 
4026     try {
4027         if (!Glib::file_test(fname, Glib::FILE_TEST_EXISTS)) {
4028             return 1;
4029         }
4030 
4031         if (!keyFile.load_from_file(fname)) {
4032             // not an error to report
4033             return 1;
4034         }
4035 
4036         return load(pl, keyFile, pedited, true, fname);
4037     } catch (const Glib::Error& e) {
4038         //printf("-->%s\n", e.what().c_str());
4039         try {
4040             Exiv2Metadata md(fname, false);
4041             md.load();
4042             std::string xd = md.xmpData()["Xmp.ART.arp"].toString();
4043             Glib::ustring data = from_xmp(xd);
4044             if (!keyFile.load_from_data(data)) {
4045                 return 1;
4046             }
4047             return load(pl, keyFile, pedited, true, fname);
4048         } catch (std::exception &exc) {
4049             if (pl) {
4050                 pl->error(Glib::ustring::compose(M("PROCPARAMS_LOAD_ERROR"), fname, e.what()));
4051             }
4052             setDefaults();
4053             return 1;
4054         }
4055     } catch (std::exception &e) {
4056         if (pl) {
4057             pl->error(Glib::ustring::compose(M("PROCPARAMS_LOAD_ERROR"), fname, e.what()));
4058         }
4059         setDefaults();
4060         return 1;
4061     } catch (...) {
4062         if (pl) {
4063             pl->error(Glib::ustring::compose(M("PROCPARAMS_LOAD_ERROR"), fname, "unknown exception"));
4064         }
4065         //printf("-->unknown exception!\n");
4066         setDefaults();
4067         return 1;
4068     }
4069 }
4070 
4071 
load(ProgressListener * pl,bool load_general,const KeyFile & keyFile,const ParamsEdited * pedited,bool resetOnError,const Glib::ustring & fname)4072 int ProcParams::load(ProgressListener *pl, bool load_general,
4073                      const KeyFile &keyFile, const ParamsEdited *pedited,
4074                      bool resetOnError, const Glib::ustring &fname)
4075 {
4076 #define RELEVANT_(n) (!pedited || pedited->n)
4077 
4078     try {
4079         Glib::ustring basedir = Glib::path_get_dirname(fname);
4080 
4081         if (load_general) {
4082             ppVersion = PPVERSION;
4083             appVersion = RTVERSION;
4084 
4085             if (keyFile.has_group("Version")) {
4086                 if (keyFile.has_key("Version", "AppVersion")) {
4087                     appVersion = keyFile.get_string("Version", "AppVersion");
4088                 }
4089 
4090                 if (keyFile.has_key("Version", "Version")) {
4091                     ppVersion = keyFile.get_integer("Version", "Version");
4092                 }
4093             }
4094 
4095             if (keyFile.has_group("General") && RELEVANT_(general)) {
4096                 assignFromKeyfile(keyFile, "General", "Rank", rank);
4097                 assignFromKeyfile(keyFile, "General", "ColorLabel", colorlabel);
4098                 assignFromKeyfile(keyFile, "General", "InTrash", inTrash);
4099             }
4100         }
4101 
4102         const std::map<std::string, ToneCurveParams::TcMode> tc_mapping = {
4103             {"Standard", ToneCurveParams::TcMode::STD},
4104             {"FilmLike", ToneCurveParams::TcMode::FILMLIKE},
4105             {"SatAndValueBlending", ToneCurveParams::TcMode::SATANDVALBLENDING},
4106             {"WeightedStd", ToneCurveParams::TcMode::WEIGHTEDSTD},
4107             {"Luminance", ToneCurveParams::TcMode::LUMINANCE},
4108             {"Perceptual", ToneCurveParams::TcMode::PERCEPTUAL}
4109         };
4110 
4111         if (ppVersion < 350) {
4112             if (keyFile.has_group("Exposure")) {
4113                 if (RELEVANT_(exposure)) {
4114                     exposure.enabled = true;
4115                     assignFromKeyfile(keyFile, "Exposure", "Compensation", exposure.expcomp);
4116                 }
4117 
4118                 if (RELEVANT_(toneCurve)) {
4119                     toneCurve.enabled = true;
4120 
4121                     assignFromKeyfile(keyFile, "Exposure", "CurveMode", tc_mapping, toneCurve.curveMode);
4122                     assignFromKeyfile(keyFile, "Exposure", "CurveMode2", tc_mapping, toneCurve.curveMode2);
4123 
4124                     if (ppVersion > 200) {
4125                         assignFromKeyfile(keyFile, "Exposure", "Curve", toneCurve.curve);
4126                         assignFromKeyfile(keyFile, "Exposure", "Curve2", toneCurve.curve2);
4127                     }
4128 
4129                     assignFromKeyfile(keyFile, "Exposure", "HistogramMatching", toneCurve.histmatching);
4130                     if (ppVersion < 340) {
4131                         toneCurve.fromHistMatching = false;
4132                     } else {
4133                         assignFromKeyfile(keyFile, "Exposure", "CurveFromHistogramMatching", toneCurve.fromHistMatching);
4134                     }
4135                 }
4136                 if (RELEVANT_(saturation)) {
4137                     saturation.enabled = true;
4138                     assignFromKeyfile(keyFile, "Exposure", "Saturation", saturation.saturation);
4139                 }
4140             }
4141             if (keyFile.has_group("HLRecovery") && RELEVANT_(exposure)) {
4142                 bool en = false;
4143                 Glib::ustring method;
4144                 assignFromKeyfile(keyFile, "HLRecovery", "Enabled", en);
4145                 assignFromKeyfile(keyFile, "HLRecovery", "Method", method);
4146                 if (!en) {
4147                     exposure.hrmode = ExposureParams::HR_OFF;
4148                 } else if (method == "Blend") {
4149                     exposure.hrmode = ExposureParams::HR_BLEND;
4150                 } else if (method == "Color") {
4151                     exposure.hrmode = ExposureParams::HR_COLOR;
4152                 } else {
4153                     exposure.hrmode = ExposureParams::HR_OFF;
4154                 }
4155             }
4156         } else {
4157             if (keyFile.has_group("Exposure") && RELEVANT_(exposure)) {
4158                 assignFromKeyfile(keyFile, "Exposure", "Enabled", exposure.enabled);
4159                 assignFromKeyfile(keyFile, "Exposure", "Compensation", exposure.expcomp);
4160                 assignFromKeyfile(keyFile, "Exposure", "Black", exposure.black);
4161                 if (ppVersion >= 1000) {
4162                     Glib::ustring hr;
4163                     assignFromKeyfile(keyFile, "Exposure", "HLRecovery", hr);
4164                     if (hr == "Blend") {
4165                         exposure.hrmode = ExposureParams::HR_BLEND;
4166                     } else if (hr == "Color") {
4167                         exposure.hrmode = ExposureParams::HR_COLOR;
4168                     } else if (hr == "ColorBlend") {
4169                         exposure.hrmode = ExposureParams::HR_COLORSOFT;
4170                     } else {
4171                         exposure.hrmode = ExposureParams::HR_OFF;
4172                     }
4173                 } else {
4174                     bool en = false;
4175                     Glib::ustring method;
4176                     assignFromKeyfile(keyFile, "Exposure", "HLRecoveryEnabled", en);
4177                     assignFromKeyfile(keyFile, "Exposure", "HLRecoveryMethod", method);
4178                     if (!en) {
4179                         exposure.hrmode = ExposureParams::HR_OFF;
4180                     } else if (method == "Blend") {
4181                         exposure.hrmode = ExposureParams::HR_BLEND;
4182                     } else if (method == "Color") {
4183                         exposure.hrmode = ExposureParams::HR_COLOR;
4184                     } else {
4185                         exposure.hrmode = ExposureParams::HR_OFF;
4186                     }
4187                 }
4188             }
4189             if (keyFile.has_group("Saturation") && RELEVANT_(saturation)) {
4190                 assignFromKeyfile(keyFile, "Saturation", "Enabled", saturation.enabled);
4191                 assignFromKeyfile(keyFile, "Saturation", "Saturation", saturation.saturation);
4192                 assignFromKeyfile(keyFile, "Saturation", "Vibrance", saturation.vibrance);
4193             }
4194             if (keyFile.has_group("ToneCurve") && RELEVANT_(toneCurve)) {
4195                 assignFromKeyfile(keyFile, "ToneCurve", "Enabled", toneCurve.enabled);
4196                 assignFromKeyfile(keyFile, "ToneCurve", "Contrast", toneCurve.contrast);
4197                 assignFromKeyfile(keyFile, "ToneCurve", "CurveMode", tc_mapping, toneCurve.curveMode);
4198                 assignFromKeyfile(keyFile, "ToneCurve", "CurveMode2", tc_mapping, toneCurve.curveMode2);
4199 
4200                 assignFromKeyfile(keyFile, "ToneCurve", "Curve", toneCurve.curve);
4201                 assignFromKeyfile(keyFile, "ToneCurve", "Curve2", toneCurve.curve2);
4202                 assignFromKeyfile(keyFile, "ToneCurve", "HistogramMatching", toneCurve.histmatching);
4203                 assignFromKeyfile(keyFile, "ToneCurve", "CurveFromHistogramMatching", toneCurve.fromHistMatching);
4204                 assignFromKeyfile(keyFile, "ToneCurve", "Saturation", toneCurve.saturation);
4205                 assignFromKeyfile(keyFile, "ToneCurve", "PerceptualStrength", toneCurve.perceptualStrength);
4206             }
4207         }
4208 
4209         if (keyFile.has_group("Channel Mixer") && RELEVANT_(chmixer)) {
4210             if (ppVersion >= 329) {
4211                 assignFromKeyfile(keyFile, "Channel Mixer", "Enabled", chmixer.enabled);
4212             } else {
4213                 chmixer.enabled = true;
4214             }
4215 
4216             int mode = 0;
4217             if (assignFromKeyfile(keyFile, "Channel Mixer", "Mode", mode)) {
4218                 chmixer.mode = ChannelMixerParams::Mode(mode);
4219             }
4220 
4221             if (keyFile.has_key("Channel Mixer", "Red") && keyFile.has_key("Channel Mixer", "Green") && keyFile.has_key("Channel Mixer", "Blue")) {
4222                 const std::vector<int> rmix = keyFile.get_integer_list("Channel Mixer", "Red");
4223                 const std::vector<int> gmix = keyFile.get_integer_list("Channel Mixer", "Green");
4224                 const std::vector<int> bmix = keyFile.get_integer_list("Channel Mixer", "Blue");
4225 
4226                 if (rmix.size() == 3 && gmix.size() == 3 && bmix.size() == 3) {
4227                     memcpy(chmixer.red,   rmix.data(), 3 * sizeof(int));
4228                     memcpy(chmixer.green, gmix.data(), 3 * sizeof(int));
4229                     memcpy(chmixer.blue,  bmix.data(), 3 * sizeof(int));
4230                 }
4231                 if (ppVersion < 338) {
4232                     for (int i = 0; i < 3; ++i) {
4233                         chmixer.red[i] *= 10;
4234                         chmixer.green[i] *= 10;
4235                         chmixer.blue[i] *= 10;
4236                     }
4237                 }
4238             }
4239 
4240             if (keyFile.has_key("Channel Mixer", "HueTweak")) {
4241                 const std::vector<int> h = keyFile.get_integer_list("Channel Mixer", "HueTweak");
4242                 if (h.size() == 3) {
4243                     for (int i = 0; i < 3; ++i) {
4244                         chmixer.hue_tweak[i] = h[i];
4245                     }
4246                 }
4247             }
4248             if (keyFile.has_key("Channel Mixer", "SatTweak")) {
4249                 const std::vector<int> s = keyFile.get_integer_list("Channel Mixer", "SatTweak");
4250                 if (s.size() == 3) {
4251                     for (int i = 0; i < 3; ++i) {
4252                         chmixer.sat_tweak[i] = s[i];
4253                     }
4254                 }
4255             }
4256         }
4257 
4258         if (keyFile.has_group("Black & White") && RELEVANT_(blackwhite)) {
4259             assignFromKeyfile(keyFile, "Black & White", "Enabled", blackwhite.enabled);
4260             assignFromKeyfile(keyFile, "Black & White", "MixerRed", blackwhite.mixerRed);
4261             assignFromKeyfile(keyFile, "Black & White", "MixerGreen", blackwhite.mixerGreen);
4262             assignFromKeyfile(keyFile, "Black & White", "MixerBlue", blackwhite.mixerBlue);
4263             assignFromKeyfile(keyFile, "Black & White", "GammaRed", blackwhite.gammaRed);
4264             assignFromKeyfile(keyFile, "Black & White", "GammaGreen", blackwhite.gammaGreen);
4265             assignFromKeyfile(keyFile, "Black & White", "GammaBlue", blackwhite.gammaBlue);
4266             assignFromKeyfile(keyFile, "Black & White", "Filter", blackwhite.filter);
4267             assignFromKeyfile(keyFile, "Black & White", "Setting", blackwhite.setting);
4268             if (keyFile.has_key("Black & White", "ColorCast")) {
4269                 std::vector<int> ccast = keyFile.get_integer_list("Black & White", "ColorCast");
4270                 if (ccast.size() >= 2) {
4271                     blackwhite.colorCast.setValues(ccast[0], ccast[1]);
4272                 }
4273             }
4274         }
4275 
4276         if (keyFile.has_group("HSL Equalizer") && RELEVANT_(hsl)) {
4277             assignFromKeyfile(keyFile, "HSL Equalizer", "Enabled", hsl.enabled);
4278             assignFromKeyfile(keyFile, "HSL Equalizer", "HCurve", hsl.hCurve);
4279             assignFromKeyfile(keyFile, "HSL Equalizer", "SCurve", hsl.sCurve);
4280             assignFromKeyfile(keyFile, "HSL Equalizer", "LCurve", hsl.lCurve);
4281             assignFromKeyfile(keyFile, "HSL Equalizer", "Smoothing", hsl.smoothing);
4282         }
4283 
4284         if (keyFile.has_group("Local Contrast") && RELEVANT_(localContrast)) {
4285             assignFromKeyfile(keyFile, "Local Contrast", "Enabled", localContrast.enabled);
4286 
4287             std::vector<LocalContrastParams::Region> ll;
4288             std::vector<Mask> lm;
4289             bool found = false;
4290             bool done = false;
4291             for (int i = 0; !done; ++i) {
4292                 LocalContrastParams::Region cur;
4293                 Mask curmask;
4294                 done = true;
4295                 std::string n = i ? std::string("_") + std::to_string(i) : std::string("");
4296                 if (assignFromKeyfile(keyFile, "Local Contrast", Glib::ustring("Contrast") + n, cur.contrast)) {
4297                     found = true;
4298                     done = false;
4299                 }
4300                 if (assignFromKeyfile(keyFile, "Local Contrast", Glib::ustring("Curve") + n, cur.curve)) {
4301                     found = true;
4302                     done = false;
4303                 }
4304                 if (curmask.load(ppVersion, keyFile, "Local Contrast", "", n)) {
4305                     found = true;
4306                     done = false;
4307                 }
4308                 if (!done) {
4309                     ll.emplace_back(cur);
4310                     lm.emplace_back(curmask);
4311                 }
4312             }
4313             if (found) {
4314                 localContrast.regions = std::move(ll);
4315                 localContrast.labmasks = std::move(lm);
4316             }
4317             assert(localContrast.regions.size() == localContrast.labmasks.size());
4318             assignFromKeyfile(keyFile, "Local Contrast", "ShowMask", localContrast.showMask);
4319         }
4320 
4321         if (keyFile.has_group("Luminance Curve") && RELEVANT_(labCurve)) {
4322             assignFromKeyfile(keyFile, "Luminance Curve", "Enabled", labCurve.enabled);
4323             assignFromKeyfile(keyFile, "Luminance Curve", "Brightness", labCurve.brightness);
4324             assignFromKeyfile(keyFile, "Luminance Curve", "Contrast", labCurve.contrast);
4325             assignFromKeyfile(keyFile, "Luminance Curve", "Chromaticity", labCurve.chromaticity);
4326             assignFromKeyfile(keyFile, "Luminance Curve", "LCurve", labCurve.lcurve);
4327             assignFromKeyfile(keyFile, "Luminance Curve", "aCurve", labCurve.acurve);
4328             assignFromKeyfile(keyFile, "Luminance Curve", "bCurve", labCurve.bcurve);
4329         }
4330 
4331         if (keyFile.has_group("Sharpening") && RELEVANT_(sharpening)) {
4332             assignFromKeyfile(keyFile, "Sharpening", "Enabled", sharpening.enabled);
4333             if (ppVersion >= 334) {
4334                 assignFromKeyfile(keyFile, "Sharpening", "Contrast", sharpening.contrast);
4335             } else {
4336                 sharpening.contrast = 0;
4337             }
4338             assignFromKeyfile(keyFile, "Sharpening", "Radius", sharpening.radius);
4339             assignFromKeyfile(keyFile, "Sharpening", "Amount", sharpening.amount);
4340 
4341             if (keyFile.has_key("Sharpening", "Threshold")) {
4342                 if (ppVersion < 302) {
4343                     int thresh = min(keyFile.get_integer("Sharpening", "Threshold"), 2000);
4344                     sharpening.threshold.setValues(thresh, thresh, 2000, 2000);  // TODO: 2000 is the maximum value and is taken of rtgui/sharpening.cc ; should be changed by the tool modularization
4345                 } else {
4346                     const std::vector<int> thresh = keyFile.get_integer_list("Sharpening", "Threshold");
4347 
4348                     if (thresh.size() >= 4) {
4349                         sharpening.threshold.setValues(thresh[0], thresh[1], min(thresh[2], 2000), min(thresh[3], 2000));
4350                     }
4351                 }
4352             }
4353 
4354             assignFromKeyfile(keyFile, "Sharpening", "OnlyEdges", sharpening.edgesonly);
4355             assignFromKeyfile(keyFile, "Sharpening", "EdgedetectionRadius", sharpening.edges_radius);
4356             assignFromKeyfile(keyFile, "Sharpening", "EdgeTolerance", sharpening.edges_tolerance);
4357             assignFromKeyfile(keyFile, "Sharpening", "HalocontrolEnabled", sharpening.halocontrol);
4358             assignFromKeyfile(keyFile, "Sharpening", "HalocontrolAmount", sharpening.halocontrol_amount);
4359             assignFromKeyfile(keyFile, "Sharpening", "Method", sharpening.method);
4360             assignFromKeyfile(keyFile, "Sharpening", "DeconvRadius", sharpening.deconvradius);
4361             assignFromKeyfile(keyFile, "Sharpening", "DeconvAmount", sharpening.deconvamount);
4362             assignFromKeyfile(keyFile, "Sharpening", "DeconvAutoRadius", sharpening.deconvAutoRadius);
4363             assignFromKeyfile(keyFile, "Sharpening", "DeconvCornerBoost", sharpening.deconvCornerBoost);
4364             assignFromKeyfile(keyFile, "Sharpening", "DeconvCornerLatitude", sharpening.deconvCornerLatitude);
4365         }
4366 
4367         if (keyFile.has_group("White Balance") && RELEVANT_(wb)) {
4368             assignFromKeyfile(keyFile, "White Balance", "Enabled", wb.enabled);
4369             Glib::ustring method = "CustomTemp";
4370             assignFromKeyfile(keyFile, "White Balance", "Setting", method);
4371             assignFromKeyfile(keyFile, "White Balance", "Temperature", wb.temperature);
4372             assignFromKeyfile(keyFile, "White Balance", "Green", wb.green);
4373             assignFromKeyfile(keyFile, "White Balance", "Equal", wb.equal);
4374             std::vector<float> m;
4375             if (assignFromKeyfile(keyFile, "White Balance", "Multipliers", m) && m.size() == 3) {
4376                 for (int i = 0; i < 3; ++i) {
4377                     wb.mult[i] = m[i];
4378                 }
4379             }
4380             if (method == "Camera") {
4381                 wb.method = WBParams::CAMERA;
4382             } else if (method == "Auto") {
4383                 wb.method = WBParams::AUTO;
4384             } else if (method == "CustomMult") {
4385                 wb.method = WBParams::CUSTOM_MULT;
4386             } else {
4387                 wb.method = WBParams::CUSTOM_TEMP;
4388             }
4389         }
4390 
4391         if (keyFile.has_group("Defringing") && RELEVANT_(defringe)) {
4392             assignFromKeyfile(keyFile, "Defringing", "Enabled", defringe.enabled);
4393             assignFromKeyfile(keyFile, "Defringing", "Radius", defringe.radius);
4394 
4395             if (keyFile.has_key("Defringing", "Threshold")) {
4396                 defringe.threshold = (float)keyFile.get_integer("Defringing", "Threshold");
4397             }
4398 
4399             if (ppVersion < 310) {
4400                 defringe.threshold = sqrt(defringe.threshold * 33.f / 5.f);
4401             }
4402 
4403             assignFromKeyfile(keyFile, "Defringing", "HueCurve", defringe.huecurve);
4404         }
4405 
4406         if (keyFile.has_group("Impulse Denoising") && RELEVANT_(impulseDenoise)) {
4407             assignFromKeyfile(keyFile, "Impulse Denoising", "Enabled", impulseDenoise.enabled);
4408             assignFromKeyfile(keyFile, "Impulse Denoising", "Threshold", impulseDenoise.thresh);
4409         }
4410 
4411         if (ppVersion < 346) {
4412             if (keyFile.has_group("Directional Pyramid Denoising") && RELEVANT_(denoise)) { //TODO: No longer an accurate description for FT denoise
4413                 assignFromKeyfile(keyFile, "Directional Pyramid Denoising", "Enabled", denoise.enabled);
4414                 assignFromKeyfile(keyFile, "Directional Pyramid Denoising", "Luma", denoise.luminance);
4415                 assignFromKeyfile(keyFile, "Directional Pyramid Denoising", "Ldetail", denoise.luminanceDetail);
4416                 assignFromKeyfile(keyFile, "Directional Pyramid Denoising", "Chroma", denoise.chrominance);
4417                 Glib::ustring val;
4418                 if (assignFromKeyfile(keyFile, "Directional Pyramid Denoising", "C2Method", val)) {
4419                     if (val == "MANU") {
4420                         denoise.chrominanceMethod = DenoiseParams::ChrominanceMethod::MANUAL;
4421                     } else {
4422                         denoise.chrominanceMethod = DenoiseParams::ChrominanceMethod::AUTOMATIC;
4423                     }
4424                 }
4425                 if (assignFromKeyfile(keyFile, "Directional Pyramid Denoising", "SMethod", val)) {
4426                     denoise.aggressive = (val == "shalbi");
4427                 }
4428                 assignFromKeyfile(keyFile, "Directional Pyramid Denoising", "Redchro", denoise.chrominanceRedGreen);
4429                 assignFromKeyfile(keyFile, "Directional Pyramid Denoising", "Bluechro", denoise.chrominanceBlueYellow);
4430             }
4431         } else {
4432             if (keyFile.has_group("Denoise") && RELEVANT_(denoise)) {
4433                 assignFromKeyfile(keyFile, "Denoise", "Enabled", denoise.enabled);
4434                 Glib::ustring cs = "RGB";
4435                 assignFromKeyfile(keyFile, "Denoise", "ColorSpace", cs);
4436                 if (cs == "LAB") {
4437                     denoise.colorSpace = DenoiseParams::ColorSpace::LAB;
4438                 } else {
4439                     denoise.colorSpace = DenoiseParams::ColorSpace::RGB;
4440                 }
4441                 int val;
4442                 assignFromKeyfile(keyFile, "Denoise", "Aggressive", denoise.aggressive);
4443                 assignFromKeyfile(keyFile, "Denoise", "Gamma", denoise.gamma);
4444                 assignFromKeyfile(keyFile, "Denoise", "Luminance", denoise.luminance);
4445                 assignFromKeyfile(keyFile, "Denoise", "LuminanceDetail", denoise.luminanceDetail);
4446                 assignFromKeyfile(keyFile, "Denoise", "LuminanceDetailThreshold", denoise.luminanceDetailThreshold);
4447                 if (assignFromKeyfile(keyFile, "Denoise", "ChrominanceMethod", val)) {
4448                     denoise.chrominanceMethod = static_cast<DenoiseParams::ChrominanceMethod>(val);
4449                 }
4450                 assignFromKeyfile(keyFile, "Denoise", "ChrominanceAutoFactor", denoise.chrominanceAutoFactor);
4451                 assignFromKeyfile(keyFile, "Denoise", "Chrominance", denoise.chrominance);
4452                 assignFromKeyfile(keyFile, "Denoise", "ChrominanceRedGreen", denoise.chrominanceRedGreen);
4453                 assignFromKeyfile(keyFile, "Denoise", "ChrominanceBlueYellow", denoise.chrominanceBlueYellow);
4454                 assignFromKeyfile(keyFile, "Denoise", "SmoothingEnabled", denoise.smoothingEnabled);
4455                 if (assignFromKeyfile(keyFile, "Denoise", "SmoothingMethod", val) && val != 1) {
4456                     denoise.smoothingEnabled = false;
4457                 }
4458                 assignFromKeyfile(keyFile, "Denoise", "GuidedChromaRadius", denoise.guidedChromaRadius);
4459                 assignFromKeyfile(keyFile, "Denoise", "NLDetail", denoise.nlDetail);
4460                 assignFromKeyfile(keyFile, "Denoise", "NLStrength", denoise.nlStrength);
4461             }
4462         }
4463 
4464         const Glib::ustring tbgroup = ppVersion < 1000 ? "EPD" : "TextureBoost";
4465         if (keyFile.has_group(tbgroup) && RELEVANT_(textureBoost)) {
4466             assignFromKeyfile(keyFile, tbgroup, "Enabled", textureBoost.enabled);
4467 
4468             std::vector<TextureBoostParams::Region> ll;
4469             std::vector<Mask> lm;
4470             bool found = false;
4471             bool done = false;
4472             for (int i = 0; !done; ++i) {
4473                 TextureBoostParams::Region cur;
4474                 Mask curmask;
4475                 done = true;
4476                 std::string n = i ? std::string("_") + std::to_string(i) : std::string("");
4477                 if (assignFromKeyfile(keyFile, tbgroup, Glib::ustring("Strength") + n, cur.strength)) {
4478                     found = true;
4479                     done = false;
4480                 }
4481                 if (assignFromKeyfile(keyFile, tbgroup, Glib::ustring(ppVersion < 1009 ? "EdgeStopping" : "DetailThreshold") + n, cur.detailThreshold)) {
4482                     found = true;
4483                     done = false;
4484                 }
4485                 if (assignFromKeyfile(keyFile, tbgroup, Glib::ustring("Iterations") + n, cur.iterations)) {
4486                     found = true;
4487                     done = false;
4488                 }
4489                 if (curmask.load(ppVersion, keyFile, tbgroup, "", n)) {
4490                     found = true;
4491                     done = false;
4492                 }
4493                 if (!done) {
4494                     ll.emplace_back(cur);
4495                     lm.emplace_back(curmask);
4496                 }
4497             }
4498             if (found) {
4499                 textureBoost.regions = std::move(ll);
4500                 textureBoost.labmasks = std::move(lm);
4501             }
4502             assert(textureBoost.regions.size() == textureBoost.labmasks.size());
4503             assignFromKeyfile(keyFile, tbgroup, "ShowMask", textureBoost.showMask);
4504         }
4505 
4506         if (keyFile.has_group("FattalToneMapping") && RELEVANT_(fattal)) {
4507             assignFromKeyfile(keyFile, "FattalToneMapping", "Enabled", fattal.enabled);
4508             assignFromKeyfile(keyFile, "FattalToneMapping", "Threshold", fattal.threshold);
4509             assignFromKeyfile(keyFile, "FattalToneMapping", "Amount", fattal.amount);
4510             assignFromKeyfile(keyFile, "FattalToneMapping", "SaturationControl", fattal.satcontrol);
4511         }
4512 
4513         if (keyFile.has_group("LogEncoding") && RELEVANT_(logenc)) {
4514             assignFromKeyfile(keyFile, "LogEncoding", "Enabled", logenc.enabled);
4515             if (ppVersion >= 1013) {
4516                 assignFromKeyfile(keyFile, "LogEncoding", "Auto", logenc.autocompute);
4517             } else {
4518                 logenc.autocompute = false;
4519             }
4520             assignFromKeyfile(keyFile, "LogEncoding", ppVersion < 1024 ? "AutoGray" : "AutoGain", logenc.autogain);
4521             if (ppVersion < 349) {
4522                 if (assignFromKeyfile(keyFile, "LogEncoding", "GrayPoint", logenc.gain) && logenc.gain > 0) {
4523                     logenc.gain = std::log2(18.0/logenc.gain);
4524                 }
4525             } else {
4526                 if (ppVersion < 1024) {
4527                     if (assignFromKeyfile(keyFile, "LogEncoding", "SourceGray", logenc.gain) && logenc.gain > 0) {
4528                         logenc.gain = std::log2(18.0/logenc.gain);
4529                     }
4530                 } else {
4531                     assignFromKeyfile(keyFile, "LogEncoding", "Gain", logenc.gain);
4532                 }
4533                 assignFromKeyfile(keyFile, "LogEncoding", "TargetGray", logenc.targetGray);
4534             }
4535             assignFromKeyfile(keyFile, "LogEncoding", "BlackEv", logenc.blackEv);
4536             assignFromKeyfile(keyFile, "LogEncoding", "WhiteEv", logenc.whiteEv);
4537             if (ppVersion >= 1006) {
4538                 assignFromKeyfile(keyFile, "LogEncoding", "Regularization", logenc.regularization);
4539             } else {
4540                 logenc.regularization = 0;
4541             }
4542             if (ppVersion < 1025) {
4543                 toneCurve.contrast *= 2;
4544             }
4545         }
4546 
4547         if (keyFile.has_group("ToneEqualizer") && RELEVANT_(toneEqualizer)) {
4548             assignFromKeyfile(keyFile, "ToneEqualizer", "Enabled", toneEqualizer.enabled);
4549             for (size_t i = 0; i < toneEqualizer.bands.size(); ++i) {
4550                 assignFromKeyfile(keyFile, "ToneEqualizer", "Band" + std::to_string(i), toneEqualizer.bands[i]);
4551             }
4552             if (ppVersion >= 1020) {
4553                 assignFromKeyfile(keyFile, "ToneEqualizer", "Regularization", toneEqualizer.regularization);
4554             } else if (ppVersion >= 1002) {
4555                 if (assignFromKeyfile(keyFile, "ToneEqualizer", "Regularization", toneEqualizer.regularization)) {
4556                     if (toneEqualizer.regularization > 0) {
4557                         //toneEqualizer.regularization = 6 - std::min(toneEqualizer.regularization, 5);
4558                         toneEqualizer.regularization = 4;
4559                     } else if (toneEqualizer.regularization == -5) {
4560                         toneEqualizer.regularization = 0;
4561                     } else {
4562                         toneEqualizer.regularization = 1;
4563                     }
4564                 }
4565             } else {
4566                 assignFromKeyfile(keyFile, "ToneEqualizer", "Detail", toneEqualizer.regularization);
4567             }
4568         }
4569 
4570         if (keyFile.has_group("Crop") && RELEVANT_(crop)) {
4571             assignFromKeyfile(keyFile, "Crop", "Enabled", crop.enabled);
4572             assignFromKeyfile(keyFile, "Crop", "X", crop.x);
4573             assignFromKeyfile(keyFile, "Crop", "Y", crop.y);
4574 
4575             if (keyFile.has_key("Crop", "W")) {
4576                 crop.w = std::max(keyFile.get_integer("Crop", "W"), 1);
4577             }
4578 
4579             if (keyFile.has_key("Crop", "H")) {
4580                 crop.h = std::max(keyFile.get_integer("Crop", "H"), 1);
4581             }
4582 
4583             assignFromKeyfile(keyFile, "Crop", "FixedRatio", crop.fixratio);
4584 
4585             if (assignFromKeyfile(keyFile, "Crop", "Ratio", crop.ratio)) {
4586                 //backwards compatibility for crop.ratio
4587                 if (crop.ratio == "DIN") {
4588                     crop.ratio = "1.414 - DIN EN ISO 216";
4589                 }
4590 
4591                 if (crop.ratio == "8.5:11") {
4592                     crop.ratio = "8.5:11 - US Letter";
4593                 }
4594 
4595                 if (crop.ratio == "11:17") {
4596                     crop.ratio = "11:17 - Tabloid";
4597                 }
4598             }
4599 
4600             assignFromKeyfile(keyFile, "Crop", "Orientation", crop.orientation);
4601             assignFromKeyfile(keyFile, "Crop", "Guide", crop.guide);
4602         }
4603 
4604         if (keyFile.has_group("Coarse Transformation") && RELEVANT_(coarse)) {
4605             assignFromKeyfile(keyFile, "Coarse Transformation", "Rotate", coarse.rotate);
4606             assignFromKeyfile(keyFile, "Coarse Transformation", "HorizontalFlip", coarse.hflip);
4607             assignFromKeyfile(keyFile, "Coarse Transformation", "VerticalFlip", coarse.vflip);
4608         }
4609 
4610         if (keyFile.has_group("Rotation") && RELEVANT_(rotate)) {
4611             assignFromKeyfile(keyFile, "Rotation", "Enabled", rotate.enabled);
4612             assignFromKeyfile(keyFile, "Rotation", "Degree", rotate.degree);
4613         }
4614 
4615         if (keyFile.has_group("Common Properties for Transformations") && RELEVANT_(commonTrans)) {
4616             assignFromKeyfile(keyFile, "Common Properties for Transformations", "AutoFill", commonTrans.autofill);
4617         }
4618 
4619         if (keyFile.has_group("Distortion") && RELEVANT_(distortion)) {
4620             assignFromKeyfile(keyFile, "Distortion", "Enabled", distortion.enabled);
4621             assignFromKeyfile(keyFile, "Distortion", "Amount", distortion.amount);
4622             assignFromKeyfile(keyFile, "Distortion", "Auto", distortion.autocompute);
4623         }
4624 
4625         if (keyFile.has_group("LensProfile") && RELEVANT_(lensProf)) {
4626             if (keyFile.has_key("LensProfile", "LcMode")) {
4627                 lensProf.lcMode = lensProf.getMethodNumber(keyFile.get_string("LensProfile", "LcMode"));
4628             }
4629 
4630             if (keyFile.has_key("LensProfile", "LCPFile")) {
4631                 if (ppVersion >= 1017) {
4632                     lensProf.lcpFile = filenameFromUri(keyFile.get_string("LensProfile", "LCPFile"), basedir);
4633                 } else {
4634                     lensProf.lcpFile = expandRelativePath(fname, "", keyFile.get_string("LensProfile", "LCPFile"));
4635                 }
4636 
4637                 if (ppVersion < 327 && !lensProf.lcpFile.empty()) {
4638                     lensProf.lcMode = LensProfParams::LcMode::LCP;
4639                 }
4640             }
4641 
4642             assignFromKeyfile(keyFile, "LensProfile", "UseDistortion", lensProf.useDist);
4643             assignFromKeyfile(keyFile, "LensProfile", "UseVignette", lensProf.useVign);
4644             assignFromKeyfile(keyFile, "LensProfile", "UseCA", lensProf.useCA);
4645 
4646             if (keyFile.has_key("LensProfile", "LFCameraMake")) {
4647                 lensProf.lfCameraMake = keyFile.get_string("LensProfile", "LFCameraMake");
4648             }
4649 
4650             if (keyFile.has_key("LensProfile", "LFCameraModel")) {
4651                 lensProf.lfCameraModel = keyFile.get_string("LensProfile", "LFCameraModel");
4652             }
4653 
4654             if (keyFile.has_key("LensProfile", "LFLens")) {
4655                 lensProf.lfLens = keyFile.get_string("LensProfile", "LFLens");
4656             }
4657         }
4658 
4659         if (keyFile.has_group("Perspective") && RELEVANT_(perspective)) {
4660             assignFromKeyfile(keyFile, "Perspective", "Enabled", perspective.enabled);
4661             assignFromKeyfile(keyFile, "Perspective", "Horizontal", perspective.horizontal);
4662             assignFromKeyfile(keyFile, "Perspective", "Vertical", perspective.vertical);
4663             assignFromKeyfile(keyFile, "Perspective", "Angle", perspective.angle);
4664             assignFromKeyfile(keyFile, "Perspective", "Shear", perspective.shear);
4665             assignFromKeyfile(keyFile, "Perspective", "FocalLength", perspective.flength);
4666             assignFromKeyfile(keyFile, "Perspective", "CropFactor", perspective.cropfactor);
4667             assignFromKeyfile(keyFile, "Perspective", "Aspect", perspective.aspect);
4668             if (keyFile.has_key("Perspective", "ControlLines")) {
4669                 perspective.control_lines = keyFile.get_integer_list("Perspective", "ControlLines");
4670             }
4671         }
4672 
4673         if (keyFile.has_group("Gradient") && RELEVANT_(gradient)) {
4674             assignFromKeyfile(keyFile, "Gradient", "Enabled", gradient.enabled);
4675             assignFromKeyfile(keyFile, "Gradient", "Degree", gradient.degree);
4676             assignFromKeyfile(keyFile, "Gradient", "Feather", gradient.feather);
4677             assignFromKeyfile(keyFile, "Gradient", "Strength", gradient.strength);
4678             assignFromKeyfile(keyFile, "Gradient", "CenterX", gradient.centerX);
4679             assignFromKeyfile(keyFile, "Gradient", "CenterY", gradient.centerY);
4680         }
4681 
4682         if (keyFile.has_group("PCVignette") && RELEVANT_(pcvignette)) {
4683             assignFromKeyfile(keyFile, "PCVignette", "Enabled", pcvignette.enabled);
4684             assignFromKeyfile(keyFile, "PCVignette", "Strength", pcvignette.strength);
4685             assignFromKeyfile(keyFile, "PCVignette", "Feather", pcvignette.feather);
4686             assignFromKeyfile(keyFile, "PCVignette", "Roundness", pcvignette.roundness);
4687             assignFromKeyfile(keyFile, "PCVignette", "CenterX", pcvignette.centerX);
4688             assignFromKeyfile(keyFile, "PCVignette", "CenterY", pcvignette.centerY);
4689         }
4690 
4691         if (keyFile.has_group("CACorrection") && RELEVANT_(cacorrection)) {
4692             assignFromKeyfile(keyFile, "CACorrection", "Enabled", cacorrection.enabled);
4693             assignFromKeyfile(keyFile, "CACorrection", "Red", cacorrection.red);
4694             assignFromKeyfile(keyFile, "CACorrection", "Blue", cacorrection.blue);
4695         }
4696 
4697         if (keyFile.has_group("Vignetting Correction") && RELEVANT_(vignetting)) {
4698             assignFromKeyfile(keyFile, "Vignetting Correction", "Enabled", vignetting.enabled);
4699             assignFromKeyfile(keyFile, "Vignetting Correction", "Amount", vignetting.amount);
4700             assignFromKeyfile(keyFile, "Vignetting Correction", "Radius", vignetting.radius);
4701             assignFromKeyfile(keyFile, "Vignetting Correction", "Strength", vignetting.strength);
4702             assignFromKeyfile(keyFile, "Vignetting Correction", "CenterX", vignetting.centerX);
4703             assignFromKeyfile(keyFile, "Vignetting Correction", "CenterY", vignetting.centerY);
4704         }
4705 
4706         if (keyFile.has_group("Resize") && RELEVANT_(resize)) {
4707             assignFromKeyfile(keyFile, "Resize", "Enabled", resize.enabled);
4708             assignFromKeyfile(keyFile, "Resize", "Scale", resize.scale);
4709             assignFromKeyfile(keyFile, "Resize", "AppliesTo", resize.appliesTo);
4710             assignFromKeyfile(keyFile, "Resize", "DataSpecified", resize.dataspec);
4711             assignFromKeyfile(keyFile, "Resize", "Width", resize.width);
4712             assignFromKeyfile(keyFile, "Resize", "Height", resize.height);
4713             if (ppVersion >= 339) {
4714                 assignFromKeyfile(keyFile, "Resize", "AllowUpscaling", resize.allowUpscaling);
4715             } else {
4716                 resize.allowUpscaling = false;
4717             }
4718             assignFromKeyfile(keyFile, "Resize", "PPI", resize.ppi);
4719             if (ppVersion < 1004) {
4720                 resize.unit = ResizeParams::PX;
4721             } else {
4722                 Glib::ustring u = "px";
4723                 assignFromKeyfile(keyFile, "Resize", "Unit", u);
4724                 if (u == "cm") {
4725                     resize.unit = ResizeParams::CM;
4726                 } else if (u == "in") {
4727                     resize.unit = ResizeParams::INCHES;
4728                 } else {
4729                     resize.unit = ResizeParams::PX;
4730                 }
4731             }
4732         }
4733 
4734 	if (keyFile.has_group ("Spot Removal") && RELEVANT_(spot)) {
4735             assignFromKeyfile(keyFile, "Spot Removal", "Enabled", spot.enabled);
4736             int i = 0;
4737             do {
4738                 std::stringstream ss;
4739                 ss << "Spot" << (i++ + 1);
4740 
4741                 if (keyFile.has_key ("Spot Removal", ss.str())) {
4742                     Glib::ArrayHandle<double> entry = keyFile.get_double_list("Spot Removal", ss.str());
4743                     const double epsilon = 0.001;  // to circumvent rounding of integer saved as double
4744                     SpotEntry se;
4745 
4746                     if (entry.size() == 8) {
4747                         se.sourcePos.set(int(entry.data()[0] + epsilon), int(entry.data()[1] + epsilon));
4748                         se.targetPos.set(int(entry.data()[2] + epsilon), int(entry.data()[3] + epsilon));
4749                         se.radius  = LIM<int>(int(entry.data()[4] + epsilon), SpotParams::minRadius, SpotParams::maxRadius);
4750                         se.feather = float(entry.data()[5]);
4751                         se.opacity = float(entry.data()[6]);
4752                         se.detail = entry.data()[7];
4753                         spot.entries.push_back(se);
4754                     }
4755                 } else {
4756                     break;
4757                 }
4758             } while (1);
4759         }
4760 
4761         const char *psgrp = ppVersion < 1021 ? "PostResizeSharpening" : "OutputSharpening";
4762         if (keyFile.has_group(psgrp) && RELEVANT_(prsharpening)) {
4763             assignFromKeyfile(keyFile, psgrp, "Enabled", prsharpening.enabled);
4764             assignFromKeyfile(keyFile, psgrp, "Contrast", prsharpening.contrast);
4765             assignFromKeyfile(keyFile, psgrp, "Radius", prsharpening.radius);
4766             assignFromKeyfile(keyFile, psgrp, "Amount", prsharpening.amount);
4767 
4768             if (keyFile.has_key(psgrp, "Threshold")) {
4769                 if (ppVersion < 302) {
4770                     int thresh = min(keyFile.get_integer(psgrp, "Threshold"), 2000);
4771                     prsharpening.threshold.setValues(thresh, thresh, 2000, 2000);  // TODO: 2000 is the maximum value and is taken of rtgui/sharpening.cc ; should be changed by the tool modularization
4772                 } else {
4773                     const std::vector<int> thresh = keyFile.get_integer_list(psgrp, "Threshold");
4774 
4775                     if (thresh.size() >= 4) {
4776                         prsharpening.threshold.setValues(thresh[0], thresh[1], min(thresh[2], 2000), min(thresh[3], 2000));
4777                     }
4778                 }
4779             }
4780 
4781             assignFromKeyfile(keyFile, psgrp, "OnlyEdges", prsharpening.edgesonly);
4782             assignFromKeyfile(keyFile, psgrp, "EdgedetectionRadius", prsharpening.edges_radius);
4783             assignFromKeyfile(keyFile, psgrp, "EdgeTolerance", prsharpening.edges_tolerance);
4784             assignFromKeyfile(keyFile, psgrp, "HalocontrolEnabled", prsharpening.halocontrol);
4785             assignFromKeyfile(keyFile, psgrp, "HalocontrolAmount", prsharpening.halocontrol_amount);
4786             assignFromKeyfile(keyFile, psgrp, "Method", prsharpening.method);
4787             assignFromKeyfile(keyFile, psgrp, "DeconvRadius", prsharpening.deconvradius);
4788             assignFromKeyfile(keyFile, psgrp, "DeconvAmount", prsharpening.deconvamount);
4789             if (ppVersion < 1021 && !resize.enabled) {
4790                 prsharpening.enabled = false;
4791             }
4792         }
4793 
4794         if (keyFile.has_group("Color Management") && RELEVANT_(icm)) {
4795             if (keyFile.has_key("Color Management", "InputProfile")) {
4796                 icm.inputProfile = keyFile.get_string("Color Management", "InputProfile");
4797                 if (icm.inputProfile.substr(0, 5) == "file:") {
4798                     if (ppVersion >= 1017) {
4799                         icm.inputProfile = "file:" + filenameFromUri(icm.inputProfile, basedir);
4800                     } else {
4801                         icm.inputProfile = expandRelativePath(fname, "file:", icm.inputProfile);
4802                     }
4803                 }
4804             }
4805 
4806             assignFromKeyfile(keyFile, "Color Management", "ToneCurve", icm.toneCurve);
4807             assignFromKeyfile(keyFile, "Color Management", "ApplyLookTable", icm.applyLookTable);
4808             assignFromKeyfile(keyFile, "Color Management", "ApplyBaselineExposureOffset", icm.applyBaselineExposureOffset);
4809             assignFromKeyfile(keyFile, "Color Management", "ApplyHueSatMap", icm.applyHueSatMap);
4810             assignFromKeyfile(keyFile, "Color Management", "DCPIlluminant", icm.dcpIlluminant);
4811             assignFromKeyfile(keyFile, "Color Management", "WorkingProfile", icm.workingProfile);
4812 
4813             assignFromKeyfile(keyFile, "Color Management", "OutputProfile", icm.outputProfile);
4814             if (ppVersion < 341) {
4815                 if (icm.outputProfile == "RT_Medium_gsRGB") {
4816                     icm.outputProfile = "RTv4_Medium";
4817                 } else if (icm.outputProfile == "RT_Large_gBT709" || icm.outputProfile == "RT_Large_g10" || icm.outputProfile == "RT_Large_gsRGB") {
4818                     icm.outputProfile = "RTv4_Large";
4819                 } else if (icm.outputProfile == "WideGamutRGB") {
4820                     icm.outputProfile = "RTv4_Wide";
4821                 } else if (icm.outputProfile == "RT_sRGB_gBT709" || icm.outputProfile == "RT_sRGB_g10" || icm.outputProfile == "RT_sRGB") {
4822                     icm.outputProfile = "RTv4_sRGB";
4823                 } else if (icm.outputProfile == "BetaRGB") { // Have we ever provided this profile ? Should we convert this filename ?
4824                     icm.outputProfile = "RTv4_Beta";
4825                 } else if (icm.outputProfile == "BestRGB") { // Have we ever provided this profile ? Should we convert this filename ?
4826                     icm.outputProfile = "RTv4_Best";
4827                 } else if (icm.outputProfile == "Rec2020") {
4828                     icm.outputProfile = "RTv4_Rec2020";
4829                 } else if (icm.outputProfile == "Bruce") { // Have we ever provided this profile ? Should we convert this filename ?
4830                     icm.outputProfile = "RTv4_Bruce";
4831                 } else if (icm.outputProfile == "ACES") {
4832                     icm.outputProfile = "RTv4_ACES-AP0";
4833                 }
4834             }
4835             if (keyFile.has_key("Color Management", "OutputProfileIntent")) {
4836                 Glib::ustring intent = keyFile.get_string("Color Management", "OutputProfileIntent");
4837 
4838                 if (intent == "Perceptual") {
4839                     icm.outputIntent = RI_PERCEPTUAL;
4840                 } else if (intent == "Relative") {
4841                     icm.outputIntent = RI_RELATIVE;
4842                 } else if (intent == "Saturation") {
4843                     icm.outputIntent = RI_SATURATION;
4844                 } else if (intent == "Absolute") {
4845                     icm.outputIntent = RI_ABSOLUTE;
4846                 }
4847             }
4848             assignFromKeyfile(keyFile, "Color Management", "OutputBPC", icm.outputBPC);
4849         }
4850 
4851         if (keyFile.has_group("SoftLight") && RELEVANT_(softlight)) {
4852             assignFromKeyfile(keyFile, "SoftLight", "Enabled", softlight.enabled);
4853             assignFromKeyfile(keyFile, "SoftLight", "Strength", softlight.strength);
4854         }
4855 
4856         if (keyFile.has_group("Dehaze") && RELEVANT_(dehaze)) {
4857             assignFromKeyfile(keyFile, "Dehaze", "Enabled", dehaze.enabled);
4858             if (ppVersion < 1010) {
4859                 int s = 0;
4860                 if (assignFromKeyfile(keyFile, "Dehaze", "Strength", s)) {
4861                     float v = 0.5f + LIM((float(s) / 200.f) * 1.38f, -0.5f, 0.5f);
4862                     dehaze.strength = {
4863                         FCT_MinMaxCPoints,
4864                         0.0,
4865                         v,
4866                         0.0,
4867                         0.0,
4868                         1.0,
4869                         v,
4870                         0.0,
4871                         0.0
4872                     };
4873                 }
4874             } else {
4875                 assignFromKeyfile(keyFile, "Dehaze", "Strength", dehaze.strength);
4876             }
4877             assignFromKeyfile(keyFile, "Dehaze", "ShowDepthMap", dehaze.showDepthMap);
4878             assignFromKeyfile(keyFile, "Dehaze", "Depth", dehaze.depth);
4879             assignFromKeyfile(keyFile, "Dehaze", "Luminance", dehaze.luminance);
4880             assignFromKeyfile(keyFile, "Dehaze", "Blackpoint", dehaze.blackpoint);
4881         }
4882 
4883         if (keyFile.has_group("Film Simulation") && RELEVANT_(filmSimulation)) {
4884             assignFromKeyfile(keyFile, "Film Simulation", "Enabled", filmSimulation.enabled);
4885             assignFromKeyfile(keyFile, "Film Simulation", "ClutFilename", filmSimulation.clutFilename);
4886             if (ppVersion >= 1017) {
4887                 filmSimulation.clutFilename = filenameFromUri(filmSimulation.clutFilename, basedir);
4888             }
4889 
4890             if (keyFile.has_key("Film Simulation", "Strength")) {
4891                 if (ppVersion < 321) {
4892                     filmSimulation.strength = keyFile.get_double("Film Simulation", "Strength") * 100 + 0.1;
4893                 } else {
4894                     filmSimulation.strength = keyFile.get_integer("Film Simulation", "Strength");
4895                 }
4896             }
4897         }
4898 
4899         if (keyFile.has_group("RGB Curves") && RELEVANT_(rgbCurves)) {
4900             if (ppVersion >= 329) {
4901                 assignFromKeyfile(keyFile, "RGB Curves", "Enabled", rgbCurves.enabled);
4902             } else {
4903                 rgbCurves.enabled = true;
4904             }
4905 
4906             assignFromKeyfile(keyFile, "RGB Curves", "rCurve", rgbCurves.rcurve);
4907             assignFromKeyfile(keyFile, "RGB Curves", "gCurve", rgbCurves.gcurve);
4908             assignFromKeyfile(keyFile, "RGB Curves", "bCurve", rgbCurves.bcurve);
4909         }
4910 
4911         if (keyFile.has_group("Grain") && RELEVANT_(grain)) {
4912             assignFromKeyfile(keyFile, "Grain", "Enabled", grain.enabled);
4913             assignFromKeyfile(keyFile, "Grain", "ISO", grain.iso);
4914             assignFromKeyfile(keyFile, "Grain", "Strength", grain.strength);
4915             assignFromKeyfile(keyFile, "Grain", "Scale", grain.scale);
4916         }
4917 
4918         const char *smoothing_group = ppVersion < 1016 ? "GuidedSmoothing" : "Smoothing";
4919         if (keyFile.has_group(smoothing_group) && RELEVANT_(smoothing)) {
4920             assignFromKeyfile(keyFile, smoothing_group, "Enabled", smoothing.enabled);
4921 
4922             std::vector<SmoothingParams::Region> ll;
4923             std::vector<Mask> lm;
4924             bool found = false;
4925             bool done = false;
4926             for (int i = 1; !done; ++i) {
4927                 SmoothingParams::Region cur;
4928                 Mask curmask;
4929                 done = true;
4930                 std::string n = std::to_string(i);
4931                 int c;
4932                 if (assignFromKeyfile(keyFile, smoothing_group, Glib::ustring("Channel_") + n, c)) {
4933                     cur.channel = SmoothingParams::Region::Channel(c);
4934                     found = true;
4935                     done = false;
4936                 }
4937                 if (assignFromKeyfile(keyFile, smoothing_group, Glib::ustring("Mode_") + n, c)) {
4938                     cur.mode = SmoothingParams::Region::Mode(c);
4939                     found = true;
4940                     done = false;
4941                 }
4942                 if (assignFromKeyfile(keyFile, smoothing_group, Glib::ustring("Radius_") + n, cur.radius)) {
4943                     found = true;
4944                     done = false;
4945                 }
4946                 if (assignFromKeyfile(keyFile, smoothing_group, Glib::ustring("Sigma_") + n, cur.sigma)) {
4947                     found = true;
4948                     done = false;
4949                 }
4950                 if (assignFromKeyfile(keyFile, smoothing_group, Glib::ustring("Epsilon_") + n, cur.epsilon)) {
4951                     found = true;
4952                     done = false;
4953                 }
4954                 if (assignFromKeyfile(keyFile, smoothing_group, Glib::ustring("Iterations_") + n, cur.iterations)) {
4955                     found = true;
4956                     done = false;
4957                 }
4958                 if (assignFromKeyfile(keyFile, smoothing_group, Glib::ustring("Falloff_") + n, cur.falloff)) {
4959                     found = true;
4960                     done = false;
4961                 }
4962                 if (assignFromKeyfile(keyFile, smoothing_group, Glib::ustring("NLStrength_") + n, cur.nlstrength)) {
4963                     found = true;
4964                     done = false;
4965                 }
4966                 if (assignFromKeyfile(keyFile, smoothing_group, Glib::ustring("NLDetail_") + n, cur.nldetail)) {
4967                     found = true;
4968                     done = false;
4969                 }
4970                 if (curmask.load(ppVersion, keyFile, smoothing_group, "", Glib::ustring("_") + n)) {
4971                     found = true;
4972                     done = false;
4973                 }
4974                 if (!done) {
4975                     ll.emplace_back(cur);
4976                     lm.emplace_back(curmask);
4977                 }
4978             }
4979             if (found) {
4980                 smoothing.regions = std::move(ll);
4981                 smoothing.labmasks = std::move(lm);
4982             }
4983             assert(smoothing.regions.size() == smoothing.labmasks.size());
4984             assignFromKeyfile(keyFile, smoothing_group, "ShowMask", smoothing.showMask);
4985         }
4986 
4987         const char *ccgroup = "ColorCorrection";
4988         if (keyFile.has_group(ccgroup) && RELEVANT_(colorcorrection)) {
4989             const Glib::ustring prefix = "";
4990             assignFromKeyfile(keyFile, ccgroup, "Enabled", colorcorrection.enabled);
4991             std::vector<ColorCorrectionParams::Region> lg;
4992             std::vector<Mask> lm;
4993             bool found = false;
4994             bool done = false;
4995 
4996             for (int i = 1; !done; ++i) {
4997                 ColorCorrectionParams::Region cur;
4998                 Mask curmask;
4999                 done = true;
5000                 std::string n = std::to_string(i);
5001 
5002                 const auto get =
5003                     [&](const Glib::ustring &key, double &val) -> bool
5004                     {
5005                         if (assignFromKeyfile(keyFile, ccgroup, prefix + key + n, val)) {
5006                             found = true;
5007                             done = false;
5008                             return true;
5009                         } else {
5010                             return false;
5011                         }
5012                     };
5013 
5014 
5015                 get("A_", cur.a);
5016                 get("B_", cur.b);
5017                 get("ABScale_", cur.abscale);
5018                 if (ppVersion < 1005) {
5019                     int c = -1;
5020                     if (assignFromKeyfile(keyFile, ccgroup, prefix + Glib::ustring("Channel_") + n, c)) {
5021                         found = true;
5022                         done = false;
5023                     }
5024                     if (c < 0) {
5025                         cur.mode = ColorCorrectionParams::Mode::YUV;
5026                         c = 0;
5027                     } else {
5028                         cur.mode = ColorCorrectionParams::Mode::RGB;
5029                     }
5030                     get("Slope_", cur.slope[c]);
5031                     if (get("Offset_", cur.offset[c])) {
5032                         if (ppVersion <= 1002) {
5033                             cur.offset[c] *= 2;
5034                         }
5035                     }
5036                     get("Power_", cur.power[c]);
5037                     get("Pivot_", cur.pivot[c]);
5038                 } else {
5039                     bool rgb_channels = false;
5040                     Glib::ustring mode;
5041                     if (assignFromKeyfile(keyFile, ccgroup, prefix + Glib::ustring("RGBChannels_") + n, rgb_channels)) {
5042                         found = true;
5043                         done = false;
5044                         cur.mode = rgb_channels ? ColorCorrectionParams::Mode::RGB : ColorCorrectionParams::Mode::YUV;
5045                     } else if (assignFromKeyfile(keyFile, ccgroup, prefix + Glib::ustring("Mode_") + n, mode)) {
5046                         if (mode == "YUV") {
5047                             cur.mode = ColorCorrectionParams::Mode::YUV;
5048                         } else if (mode == "RGB") {
5049                             cur.mode = ColorCorrectionParams::Mode::RGB;
5050                         } else if (mode == "HSL") {
5051                             cur.mode = ColorCorrectionParams::Mode::HSL;
5052                         }
5053                         found = true;
5054                         done = false;
5055                     }
5056 
5057                     if (cur.mode != ColorCorrectionParams::Mode::RGB) {
5058                         get("Slope_", cur.slope[0]);
5059                         cur.slope[1] = cur.slope[2] = cur.slope[0];
5060                         get("Offset_", cur.offset[0]);
5061                         cur.offset[1] = cur.offset[2] = cur.offset[0];
5062                         get("Power_", cur.power[0]);
5063                         cur.power[1] = cur.power[2] = cur.power[0];
5064                         get("Pivot_", cur.pivot[0]);
5065                         cur.pivot[1] = cur.pivot[2] = cur.pivot[0];
5066                     } else {
5067                         const char *chan[3] = { "R", "G", "B" };
5068                         for (int c = 0; c < 3; ++c) {
5069                             get(Glib::ustring("Slope") + chan[c] + "_", cur.slope[c]);
5070                             get(Glib::ustring("Offset") + chan[c] + "_", cur.offset[c]);
5071                             get(Glib::ustring("Power") + chan[c] + "_", cur.power[c]);
5072                             get(Glib::ustring("Pivot") + chan[c] + "_", cur.pivot[c]);
5073                         }
5074                     }
5075                     {
5076                         const char *chan[3] = { "Slope", "Offset", "Power" };
5077                         for (int c = 0; c < 3; ++c) {
5078                             Glib::ustring w = chan[c];
5079                             get(w + "H_", cur.hue[c]);
5080                             get(w + "S_", cur.sat[c]);
5081                             get(w + "L_", cur.factor[c]);
5082                         }
5083                     }
5084                 }
5085                 if (ppVersion < 1018) {
5086                     double sat = 0;
5087                     get("Saturation_", sat);
5088                     if (cur.mode == ColorCorrectionParams::Mode::YUV) {
5089                         cur.inSaturation = sat;
5090                         cur.outSaturation = 0;
5091                     } else {
5092                         cur.inSaturation = 0;
5093                         cur.outSaturation = sat;
5094                     }
5095                 } else {
5096                     get("InSaturation_", cur.inSaturation);
5097                     get("OutSaturation_", cur.outSaturation);
5098                 }
5099                 if (assignFromKeyfile(keyFile, ccgroup, prefix + "RGBLuminance_" + n, cur.rgbluminance)) {
5100                     found = true;
5101                     done = false;
5102                 }
5103                 if (curmask.load(ppVersion, keyFile, ccgroup, prefix, Glib::ustring("_") + n)) {
5104                     found = true;
5105                     done = false;
5106                 }
5107                 if (!done) {
5108                     lg.emplace_back(cur);
5109                     lm.emplace_back(curmask);
5110                 }
5111             }
5112             if (found) {
5113                 colorcorrection.regions = std::move(lg);
5114                 colorcorrection.labmasks = std::move(lm);
5115             }
5116             assert(colorcorrection.regions.size() == colorcorrection.labmasks.size());
5117             assignFromKeyfile(keyFile, ccgroup, ppVersion < 348 ? "showMask" : "LabRegionsShowMask", colorcorrection.showMask);
5118         }
5119 
5120         if (keyFile.has_group("RAW")) {
5121             if (RELEVANT_(darkframe)) {
5122                 assignFromKeyfile(keyFile, "RAW", "DarkFrameEnabled", raw.enable_darkframe);
5123                 if (keyFile.has_key("RAW", "DarkFrame")) {
5124                     if (ppVersion >= 1017) {
5125                         raw.dark_frame = filenameFromUri(keyFile.get_string("RAW", "DarkFrame"), basedir);
5126                     } else {
5127                         raw.dark_frame = expandRelativePath(fname, "", keyFile.get_string("RAW", "DarkFrame"));
5128                     }
5129                 }
5130 
5131                 assignFromKeyfile(keyFile, "RAW", "DarkFrameAuto", raw.df_autoselect);
5132             }
5133 
5134             if (RELEVANT_(flatfield)) {
5135                 assignFromKeyfile(keyFile, "RAW", "FlatFieldEnabled", raw.enable_flatfield);
5136                 if (keyFile.has_key("RAW", "FlatFieldFile")) {
5137                     if (ppVersion >= 1017) {
5138                         raw.ff_file = filenameFromUri(keyFile.get_string("RAW", "FlatFieldFile"), basedir);
5139                     } else {
5140                         raw.ff_file = expandRelativePath(fname, "", keyFile.get_string("RAW", "FlatFieldFile"));
5141                     }
5142                 }
5143 
5144                 assignFromKeyfile(keyFile, "RAW", "FlatFieldAutoSelect", raw.ff_AutoSelect);
5145                 assignFromKeyfile(keyFile, "RAW", "FlatFieldBlurRadius", raw.ff_BlurRadius);
5146                 assignFromKeyfile(keyFile, "RAW", "FlatFieldBlurType", raw.ff_BlurType);
5147                 assignFromKeyfile(keyFile, "RAW", "FlatFieldAutoClipControl", raw.ff_AutoClipControl);
5148 
5149                 if (ppVersion < 328) {
5150                     // With ppversion < 328 this value was stored as a boolean, which is nonsense.
5151                     // To avoid annoying warnings we skip reading and assume 0.
5152                     raw.ff_clipControl = 0;
5153                 } else {
5154                     assignFromKeyfile(keyFile, "RAW", "FlatFieldClipControl", raw.ff_clipControl);
5155                 }
5156                 assignFromKeyfile(keyFile, "RAW", "FlatFieldUseEmbedded", raw.ff_embedded);
5157             }
5158 
5159             if (RELEVANT_(rawCA)) {
5160                 assignFromKeyfile(keyFile, "RAW", "CAEnabled", raw.enable_ca);
5161                 assignFromKeyfile(keyFile, "RAW", "CA", raw.ca_autocorrect);
5162                 if (ppVersion >= 342) {
5163                     assignFromKeyfile(keyFile, "RAW", "CAAutoIterations", raw.caautoiterations);
5164                 } else {
5165                     raw.caautoiterations = 1;
5166                 }
5167 
5168                 if (ppVersion >= 343) {
5169                     assignFromKeyfile(keyFile, "RAW", "CAAvoidColourshift", raw.ca_avoidcolourshift);
5170                 } else {
5171                     raw.ca_avoidcolourshift = false;
5172                 }
5173                 assignFromKeyfile(keyFile, "RAW", "CARed", raw.cared);
5174                 assignFromKeyfile(keyFile, "RAW", "CABlue", raw.cablue);
5175             }
5176 
5177             if (RELEVANT_(hotDeadPixelFilter)) {
5178                 assignFromKeyfile(keyFile, "RAW", "HotDeadPixelEnabled", raw.enable_hotdeadpix);
5179                 // For compatibility to elder pp3 versions
5180                 assignFromKeyfile(keyFile, "RAW", "HotDeadPixels", raw.hotPixelFilter);
5181                 raw.deadPixelFilter = raw.hotPixelFilter;
5182 
5183                 assignFromKeyfile(keyFile, "RAW", "HotPixelFilter", raw.hotPixelFilter);
5184                 assignFromKeyfile(keyFile, "RAW", "DeadPixelFilter", raw.deadPixelFilter);
5185                 assignFromKeyfile(keyFile, "RAW", "HotDeadPixelThresh", raw.hotdeadpix_thresh);
5186             }
5187             if (RELEVANT_(rawWhite)) {
5188                 assignFromKeyfile(keyFile, "RAW", "PreExposureEnabled", raw.enable_whitepoint);
5189                 assignFromKeyfile(keyFile, "RAW", "PreExposure", raw.expos);
5190             }
5191 
5192             if (ppVersion < 320) {
5193                 if (RELEVANT_(demosaic)) {
5194                     //assignFromKeyfile(keyFile, "RAW", "Method", raw.bayersensor.method);
5195                     assignFromKeyfile(keyFile, "RAW", "CcSteps", raw.bayersensor.ccSteps);
5196                     // assignFromKeyfile(keyFile, "RAW", "DCBIterations", raw.bayersensor.dcb_iterations);
5197                     // assignFromKeyfile(keyFile, "RAW", "DCBEnhance", raw.bayersensor.dcb_enhance);
5198                     assignFromKeyfile(keyFile, "RAW", "LMMSEIterations", raw.bayersensor.lmmse_iterations);
5199                 }
5200                 if (RELEVANT_(rawPreprocessing)) {
5201                     assignFromKeyfile(keyFile, "RAW", "LineDenoise", raw.bayersensor.linenoise);
5202                     assignFromKeyfile(keyFile, "RAW", "GreenEqThreshold", raw.bayersensor.greenthresh);
5203                 }
5204                 if (RELEVANT_(rawBlack)) {
5205                     assignFromKeyfile(keyFile, "RAW", "PreBlackzero", raw.bayersensor.black0);
5206                     assignFromKeyfile(keyFile, "RAW", "PreBlackone", raw.bayersensor.black1);
5207                     assignFromKeyfile(keyFile, "RAW", "PreBlacktwo", raw.bayersensor.black2);
5208                     assignFromKeyfile(keyFile, "RAW", "PreBlackthree", raw.bayersensor.black3);
5209                     assignFromKeyfile(keyFile, "RAW", "PreTwoGreen", raw.bayersensor.twogreen);
5210                 }
5211             }
5212         }
5213 
5214         if (keyFile.has_group("RAW Bayer")) {
5215             if (RELEVANT_(demosaic)) {
5216                 Glib::ustring method;
5217                 if (assignFromKeyfile(keyFile, "RAW Bayer", "Method", method)) {
5218                     auto &v = raw.bayersensor.getMethodStrings();
5219                     auto it = std::find(v.begin(), v.end(), method);
5220                     if (it != v.end()) {
5221                         raw.bayersensor.method = RAWParams::BayerSensor::Method(it - v.begin());
5222                     } else if (method == "amazevng4" || method == "dcbvng4") {
5223                         raw.bayersensor.method = RAWParams::BayerSensor::Method::AMAZEBILINEAR;
5224                     } else if (method == "rcdvng4") {
5225                         raw.bayersensor.method = RAWParams::BayerSensor::Method::RCDBILINEAR;
5226                     } else {
5227                         raw.bayersensor.method = RAWParams::BayerSensor::Method::AMAZE;
5228                     }
5229                 }
5230                 assignFromKeyfile(keyFile, "RAW Bayer", "Border", raw.bayersensor.border);
5231 
5232                 if (keyFile.has_key("RAW Bayer", "ImageNum")) {
5233                     raw.bayersensor.imageNum = keyFile.get_integer("RAW Bayer", "ImageNum") - 1;
5234                 }
5235 
5236                 assignFromKeyfile(keyFile, "RAW Bayer", "CcSteps", raw.bayersensor.ccSteps);
5237             }
5238 
5239             if (RELEVANT_(rawBlack)) {
5240                 assignFromKeyfile(keyFile, "RAW Bayer", "PreBlackEnabled", raw.bayersensor.enable_black);
5241                 assignFromKeyfile(keyFile, "RAW Bayer", "PreBlack0", raw.bayersensor.black0);
5242                 assignFromKeyfile(keyFile, "RAW Bayer", "PreBlack1", raw.bayersensor.black1);
5243                 assignFromKeyfile(keyFile, "RAW Bayer", "PreBlack2", raw.bayersensor.black2);
5244                 assignFromKeyfile(keyFile, "RAW Bayer", "PreBlack3", raw.bayersensor.black3);
5245                 assignFromKeyfile(keyFile, "RAW Bayer", "PreTwoGreen", raw.bayersensor.twogreen);
5246             }
5247 
5248             if (RELEVANT_(rawPreprocessing)) {
5249                 assignFromKeyfile(keyFile, "RAW Bayer", "PreprocessingEnabled", raw.bayersensor.enable_preproc);
5250                 assignFromKeyfile(keyFile, "RAW Bayer", "LineDenoise", raw.bayersensor.linenoise);
5251 
5252                 if (keyFile.has_key("RAW Bayer", "LineDenoiseDirection")) {
5253                     raw.bayersensor.linenoiseDirection = RAWParams::BayerSensor::LineNoiseDirection(keyFile.get_integer("RAW Bayer", "LineDenoiseDirection"));
5254                 }
5255 
5256                 assignFromKeyfile(keyFile, "RAW Bayer", "GreenEqThreshold", raw.bayersensor.greenthresh);
5257             }
5258 
5259             if (RELEVANT_(demosaic)) {
5260                 // assignFromKeyfile(keyFile, "RAW Bayer", "DCBIterations", raw.bayersensor.dcb_iterations);
5261                 // assignFromKeyfile(keyFile, "RAW Bayer", "DCBEnhance", raw.bayersensor.dcb_enhance);
5262                 assignFromKeyfile(keyFile, "RAW Bayer", "LMMSEIterations", raw.bayersensor.lmmse_iterations);
5263                 assignFromKeyfile(keyFile, "RAW Bayer", "DualDemosaicAutoContrast", raw.bayersensor.dualDemosaicAutoContrast);
5264                 if (ppVersion < 345) {
5265                     raw.bayersensor.dualDemosaicAutoContrast = false;
5266                 }
5267                 assignFromKeyfile(keyFile, "RAW Bayer", "DualDemosaicContrast", raw.bayersensor.dualDemosaicContrast);
5268 
5269                 if (keyFile.has_key("RAW Bayer", "PixelShiftMotionCorrectionMethod")) {
5270                     raw.bayersensor.pixelShiftMotionCorrectionMethod = (RAWParams::BayerSensor::PSMotionCorrectionMethod)keyFile.get_integer("RAW Bayer", "PixelShiftMotionCorrectionMethod");
5271                 }
5272 
5273                 assignFromKeyfile(keyFile, "RAW Bayer", "PixelShiftEperIso", raw.bayersensor.pixelShiftEperIso);
5274                 if (ppVersion < 332) {
5275                     raw.bayersensor.pixelShiftEperIso += 1.0;
5276                 }
5277                 assignFromKeyfile(keyFile, "RAW Bayer", "PixelShiftSigma", raw.bayersensor.pixelShiftSigma);
5278                 assignFromKeyfile(keyFile, "RAW Bayer", "PixelShiftShowMotion", raw.bayersensor.pixelShiftShowMotion);
5279                 assignFromKeyfile(keyFile, "RAW Bayer", "PixelShiftShowMotionMaskOnly", raw.bayersensor.pixelShiftShowMotionMaskOnly);
5280                 assignFromKeyfile(keyFile, "RAW Bayer", "pixelShiftHoleFill", raw.bayersensor.pixelShiftHoleFill);
5281                 assignFromKeyfile(keyFile, "RAW Bayer", "pixelShiftMedian", raw.bayersensor.pixelShiftMedian);
5282                 assignFromKeyfile(keyFile, "RAW Bayer", "pixelShiftGreen", raw.bayersensor.pixelShiftGreen);
5283                 assignFromKeyfile(keyFile, "RAW Bayer", "pixelShiftBlur", raw.bayersensor.pixelShiftBlur);
5284                 assignFromKeyfile(keyFile, "RAW Bayer", "pixelShiftSmoothFactor", raw.bayersensor.pixelShiftSmoothFactor);
5285                 assignFromKeyfile(keyFile, "RAW Bayer", "pixelShiftEqualBright", raw.bayersensor.pixelShiftEqualBright);
5286                 assignFromKeyfile(keyFile, "RAW Bayer", "pixelShiftEqualBrightChannel", raw.bayersensor.pixelShiftEqualBrightChannel);
5287                 assignFromKeyfile(keyFile, "RAW Bayer", "pixelShiftNonGreenCross", raw.bayersensor.pixelShiftNonGreenCross);
5288 
5289                 if (ppVersion < 336) {
5290                     if (keyFile.has_key("RAW Bayer", "pixelShiftLmmse")) {
5291                         bool useLmmse = keyFile.get_boolean ("RAW Bayer", "pixelShiftLmmse");
5292                         if (useLmmse) {
5293                             raw.bayersensor.pixelShiftDemosaicMethod = raw.bayersensor.getPSDemosaicMethodString(RAWParams::BayerSensor::PSDemosaicMethod::LMMSE);
5294                         } else {
5295                             raw.bayersensor.pixelShiftDemosaicMethod = raw.bayersensor.getPSDemosaicMethodString(RAWParams::BayerSensor::PSDemosaicMethod::AMAZE);
5296                         }
5297                     }
5298                 } else {
5299                     assignFromKeyfile(keyFile, "RAW Bayer", "pixelShiftDemosaicMethod", raw.bayersensor.pixelShiftDemosaicMethod);
5300                 }
5301             }
5302 
5303             if (RELEVANT_(rawPreprocessing)) {
5304                 assignFromKeyfile(keyFile, "RAW Bayer", "PDAFLinesFilter", raw.bayersensor.pdafLinesFilter);
5305                 assignFromKeyfile(keyFile, "RAW Bayer", "DynamicRowNoiseFilter", raw.bayersensor.dynamicRowNoiseFilter);
5306             }
5307         }
5308 
5309         if (keyFile.has_group("RAW X-Trans")) {
5310             if (RELEVANT_(demosaic)) {
5311                 Glib::ustring method;
5312                 if (assignFromKeyfile(keyFile, "RAW X-Trans", "Method", method)) {
5313                     const auto &v = RAWParams::XTransSensor::getMethodStrings();
5314                     auto it = std::find(v.begin(), v.end(), method);
5315                     if (it != v.end()) {
5316                         raw.xtranssensor.method = RAWParams::XTransSensor::Method(it - v.begin());
5317                     } else {
5318                         raw.xtranssensor.method = RAWParams::XTransSensor::Method::THREE_PASS;
5319                     }
5320                 }
5321                 assignFromKeyfile(keyFile, "RAW X-Trans", "DualDemosaicAutoContrast", raw.xtranssensor.dualDemosaicAutoContrast);
5322                 if (ppVersion < 345) {
5323                     raw.xtranssensor.dualDemosaicAutoContrast = false;
5324                 }
5325                 assignFromKeyfile(keyFile, "RAW X-Trans", "DualDemosaicContrast", raw.xtranssensor.dualDemosaicContrast);
5326                 assignFromKeyfile(keyFile, "RAW X-Trans", "Border", raw.xtranssensor.border);
5327                 assignFromKeyfile(keyFile, "RAW X-Trans", "CcSteps", raw.xtranssensor.ccSteps);
5328             }
5329             if (RELEVANT_(rawBlack)) {
5330                 assignFromKeyfile(keyFile, "RAW X-Trans", "PreBlackEnabled", raw.xtranssensor.enable_black);
5331                 assignFromKeyfile(keyFile, "RAW X-Trans", "PreBlackRed", raw.xtranssensor.blackred);
5332                 assignFromKeyfile(keyFile, "RAW X-Trans", "PreBlackGreen", raw.xtranssensor.blackgreen);
5333                 assignFromKeyfile(keyFile, "RAW X-Trans", "PreBlackBlue", raw.xtranssensor.blackblue);
5334             }
5335         }
5336 
5337         if (keyFile.has_group("Film Negative") && RELEVANT_(filmNegative)) {
5338             assignFromKeyfile(keyFile, "Film Negative", "Enabled", filmNegative.enabled);
5339             assignFromKeyfile(keyFile, "Film Negative", "RedRatio", filmNegative.redRatio);
5340             assignFromKeyfile(keyFile, "Film Negative", "GreenExponent", filmNegative.greenExp);
5341             assignFromKeyfile(keyFile, "Film Negative", "BlueRatio", filmNegative.blueRatio);
5342             if (ppVersion >= 1011) {
5343                 assignFromKeyfile(keyFile, "Film Negative", "RedBase", filmNegative.redBase);
5344                 assignFromKeyfile(keyFile, "Film Negative", "GreenBase", filmNegative.greenBase);
5345                 assignFromKeyfile(keyFile, "Film Negative", "BlueBase", filmNegative.blueBase);
5346             } else {
5347                 // Backwards compatibility: use special film base value -1
5348                 filmNegative.redBase = -1.f;
5349                 filmNegative.greenBase = -1.f;
5350                 filmNegative.blueBase = -1.f;
5351             }
5352         }
5353 
5354         if (keyFile.has_group("MetaData") && RELEVANT_(metadata)) {
5355             int mode = int(ppVersion < 1012 ? MetaDataParams::TUNNEL : MetaDataParams::EDIT);
5356             assignFromKeyfile(keyFile, "MetaData", "Mode", mode);
5357 
5358             if (mode >= int(MetaDataParams::TUNNEL) && mode <= int(MetaDataParams::STRIP)) {
5359                 metadata.mode = static_cast<MetaDataParams::Mode>(mode);
5360             }
5361 
5362             if (!assignFromKeyfile(keyFile, "MetaData", "ExifKeys", metadata.exifKeys)) {
5363                 if (ppVersion < 1012) {
5364                     metadata.exifKeys = { "*" };
5365                 }
5366             }
5367         }
5368 
5369         if (keyFile.has_group("Exif") && RELEVANT_(exif)) {
5370             for (const auto& key : keyFile.get_keys("Exif")) {
5371                 auto it = exif_keys.find(key);
5372                 if (it != exif_keys.end()) {
5373                     metadata.exif[it->second] = keyFile.get_string("Exif", key);
5374                 }
5375             }
5376         }
5377 
5378         /*
5379          * Load iptc change settings
5380          *
5381          * Existing values are preserved, and the stored values
5382          * are added to the list. To reset a field, the user has to
5383          * save the profile with the field leaved empty, but still
5384          * terminated by a semi-column ";"
5385          *
5386          * Please note that the old Keywords and SupplementalCategories
5387          * tag content is fully replaced by the new one,
5388          * i.e. they don't merge
5389          */
5390         if (keyFile.has_group("IPTC") && RELEVANT_(iptc)) {
5391             for (const auto& key : keyFile.get_keys("IPTC")) {
5392                 // does this key already exist?
5393                 auto it = iptc_keys.find(key);
5394                 if (it == iptc_keys.end()) {
5395                     continue;
5396                 }
5397                 auto kk = it->second;
5398                 const IPTCPairs::iterator element = metadata.iptc.find(kk);
5399 
5400                 if (element != metadata.iptc.end()) {
5401                     // it already exist so we cleanup the values
5402                     element->second.clear();
5403                 }
5404 
5405                 // TODO: look out if merging Keywords and SupplementalCategories from the procparams chain would be interesting
5406                 for (const auto& currLoadedTagValue : keyFile.get_string_list("IPTC", key)) {
5407                     metadata.iptc[kk].push_back(currLoadedTagValue);
5408                 }
5409             }
5410         }
5411 
5412         return 0;
5413     } catch (const Glib::Error& e) {
5414         //printf("-->%s\n", e.what().c_str());
5415         if (pl) {
5416             pl->error(Glib::ustring::compose(M("PROCPARAMS_LOAD_ERROR"), fname, e.what()));
5417         }
5418         if (resetOnError) {
5419             setDefaults();
5420         }
5421         return 1;
5422     } catch (std::exception &e) {
5423         if (pl) {
5424             pl->error(Glib::ustring::compose(M("PROCPARAMS_LOAD_ERROR"), fname, e.what()));
5425         }
5426         if (resetOnError) {
5427             setDefaults();
5428         }
5429         return 1;
5430     } catch (...) {
5431         //printf("-->unknown exception!\n");
5432         if (pl) {
5433             pl->error(Glib::ustring::compose(M("PROCPARAMS_LOAD_ERROR"), fname, "unknown exception"));
5434         }
5435         if (resetOnError) {
5436             setDefaults();
5437         }
5438         return 1;
5439     }
5440 
5441     return 0;
5442 
5443 #undef RELEVANT_
5444 }
5445 
5446 
load(ProgressListener * pl,const KeyFile & keyFile,const ParamsEdited * pedited,bool resetOnError,const Glib::ustring & fname)5447 int ProcParams::load(ProgressListener *pl,
5448                      const KeyFile &keyFile, const ParamsEdited *pedited,
5449                      bool resetOnError, const Glib::ustring &fname)
5450 {
5451     return load(pl, true, keyFile, pedited, resetOnError, fname);
5452 }
5453 
5454 
create()5455 ProcParams* ProcParams::create()
5456 {
5457     return new ProcParams();
5458 }
5459 
destroy(ProcParams * pp)5460 void ProcParams::destroy(ProcParams* pp)
5461 {
5462     delete pp;
5463 }
5464 
operator ==(const ProcParams & other) const5465 bool ProcParams::operator ==(const ProcParams& other) const
5466 {
5467     return
5468         exposure == other.exposure
5469         && saturation == other.saturation
5470         && toneCurve == other.toneCurve
5471         && localContrast == other.localContrast
5472         && labCurve == other.labCurve
5473         && sharpening == other.sharpening
5474         && prsharpening == other.prsharpening
5475         && wb == other.wb
5476         && impulseDenoise == other.impulseDenoise
5477         && denoise == other.denoise
5478         && textureBoost == other.textureBoost
5479         && fattal == other.fattal
5480         && logenc == other.logenc
5481         && defringe == other.defringe
5482         && toneEqualizer == other.toneEqualizer
5483         && crop == other.crop
5484         && coarse == other.coarse
5485         && rotate == other.rotate
5486         && commonTrans == other.commonTrans
5487         && distortion == other.distortion
5488         && lensProf == other.lensProf
5489         && perspective == other.perspective
5490         && gradient == other.gradient
5491         && pcvignette == other.pcvignette
5492         && cacorrection == other.cacorrection
5493         && vignetting == other.vignetting
5494         && chmixer == other.chmixer
5495         && blackwhite == other.blackwhite
5496         && hsl == other.hsl
5497         && resize == other.resize
5498         && raw == other.raw
5499         && icm == other.icm
5500         && filmSimulation == other.filmSimulation
5501         && softlight == other.softlight
5502         && rgbCurves == other.rgbCurves
5503         && metadata == other.metadata
5504         && dehaze == other.dehaze
5505         && grain == other.grain
5506         && smoothing == other.smoothing
5507         && colorcorrection == other.colorcorrection
5508         && filmNegative == other.filmNegative
5509         && spot == other.spot;
5510 }
5511 
operator !=(const ProcParams & other) const5512 bool ProcParams::operator !=(const ProcParams& other) const
5513 {
5514     return !(*this == other);
5515 }
5516 
init()5517 void ProcParams::init()
5518 {
5519 }
5520 
cleanup()5521 void ProcParams::cleanup()
5522 {
5523 }
5524 
write(ProgressListener * pl,const Glib::ustring & fname,const Glib::ustring & content) const5525 int ProcParams::write(ProgressListener *pl,
5526                       const Glib::ustring& fname, const Glib::ustring& content) const
5527 {
5528     int error = 0;
5529 
5530     if (fname.length()) {
5531         FILE *f;
5532         f = g_fopen(fname.c_str(), "wt");
5533 
5534         if (f == nullptr) {
5535             if (pl) {
5536                 pl->error(Glib::ustring::compose(M("PROCPARAMS_SAVE_ERROR"), fname, "write error"));
5537             }
5538             error = 1;
5539         } else {
5540             fprintf(f, "%s", content.c_str());
5541             fclose(f);
5542         }
5543     }
5544 
5545     return error;
5546 }
5547 
5548 
FullPartialProfile(const ProcParams & pp)5549 FullPartialProfile::FullPartialProfile(const ProcParams &pp):
5550     pp_(pp)
5551 {
5552 }
5553 
5554 
applyTo(ProcParams & pp) const5555 bool FullPartialProfile::applyTo(ProcParams &pp) const
5556 {
5557     pp = pp_;
5558     return true;
5559 }
5560 
5561 
FilePartialProfile(ProgressListener * pl,const Glib::ustring & fname,bool full)5562 FilePartialProfile::FilePartialProfile(ProgressListener *pl, const Glib::ustring &fname, bool full):
5563     pl_(pl),
5564     fname_(fname),
5565     full_(full)
5566 {
5567 }
5568 
5569 
applyTo(ProcParams & pp) const5570 bool FilePartialProfile::applyTo(ProcParams &pp) const
5571 {
5572     if (full_) {
5573         pp.setDefaults();
5574     }
5575     return fname_.empty() || (pp.load(pl_, fname_) == 0);
5576 }
5577 
5578 
PEditedPartialProfile(ProgressListener * pl,const Glib::ustring & fname,const ParamsEdited & pe)5579 PEditedPartialProfile::PEditedPartialProfile(ProgressListener *pl, const Glib::ustring &fname, const ParamsEdited &pe):
5580     pl_(pl),
5581     fname_(fname),
5582     pp_(),
5583     pe_(pe)
5584 {
5585 }
5586 
5587 
PEditedPartialProfile(const ProcParams & pp,const ParamsEdited & pe)5588 PEditedPartialProfile::PEditedPartialProfile(const ProcParams &pp, const ParamsEdited &pe):
5589     pl_(nullptr),
5590     fname_(""),
5591     pp_(pp),
5592     pe_(pe)
5593 {
5594 }
5595 
5596 
applyTo(ProcParams & pp) const5597 bool PEditedPartialProfile::applyTo(ProcParams &pp) const
5598 {
5599     if (!fname_.empty()) {
5600         KeyFile keyfile;
5601         try {
5602             if (!Glib::file_test(fname_, Glib::FILE_TEST_EXISTS) ||
5603                 !keyfile.load_from_file(fname_)) {
5604                 // not an error to repor
5605                 return false;
5606             }
5607         } catch (const Glib::Error& e) {
5608             //printf("-->%s\n", e.what().c_str());
5609             if (pl_) {
5610                 pl_->error(Glib::ustring::compose(M("PROCPARAMS_LOAD_ERROR"), fname_, e.what()));
5611             }
5612             return false;
5613         }
5614         return pp.load(pl_, keyfile, &pe_, false) == 0;
5615     } else {
5616         KeyFile keyfile;
5617         if (pp_.save(pl_, keyfile, &pe_) == 0) {
5618             return pp.load(pl_, keyfile, &pe_, false) == 0;
5619         }
5620     }
5621     return false;
5622 }
5623 
5624 
5625 //-----------------------------------------------------------------------------
5626 // ProcParamsWithSnapshots
5627 //-----------------------------------------------------------------------------
5628 
load(ProgressListener * pl,const Glib::ustring & fname)5629 int ProcParamsWithSnapshots::load(ProgressListener *pl,
5630                                   const Glib::ustring &fname)
5631 {
5632     setlocale(LC_NUMERIC, "C");  // to set decimal point to "."
5633 
5634     if (fname.empty()) {
5635         return 1;
5636     }
5637 
5638     KeyFile keyfile;
5639     keyfile.setProgressListener(pl);
5640 
5641     snapshots.clear();
5642 
5643     try {
5644         if (!Glib::file_test(fname, Glib::FILE_TEST_EXISTS) ||
5645             !keyfile.load_from_file(fname)) {
5646             // not an error to report
5647             return 1;
5648         }
5649         if (master.load(pl, true, keyfile, nullptr, true, fname) != 0) {
5650             return 1;
5651         }
5652         const std::string sn = "Snapshot_";
5653         if (keyfile.has_group("Snapshots")) {
5654             for (size_t i = 1; ; ++i) {
5655                 Glib::ustring key = sn + std::to_string(i);
5656                 if (keyfile.has_key("Snapshots", key)) {
5657                     auto name = keyfile.get_string("Snapshots", key);
5658                     snapshots.push_back(std::make_pair(name, ProcParams()));
5659                 } else {
5660                     break;
5661                 }
5662             }
5663         }
5664 
5665         for (size_t i = 0; i < snapshots.size(); ++i) {
5666             keyfile.set_prefix(sn + std::to_string(i+1) + " ");
5667             snapshots[i].second.appVersion = master.appVersion;
5668             snapshots[i].second.ppVersion = master.ppVersion;
5669             if (snapshots[i].second.load(pl, false, keyfile, nullptr, true, fname) != 0) {
5670                 snapshots.resize(i);
5671                 break;
5672             }
5673         }
5674 
5675         return 0;
5676     } catch (const Glib::Error &e) {
5677         if (pl) {
5678             pl->error(Glib::ustring::compose(M("PROCPARAMS_LOAD_ERROR"), fname, e.what()));
5679         }
5680         //printf("-->%s\n", e.what().c_str());
5681         master.setDefaults();
5682         snapshots.clear();
5683         return 1;
5684     } catch (...) {
5685         if (pl) {
5686             pl->error(Glib::ustring::compose(M("PROCPARAMS_LOAD_ERROR"), fname, "unknown exception"));
5687         }
5688         //printf("-->unknown exception!\n");
5689         master.setDefaults();
5690         snapshots.clear();
5691         return 1;
5692     }
5693 }
5694 
5695 
save(ProgressListener * pl,const Glib::ustring & fname,const Glib::ustring & fname2)5696 int ProcParamsWithSnapshots::save(ProgressListener *pl,
5697                                   const Glib::ustring &fname, const Glib::ustring &fname2)
5698 {
5699     if (fname.empty() && fname2.empty()) {
5700         return 0;
5701     }
5702 
5703     Glib::ustring data;
5704 
5705     try {
5706         KeyFile keyfile;
5707 
5708         keyfile.set_string("Version", "AppVersion", RTVERSION);
5709         keyfile.set_integer("Version", "Version", PPVERSION);
5710         if (master.rank >= 0) {
5711             saveToKeyfile("General", "Rank", master.rank, keyfile);
5712         }
5713         saveToKeyfile("General", "ColorLabel", master.colorlabel, keyfile);
5714         saveToKeyfile("General", "InTrash", master.inTrash, keyfile);
5715 
5716         const std::string sn = "Snapshot_";
5717         for (size_t i = 0; i < snapshots.size(); ++i) {
5718             Glib::ustring key = sn + std::to_string(i+1);
5719             keyfile.set_string("Snapshots", key, snapshots[i].first);
5720         }
5721 
5722         int ret = master.save(pl, false, keyfile, nullptr, fname);
5723         if (ret != 0) {
5724             return ret;
5725         }
5726 
5727         for (size_t i = 0; i < snapshots.size(); ++i) {
5728             keyfile.set_prefix(sn + std::to_string(i+1) + " ");
5729             ret = snapshots[i].second.save(pl, false, keyfile, nullptr, fname);
5730             if (ret != 0) {
5731                 return ret;
5732             }
5733         }
5734 
5735         data = keyfile.to_data();
5736     } catch (Glib::KeyFileError &exc) {
5737         if (pl) {
5738             pl->error(Glib::ustring::compose(M("PROCPARAMS_SAVE_ERROR"), fname, exc.what()));
5739         }
5740     }
5741 
5742     if (data.empty()) {
5743         return 1;
5744     }
5745 
5746     int error1, error2;
5747     error1 = master.write(pl, fname, data);
5748 
5749     if (!fname2.empty()) {
5750         error2 = master.write(pl, fname2, data);
5751         // If at least one file has been saved, it's a success
5752         return error1 & error2;
5753     } else {
5754         return error1;
5755     }
5756 
5757     return 0;
5758 }
5759 
5760 }} // namespace rtengine::procparams
5761 
5762