Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
lens.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2009-2013, 2016 johannes hanika.
4 Copyright (C) 2010 Alexandre Prokoudine.
5 Copyright (C) 2010-2011 Bruce Guenter.
6 Copyright (C) 2010-2011, 2013 Henrik Andersson.
7 Copyright (C) 2010 Milan Knížek.
8 Copyright (C) 2010, 2013-2014 Pascal de Bruijn.
9 Copyright (C) 2010 Stuart Henderson.
10 Copyright (C) 2010 Thierry Leconte.
11 Copyright (C) 2011, 2013 Antony Dovgal.
12 Copyright (C) 2011-2012 Jérémy Rosen.
13 Copyright (C) 2011 Olivier Tribout.
14 Copyright (C) 2011 Robert Bieber.
15 Copyright (C) 2011 Rostyslav Pidgornyi.
16 Copyright (C) 2011-2014, 2016-2019 Tobias Ellinghaus.
17 Copyright (C) 2012 Edouard Gomez.
18 Copyright (C) 2012-2013 Gabriel Ebner.
19 Copyright (C) 2012, 2015, 2019 parafin.
20 Copyright (C) 2012 Richard Wonka.
21 Copyright (C) 2012 Sergey Pavlov.
22 Copyright (C) 2012-2014, 2016-2017 Ulrich Pegelow.
23 Copyright (C) 2013, 2020-2021 Aldric Renaudin.
24 Copyright (C) 2013 Guilherme Brondani Torri.
25 Copyright (C) 2013 Ivan Tarozzi.
26 Copyright (C) 2013-2016 Roman Lebedev.
27 Copyright (C) 2013 Simon Spannagel.
28 Copyright (C) 2013 Thomas Pryds.
29 Copyright (C) 2013-2015 Torsten Bronger.
30 Copyright (C) 2015 Pedro Côrte-Real.
31 Copyright (C) 2016, 2018-2022 Pascal Obry.
32 Copyright (C) 2017 Heiko Bauke.
33 Copyright (C) 2018-2026 Aurélien PIERRE.
34 Copyright (C) 2018 Edgardo Hoszowski.
35 Copyright (C) 2018 Kelvie Wong.
36 Copyright (C) 2018 Maurizio Paglia.
37 Copyright (C) 2018 Peter Budai.
38 Copyright (C) 2018, 2021 rawfiner.
39 Copyright (C) 2019 Andreas Schneider.
40 Copyright (C) 2019 David-Tillmann Schaefer.
41 Copyright (C) 2019 Diederik ter Rahe.
42 Copyright (C) 2019 Jakub Filipowicz.
43 Copyright (C) 2019 Kevin Daudt.
44 Copyright (C) 2020-2021 Chris Elston.
45 Copyright (C) 2020-2022 Diederik Ter Rahe.
46 Copyright (C) 2020-2022 Hanno Schwalm.
47 Copyright (C) 2020 Hubert Kowalski.
48 Copyright (C) 2020-2021 Ralf Brown.
49 Copyright (C) 2021 fvollmer.
50 Copyright (C) 2022 Martin Bařinka.
51 Copyright (C) 2022 Nicolas Auffray.
52 Copyright (C) 2022 Philipp Lutz.
53 Copyright (C) 2024-2025 Alynx Zhou.
54
55 darktable is free software: you can redistribute it and/or modify
56 it under the terms of the GNU General Public License as published by
57 the Free Software Foundation, either version 3 of the License, or
58 (at your option) any later version.
59
60 darktable is distributed in the hope that it will be useful,
61 but WITHOUT ANY WARRANTY; without even the implied warranty of
62 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
63 GNU General Public License for more details.
64
65 You should have received a copy of the GNU General Public License
66 along with darktable. If not, see <http://www.gnu.org/licenses/>.
67*/
69#include "common/paths.h" // DT_PATH_MAX
70#include "common/utility.h"
71#include "system/macros.h"
73#include "common/logging.h"
74#include "system/mem_alloc.h"
75#include "system/openmp.h"
78#include "glib.h"
79
80#ifdef HAVE_CONFIG_H
81#include "config.h"
82#endif
83#include "widgets/bauhaus.h"
84#include "pixel/interpolation.h"
86#include "common/imagebuf.h"
87#include "common/opencl.h"
88#include "develop/develop.h"
89#include "develop/imageop.h"
90#include "develop/imageop_gui.h"
91#include "develop/tiling.h"
92
93#include "iop/iop_api.h"
94#include <assert.h>
95#include <ctype.h>
96#include <gtk/gtk.h>
97#include <inttypes.h>
98#include <math.h>
99#include <stdlib.h>
100#include <string.h>
101
102#include "widgets/popup.h"
103#include "widgets/widget_style.h"
104#include "control/signal.h"
105
107
108#include "lensserious.h" // side-by-side latch against lensfun, see feat/lensserious
109#include "lensserious_db.h" // ... and its calibration database, latched the same way
110#include "lensserious_vendor.h"
111
112
113/* The correction axes and the projection numbering.
114 *
115 * These values are lensfun's, and they must stay lensfun's: `modify_flags` and
116 * `target_geom` are SERIALIZED into every user's history and every preset that has ever
117 * been saved. Defining them here rather than including them is what lets liblensfun go
118 * without rewriting anyone's edits.
119 *
120 * The projection numbering is also, entry for entry, ls_lens_type_t's -- asserted below
121 * rather than assumed, because the two now live in different repositories and nothing else
122 * would notice one of them growing a member in the middle. */
166{
167 /* --- low half: lensfun's serialized axis numbering, unchangeable --- */
168 DT_LENS_MODIFY_TCA = 0x00000001,
173
174 /* --- high half: how far up sits each axis's two-bit source field --- */
180
191#define DT_LENS_MODIFY_DISTORTION_PACK \
192 (DT_LENS_MODIFY_DISTORTION | DT_LENS_MODIFY_GEOMETRY | DT_LENS_MODIFY_SCALE)
193
195#define DT_LENS_MODIFY_ANSEL_MASK 0xFF000000u
196
205#define DT_LENS_MODIFY_ALL_AXES ((int)~DT_LENS_MODIFY_ANSEL_MASK)
206
207/* The boundary, checked rather than described. Each of these is a way the two halves could
208 * come to overlap -- a sixth axis assigned too high, a source field assigned too low, a
209 * field running into the sign bit -- and each would corrupt the other half's meaning in a
210 * way that only shows up as a wrong correction on someone's photograph. */
211_Static_assert((DT_LENS_MODIFY_ALL_AXES & (int)DT_LENS_MODIFY_ANSEL_MASK) == 0,
212 "the axis space and Ansel's own bits must not overlap");
216 "every correction axis must live in the axis half");
217_Static_assert((DT_LENS_SOURCE_BITS << DT_LENS_SOURCE_SHIFT_VIGNETTING) > 0,
218 "no source field may reach the sign bit of modify_flags");
221 "the source fields must not overlap each other");
222
231
257
270
271_Static_assert((int)DT_LENS_RECTILINEAR == (int)LS_LENS_RECTILINEAR
272 && (int)DT_LENS_FISHEYE == (int)LS_LENS_FISHEYE
273 && (int)DT_LENS_PANORAMIC == (int)LS_LENS_PANORAMIC
274 && (int)DT_LENS_EQUIRECTANGULAR == (int)LS_LENS_EQUIRECTANGULAR
275 && (int)DT_LENS_FISHEYE_ORTHOGRAPHIC == (int)LS_LENS_FISHEYE_ORTHOGRAPHIC
276 && (int)DT_LENS_FISHEYE_STEREOGRAPHIC == (int)LS_LENS_FISHEYE_STEREOGRAPHIC
277 && (int)DT_LENS_FISHEYE_EQUISOLID == (int)LS_LENS_FISHEYE_EQUISOLID
278 && (int)DT_LENS_FISHEYE_THOBY == (int)LS_LENS_FISHEYE_THOBY,
279 "projection numbering must match ls_lens_type_t: stored params depend on it");
280
282
284{
285 /* The three axes a user can actually see corrected. Geometry and scale are not in it:
286 * they are how the correction is presented, not whether a lens flaw was fixed. */
289
290
292{
294 int inverse; // $MIN: 0 $MAX: 1 $DEFAULT: 0 $DESCRIPTION: "mode"
295 float scale; // $MIN: 0.1 $MAX: 2.0 $DEFAULT: 1.0
296 float crop;
297 float focal;
298 float aperture;
299 float distance;
300 dt_lens_type_t target_geom; // $DEFAULT: DT_LENS_RECTILINEAR $DESCRIPTION: "geometry"
301 char camera[128];
302 char lens[128];
303 gboolean tca_override; // $DEFAULT: FALSE $DESCRIPTION: "TCA overwrite"
304 float tca_r; // $MIN: 0.99 $MAX: 1.01 $DEFAULT: 1.0 $DESCRIPTION: "TCA red"
305 float tca_b; // $MIN: 0.99 $MAX: 1.01 $DEFAULT: 1.0 $DESCRIPTION: "TCA blue"
310 int modified; // $DEFAULT: 1
312
313/* ========================================================================================
314 * The modify_flags interface.
315 *
316 * Everything below this banner and above the next one is the only code in this file allowed
317 * to touch a bit of modify_flags. Everything else asks in terms of an axis and a source.
318 *
319 * That rule is not tidiness. The encoding has three traps, and each has already been walked
320 * into once: the source fields are two bits at a shift that is not the axis's bit position;
321 * the distortion "bit" is really three bits, so clearing the obvious one leaves projection
322 * and scaling running with their controls hidden; and manual TCA is ALSO recorded in a
323 * separate legacy boolean, so writing one without the other makes an edit mean two different
324 * things depending on which the reader trusts.
325 * ===================================================================================== */
326
330static inline int _lens_axis_flags(const dt_lens_axis_t axis)
331{
332 switch(axis)
333 {
337 default: return 0;
338 }
339}
340
348static inline int _lens_axis_presence_bit(const dt_lens_axis_t axis)
349{
351}
352
354static inline int _lens_axis_shift(const dt_lens_axis_t axis)
355{
356 switch(axis)
357 {
361 default: return DT_LENS_SOURCE_SHIFT_TCA;
362 }
363}
364
373static inline gboolean _lens_source_applicable(const dt_lens_axis_t axis,
374 const dt_lens_source_t source)
375{
376 if(source == DT_LENS_SOURCE_MANUAL) return axis == DT_LENS_AXIS_TCA;
377 return source > DT_LENS_SOURCE_OFF && source < DT_LENS_SOURCE_LAST;
378}
379
391static dt_lens_source_t _lens_source_decode(const int modify_flags,
392 const gboolean tca_override,
393 const dt_lens_axis_t axis)
394{
395 if(!(modify_flags & _lens_axis_presence_bit(axis))) return DT_LENS_SOURCE_OFF;
396
397 dt_lens_source_t source
398 = (dt_lens_source_t)((modify_flags >> _lens_axis_shift(axis)) & DT_LENS_SOURCE_BITS);
399
400 /* Zero is the database, because that is what every edit written before this decoded to. */
401 if(source == DT_LENS_SOURCE_OFF) source = DT_LENS_SOURCE_LENSFUN;
402
403 if(axis == DT_LENS_AXIS_TCA && source == DT_LENS_SOURCE_LENSFUN && tca_override)
404 source = DT_LENS_SOURCE_MANUAL;
405
406 /* A value this build has no meaning for -- a newer Ansel wrote it, or the field was
407 * corrupted. Correcting with the database beats refusing to correct at all. */
408 if(!_lens_source_applicable(axis, source)) source = DT_LENS_SOURCE_LENSFUN;
409
410 return source;
411}
412
415 const dt_lens_axis_t axis)
416{
417 return _lens_source_decode(p->modify_flags, p->tca_override, axis);
418}
419
432 dt_lens_source_t source)
433{
434 if(!_lens_source_applicable(axis, source)) source = DT_LENS_SOURCE_OFF;
435
436 p->modify_flags &= ~(DT_LENS_SOURCE_BITS << _lens_axis_shift(axis));
437
438 if(source == DT_LENS_SOURCE_OFF)
439 {
440 /* The whole pack for distortion: leaving GEOMETRY and SCALE set is what used to keep a
441 * projection change and a zoom running after their correction had been switched off. */
442 p->modify_flags &= ~_lens_axis_flags(axis);
443 /* The source bits stay cleared, so an axis switched off and on again comes back as the
444 * database rather than as whatever it was before. */
445 }
446 else
447 {
448 p->modify_flags |= _lens_axis_flags(axis);
449 p->modify_flags |= ((int)source & DT_LENS_SOURCE_BITS) << _lens_axis_shift(axis);
450 }
451
452 /* Kept in step rather than left to rot: an older Ansel reading this edit sees only
453 * tca_override, and it should still see the truth. */
454 if(axis == DT_LENS_AXIS_TCA)
455 p->tca_override = (source == DT_LENS_SOURCE_MANUAL) ? TRUE : FALSE;
456}
457
459static inline gboolean _lens_source_is(const dt_iop_lensfun_params_t *p,
460 const dt_lens_axis_t axis,
461 const dt_lens_source_t source)
462{
463 return _lens_source_get(p, axis) == source;
464}
465
467static inline gboolean _lens_axis_enabled(const dt_iop_lensfun_params_t *p,
468 const dt_lens_axis_t axis)
469{
470 return _lens_source_get(p, axis) != DT_LENS_SOURCE_OFF;
471}
472
481static inline gboolean _lens_flags_move_pixels(const int modify_flags)
482{
483 return (modify_flags & (DT_LENS_MODIFY_TCA | DT_LENS_MODIFY_DISTORTION_PACK)) != 0;
484}
485
497static inline gboolean _lens_source_is_library_served(const dt_lens_source_t source)
498{
499 return source == DT_LENS_SOURCE_LENSFUN || source == DT_LENS_SOURCE_MANUAL
500 || source == DT_LENS_SOURCE_EMBEDDED;
501}
502
504static inline gboolean _lens_flags_have_axis(const int modify_flags, const dt_lens_axis_t axis)
505{
506 return (modify_flags & _lens_axis_presence_bit(axis)) != 0;
507}
508
516static inline int _lens_mask_for_mono(const gboolean monochrome)
517{
518 return monochrome ? (DT_LENS_MODIFY_ALL_AXES & ~DT_LENS_MODIFY_TCA) : DT_LENS_MODIFY_ALL_AXES;
519}
520
521
522/* ===================================================================================== */
523
535static gboolean _lens_image_embeds(dt_iop_module_t *self, const dt_lens_axis_t axis)
536{
537 if(IS_NULL_PTR(self->dev)) return FALSE;
538 const int axes = ls_vendor_axes(&self->dev->image_storage.exif_correction);
539
540 switch(axis)
541 {
542 case DT_LENS_AXIS_DISTORTION: return (axes & LS_ENABLE_DISTORTION) != 0;
543 case DT_LENS_AXIS_TCA: return (axes & LS_ENABLE_TCA) != 0;
544 case DT_LENS_AXIS_VIGNETTING: return (axes & LS_ENABLE_VIGNETTING) != 0;
545 default: return FALSE;
546 }
547}
548
568
569/* Defined with the widgets they act on, at the bottom of the GUI section. Declared here
570 * because every setter that changes what the database can match -- the camera, the lens --
571 * has to say so, and those sit above them. */
574
585
586/* ---------------------------------------------------------------------------------------
587 * The LensSerious calibration database, latched BESIDE liblensfun's rather than replacing
588 * it.
589 *
590 * Every lookup below is answered twice -- once by liblensfun, once by LensSerious -- and
591 * both answers are printed. lensfun keeps authoring the pixels; nothing here changes what
592 * is rendered. The point is to run the real GUI on real images and read the log, because
593 * the disagreements that matter are not the ones a synthetic harness reaches: the harness
594 * walks the database, and this walks whatever a user's EXIF actually says, spelled however
595 * the camera spelled it.
596 *
597 * Reading is lock-free by construction -- `mode=ro&immutable=1` with SQLITE_OPEN_NOMUTEX,
598 * so SQLite takes no file lock, no shared-memory segment and no mutex. The price is ONE
599 * HANDLE PER THREAD, so the handle is thread-local and closed by its destructor when the
600 * thread ends. Threads that never touch a lens never open it.
601 *
602 * The one-entry caches beside it stand in for the two process-wide memo hash tables:
603 * commit_params() resolves the camera and the lens on every pipe resync, for every pipe,
604 * and asks the SAME question every time -- an image's camera and lens do not change while
605 * it is open. A per-thread cache of the last answer serves that exactly, with no lock and
606 * no unbounded growth, where a shared table would need a mutex back.
607 * ------------------------------------------------------------------------------------ */
608typedef struct _ls_tls_t
609{
610 ls_db_t *db;
611 gboolean tried;
612
613 char cam_key[512];
614 ls_camera_t cam;
615 gboolean cam_found;
616 gboolean cam_cached;
617
618 char lens_key[512];
619 long long lens_id;
620 gboolean lens_cached;
622
623/* Closed when the thread that opened it exits, which is the whole reason this is a GPrivate
624 * and not a plain __thread pointer: the handle has to be RELEASED, and nothing else in C
625 * runs code at thread exit. iop/drawlayer.c holds its per-thread scratch buffers the same
626 * way, for the same reason. */
627static void _ls_tls_free(gpointer data)
628{
629 _ls_tls_t *tls = (_ls_tls_t *)data;
630 if(IS_NULL_PTR(tls)) return;
631 if(!IS_NULL_PTR(tls->db)) ls_db_close(tls->db);
632 dt_free(tls);
633}
634
635static GPrivate _ls_tls_key = G_PRIVATE_INIT(_ls_tls_free);
636
640{
641 _ls_tls_t *tls = (_ls_tls_t *)g_private_get(&_ls_tls_key);
642 if(!IS_NULL_PTR(tls)) return tls;
643
644 tls = (_ls_tls_t *)g_malloc0(sizeof(_ls_tls_t));
645 if(IS_NULL_PTR(tls)) return NULL;
646 /* The only field whose zeroed value is not the right one: -1 is "no lens", 0 is a
647 * perfectly good row id. */
648 tls->lens_id = -1;
649 g_private_set(&_ls_tls_key, tls);
650 return tls;
651}
652
668static void _lens_report_db_failure(const char *path, const ls_db_open_status_t status,
669 const int found_version, const char *detail)
670{
671 const char *reason = (!IS_NULL_PTR(detail) && detail[0]) ? detail : "no further detail";
672
673 switch(status)
674 {
675 case LS_DB_OPEN_NO_FILE:
676 dt_print(DT_DEBUG_PIPE, "[lens] no `%s'\n", path);
677 break;
678
679 case LS_DB_OPEN_UNREADABLE:
680 /* Printed with whatever refused it, because it is not always the file: the path goes
681 * through a URI conversion before SQLite sees it, and on Windows a readable file got
682 * lost in there while "check its permissions" sent its owner looking at permissions
683 * that were fine. The detail says which of the two happened. */
685 "[lens] `%s' exists but could not be read: %s. Check its permissions, and "
686 "that it is not truncated\n", path, reason);
687 break;
688
689 case LS_DB_OPEN_SCHEMA:
690 /* The one that actually happens, and the one the old message hid: a lenses.db left
691 * behind in a prefix by an earlier install. The build only installs the file when it
692 * produced one, so a rebuild that could not run the importer leaves the old file in
693 * place and the new binary refuses it. */
695 "[lens] `%s' is a lens database of schema v%d, and this Ansel reads v%d. It "
696 "is almost certainly left over from an older install: rebuild and reinstall "
697 "so the file is replaced, or delete it if it is a stale copy in your "
698 "configuration directory.\n",
699 path, found_version, ls_db_schema_required());
700 break;
701
702 case LS_DB_OPEN_OK:
703 default:
704 break;
705 }
706}
707
708static ls_db_t *_ls_db(void)
709{
710 _ls_tls_t *tls = _ls_tls_get();
711 if(IS_NULL_PTR(tls)) return NULL;
712
713 if(tls->tried) return tls->db;
714 tls->tried = TRUE;
715
716 char dir[DT_PATH_MAX] = { 0 };
717 char path[DT_PATH_MAX] = { 0 };
718 char config_path[DT_PATH_MAX] = { 0 };
719 ls_db_open_status_t config_status = LS_DB_OPEN_NO_FILE;
720 int config_version = -1;
721 char config_detail[256] = { 0 };
722
723 dt_loc_get_user_config_dir(dir, sizeof(dir));
724 dt_concat_path_file(config_path, dir, "lenses.db");
725 tls->db = ls_db_open_diagnostic(config_path, &config_status, &config_version, config_detail,
726 sizeof(config_detail));
727
728 ls_db_open_status_t data_status = LS_DB_OPEN_NO_FILE;
729 int data_version = -1;
730 char data_detail[256] = { 0 };
731 if(IS_NULL_PTR(tls->db))
732 {
733 dt_loc_get_datadir(dir, sizeof(dir));
734 dt_concat_path_file(path, dir, "lenses.db");
735 tls->db = ls_db_open_diagnostic(path, &data_status, &data_version, data_detail,
736 sizeof(data_detail));
737 }
738
739 /* Say WHICH failure it was, per path. All three used to print "no calibration database",
740 * so a user whose file sat exactly where the message said it had looked was told to go
741 * find it again -- when what they actually had was a database from an older install,
742 * perfectly readable and built for a schema this Ansel does not read. */
743 if(IS_NULL_PTR(tls->db))
744 {
745 _lens_report_db_failure(config_path, config_status, config_version, config_detail);
746 _lens_report_db_failure(path, data_status, data_version, data_detail);
748 "[lens] no calibration database, so no lens correction is available.\n");
749 }
750 else
751 {
752 /* A config-directory database that exists and was refused is worth one line even when
753 * the shipped one saved the day: it is tried FIRST, so the user believes it is the one
754 * in use, and ansel-lens-db-update is what put it there. */
755 if(config_status != LS_DB_OPEN_OK && config_status != LS_DB_OPEN_NO_FILE)
756 _lens_report_db_failure(config_path, config_status, config_version, config_detail);
757
758 dt_print(DT_DEBUG_PIPE, "[lens] opened `%s' (schema v%d)\n",
759 (config_status == LS_DB_OPEN_OK) ? config_path : path,
760 ls_db_schema_version(tls->db));
761 }
762
763 return tls->db;
764}
765
767static gboolean _ls_find_camera(const char *maker, const char *model, ls_camera_t *out)
768{
769 if(IS_NULL_PTR(model) || !model[0]) return FALSE;
770 /* _ls_db() has already established that this thread has a cache block; it cannot have
771 * returned a database without one. */
772 _ls_tls_t *tls = _ls_tls_get();
773 ls_db_t *db = _ls_db();
774 if(IS_NULL_PTR(db) || IS_NULL_PTR(tls)) return FALSE;
775
776 char key[512];
777 snprintf(key, sizeof(key), "%s\x1f%s", maker ? maker : "", model);
778 if(tls->cam_cached && !strcmp(key, tls->cam_key))
779 {
780 if(tls->cam_found) *out = tls->cam;
781 return tls->cam_found ? TRUE : FALSE;
782 }
783
784 ls_camera_t cam;
785 /* A miss is cached too: it costs a lookup to establish and it will not change. */
786 const gboolean found = (ls_db_find_camera(db, maker, model, &cam) == 1) ? TRUE : FALSE;
787
788 g_strlcpy(tls->cam_key, key, sizeof(tls->cam_key));
789 tls->cam = cam;
790 tls->cam_found = found;
791 tls->cam_cached = TRUE;
792
793 if(found) *out = cam;
794 return found;
795}
796
829static long long _ls_find_lens(long long mount_id, float crop, float focal,
830 const char *lens_name)
831{
832 if(IS_NULL_PTR(lens_name) || !lens_name[0]) return -1;
833 _ls_tls_t *tls = _ls_tls_get();
834 ls_db_t *db = _ls_db();
835 if(IS_NULL_PTR(db) || IS_NULL_PTR(tls)) return -1;
836
837 char key[512];
838 snprintf(key, sizeof(key), "%lld\x1f%.4f\x1f%.4f\x1f%s", mount_id, (double)crop,
839 (double)focal, lens_name);
840 if(tls->lens_cached && !strcmp(key, tls->lens_key)) return tls->lens_id;
841
842 /* Several candidates rather than one, because the best-scoring name may be a lens this
843 * picture cannot have come through, and the next one down may be exactly right. */
844 ls_db_match_t m[8];
845 const int n = ls_db_match_lens(db, NULL, lens_name, mount_id, crop, m,
846 (int)(sizeof(m) / sizeof(*m)));
847 long long id = -1;
848 for(int i = 0; i < n; i++)
849 {
850 ls_lens_t cand;
851 if(ls_db_lens_by_id(db, m[i].lens_id, &cand) != 1) continue;
852
853 /* A hair of tolerance: EXIF focals are rounded, and a prime's range is a single value
854 * it must still match. Ranges are ordered by the importer. */
855 if(focal > 0.f
856 && (focal < cand.min_focal - 0.05f || focal > cand.max_focal + 0.05f))
857 continue;
858
859 id = m[i].lens_id;
860 break;
861 }
862
863 g_strlcpy(tls->lens_key, key, sizeof(tls->lens_key));
864 tls->lens_id = id;
865 tls->lens_cached = TRUE;
866 return id;
867}
868
895
908static inline gboolean _lens_data_available(const dt_iop_lensfun_data_t *d)
909{
910 return d->knots_have || (d->ls_have && d->crop > 0.f);
911}
912
913
914const char *name()
915{
916 return _("_lens correction");
917}
918
919const char *aliases()
920{
921 return _("vignette|chromatic aberrations|distortion");
922}
923
924const char **description(struct dt_iop_module_t *self)
925{
926 return dt_iop_set_description(self, _("correct lenses optical flaws"),
927 _("corrective"),
928 _("linear, RGB, scene-referred"),
929 _("geometric and reconstruction, RGB"),
930 _("linear, RGB, scene-referred"));
931}
932
933
935{
936 return IOP_GROUP_REPAIR;
937}
938
940{
941 return IOP_TAG_DISTORT;
942}
943
948
950{
951 return IOP_CS_RGB;
952}
953
954int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version,
955 void *new_params, const int new_version)
956{
957 if(old_version == 2 && new_version == 5)
958 {
959 // legacy params of version 2; version 1 comes from ancient times and seems to be forgotten by now
960 typedef struct
961 {
962 int modify_flags;
963 int inverse;
964 float scale;
965 float crop;
966 float focal;
967 float aperture;
968 float distance;
969 dt_lens_type_t target_geom;
970 char camera[52];
971 char lens[52];
972 int tca_override;
973 float tca_r, tca_b;
974 } dt_iop_lensfun_params_v2_t;
975
976 const dt_iop_lensfun_params_v2_t *o = (dt_iop_lensfun_params_v2_t *)old_params;
979
980 *n = *d; // start with a fresh copy of default parameters
981
982 n->modify_flags = o->modify_flags;
983 n->inverse = o->inverse;
984 n->scale = o->scale;
985 n->crop = o->crop;
986 n->focal = o->focal;
987 n->aperture = o->aperture;
988 n->distance = o->distance;
989 n->target_geom = o->target_geom;
990 n->tca_override = o->tca_override;
991 g_strlcpy(n->camera, o->camera, sizeof(n->camera));
992 g_strlcpy(n->lens, o->lens, sizeof(n->lens));
993 n->modified = 1;
994
995 // old versions had R and B swapped
996 n->tca_r = o->tca_b;
997 n->tca_b = o->tca_r;
998
999 return 0;
1000 }
1001 if(old_version == 3 && new_version == 5)
1002 {
1003 typedef struct
1004 {
1005 int modify_flags;
1006 int inverse;
1007 float scale;
1008 float crop;
1009 float focal;
1010 float aperture;
1011 float distance;
1012 dt_lens_type_t target_geom;
1013 char camera[128];
1014 char lens[128];
1015 int tca_override;
1016 float tca_r, tca_b;
1017 } dt_iop_lensfun_params_v3_t;
1018
1019 const dt_iop_lensfun_params_v3_t *o = (dt_iop_lensfun_params_v3_t *)old_params;
1022
1023 *n = *d; // start with a fresh copy of default parameters
1024
1025 /* The whole OLD struct, sized from the old struct. It used to be sized as the new one
1026 * minus an int, which is the same number only for as long as v5 has exactly one field
1027 * more than v3 -- add a second and this reads off the end of the caller's buffer. */
1028 memcpy(n, o, sizeof(*o));
1029
1030 // one more parameter and changed parameters in case we autodetect
1031 n->modified = 1;
1032
1033 // old versions had R and B swapped
1034 n->tca_r = o->tca_b;
1035 n->tca_b = o->tca_r;
1036
1037 return 0;
1038 }
1039
1040 if(old_version == 4 && new_version == 5)
1041 {
1042 typedef struct
1043 {
1044 int modify_flags;
1045 int inverse;
1046 float scale;
1047 float crop;
1048 float focal;
1049 float aperture;
1050 float distance;
1051 dt_lens_type_t target_geom;
1052 char camera[128];
1053 char lens[128];
1054 int tca_override;
1055 float tca_r, tca_b;
1056 int modified;
1057 } dt_iop_lensfun_params_v4_t;
1058
1059 const dt_iop_lensfun_params_v4_t *o = (dt_iop_lensfun_params_v4_t *)old_params;
1062
1063 *n = *d; // start with a fresh copy of default parameters
1064
1065 memcpy(n, o, sizeof(dt_iop_lensfun_params_t));
1066
1067 // old versions had R and B swapped
1068 n->tca_r = o->tca_b;
1069 n->tca_b = o->tca_r;
1070
1071 return 0;
1072 }
1073
1074 return 1;
1075}
1076
1077static char *_lens_sanitize(const char *orig_lens)
1078{
1079 const char *found_or = strstr(orig_lens, " or ");
1080 const char *found_parenthesis = strstr(orig_lens, " (");
1081
1082 if(found_or || found_parenthesis)
1083 {
1084 size_t pos_or = (size_t)(found_or - orig_lens);
1085 size_t pos_parenthesis = (size_t)(found_parenthesis - orig_lens);
1086 size_t pos = pos_or < pos_parenthesis ? pos_or : pos_parenthesis;
1087
1088 if(pos > 0)
1089 {
1090 char *new_lens = (char *)malloc(pos + 1);
1091
1092 strncpy(new_lens, orig_lens, pos);
1093 new_lens[pos] = '\0';
1094
1095 return new_lens;
1096 }
1097 else
1098 {
1099 char *new_lens = strdup(orig_lens);
1100 return new_lens;
1101 }
1102 }
1103 else
1104 {
1105 char *new_lens = strdup(orig_lens);
1106 return new_lens;
1107 }
1108}
1109
1125static int get_modifier(int *mods_done, int w, int h, const dt_iop_lensfun_data_t *d,
1126 int mods_filter, gboolean force_inverse, ls_modifier_t *mod,
1127 ls_modifier_t *vig_mod)
1128{
1129 memset(mod, 0, sizeof(*mod));
1130 if(mods_done) *mods_done = 0;
1131 /* A maker's own profile needs no database entry and no crop factor -- it describes THIS
1132 * body and lens, and it arrived in the file. Refusing it for want of a database lens is
1133 * how the embedded path would have been unreachable on exactly the bodies that carry
1134 * one and are not calibrated upstream. */
1135 if(!_lens_data_available(d)) return 0;
1136
1137 int mods_todo = d->modify_flags & mods_filter;
1138
1139 /* Drop any axis whose source this build cannot serve, so an unknown source from some
1140 * future Ansel visibly does nothing rather than silently correcting from the database.
1141 * Filtering here, at the one place that decides what to ask a resolver for, means the
1142 * rest of the module never re-derives it. */
1143 for(dt_lens_axis_t axis = 0; axis < DT_LENS_AXIS_LAST; axis++)
1144 {
1145 const dt_lens_source_t source = _lens_source_decode(d->modify_flags, d->tca_override, axis);
1146 /* The whole pack, so switching distortion to another source stops the projection change
1147 * and the scaling with it -- they are one correction, and leaving them running was what
1148 * kept geometry applying after its control had been hidden. */
1149 if(!_lens_source_is_library_served(source)) mods_todo &= ~_lens_axis_flags(axis);
1150 }
1151
1152 int want = 0;
1153 if(mods_todo & DT_LENS_MODIFY_DISTORTION) want |= LS_ENABLE_DISTORTION;
1154 if(mods_todo & DT_LENS_MODIFY_TCA) want |= LS_ENABLE_TCA;
1155 if(mods_todo & DT_LENS_MODIFY_VIGNETTING) want |= LS_ENABLE_VIGNETTING;
1156 if(mods_todo & DT_LENS_MODIFY_GEOMETRY) want |= LS_ENABLE_GEOMETRY;
1157 if(mods_todo & DT_LENS_MODIFY_SCALE) want |= LS_ENABLE_SCALE;
1158
1159 const int reverse = force_inverse ? !d->inverse : d->inverse;
1160
1161 /* Which resolver, decided by the distortion axis because that is the one that owns the
1162 * coordinate system: a maker's table indexes radius against the half diagonal, the
1163 * database against lensfun's short side, and a single modifier can only be in one of
1164 * them. Vignetting is not bound by that -- it is a gain, resolved separately below. */
1165 const dt_lens_source_t geom_source
1166 = _lens_source_decode(d->modify_flags, d->tca_override, DT_LENS_AXIS_DISTORTION);
1167 const gboolean geom_embedded = (geom_source == DT_LENS_SOURCE_EMBEDDED) && d->knots_have;
1168
1169 /* The one remaining way to end up on the database while the user asked for the file: the
1170 * table did not resolve. commit_params has already said why, once, so this stays quiet --
1171 * it runs per pipe per frame and would drown the reason it is reporting. */
1172
1173 int got;
1174 if(geom_embedded)
1175 {
1176 /* The maker measured the lens as it shipped, so there is no projection change to make
1177 * and the scale to use is the one their own profile asks for. */
1178 /* The maker's own autoscale AND the user's slider, composed. The profile ships the
1179 * factor that just clears the borders it leaves; the slider is what the user wants on
1180 * top of that, and passing only the first left the slider inert in this mode -- a
1181 * control that moves and changes nothing. */
1182 const float knots_scale = d->knots_scale * ((d->scale > 0.f) ? d->scale : 1.f);
1183
1184 /* Ask the table for chromatic aberration only if that axis actually chose it. The
1185 * maker's curve carries distortion and CA together, so asking for both when CA was set
1186 * to the database or to typed values would apply the maker's aberration on top of the
1187 * other one -- over-correcting the fringing by exactly the maker's amount, with the
1188 * panel showing a source the pixels never used. Asked for distortion alone, the table
1189 * hands back pure geometry and the chosen TCA model runs after it. */
1190 int knot_want = want & ~LS_ENABLE_GEOMETRY;
1191 if(_lens_source_decode(d->modify_flags, d->tca_override, DT_LENS_AXIS_TCA)
1193 knot_want &= ~LS_ENABLE_TCA;
1194
1195 got = ls_modifier_init_knots(mod, &d->ls_knots, w, h, knots_scale, knot_want, reverse);
1196
1197 /* TCA from somewhere else rides on the table's geometry. ls_modifier_init_knots() only
1198 * fills the knot tables, so the polynomial the database resolved -- or the linear one
1199 * _lens_build_data() synthesised from the user's two numbers -- has to be put in.
1200 *
1201 * ONLY THIS DIRECTION. The reverse -- a database distortion wearing the maker's
1202 * aberration -- is not implemented and is not an oversight:
1203 *
1204 * - physically it is two calibrations of the same lens applied to each other. The
1205 * maker measured their aberration as a departure from THEIR geometry, so it is only
1206 * the right departure when that geometry is the one in force;
1207 * - mechanically the table has no separate aberration to lift out. Distortion and CA
1208 * are one per-channel curve; green is the geometry and the other two are defined
1209 * relative to it, so "the CA alone" would be the ratio knot_c[c]/knot_c[1] resampled
1210 * onto the database's radius axis -- which is a different normalization again (half
1211 * short side against half diagonal), needing the radius conversion factor that
1212 * ls_modifier_set_projection() documents.
1213 *
1214 * If it is ever wanted: add LS_EVAL_TCA_KNOTS carrying those ratios plus that factor,
1215 * and expect to answer the physical objection first. _lens_rebuild_axis_row() withdraws
1216 * the row meanwhile, so the combination cannot be selected and then quietly ignored. */
1217 if((want & LS_ENABLE_TCA) && !(knot_want & LS_ENABLE_TCA) && d->ls_have)
1218 {
1219 ls_modifier_t tca_mod;
1220 if(ls_modifier_init(&tca_mod, &d->ls_lens, d->crop, w, h, d->focal, d->aperture,
1221 d->distance, 1.f, (int)d->target_geom, LS_ENABLE_TCA, reverse)
1222 & LS_ENABLE_TCA)
1223 {
1224 mod->tca = tca_mod.tca;
1225 mod->enabled |= LS_ENABLE_TCA;
1226 got |= LS_ENABLE_TCA;
1227 }
1228 }
1229
1230 /* The projection, put back on top of the maker's table.
1231 *
1232 * The table cannot supply it -- it describes the lens in the projection it shipped
1233 * with -- but the database entry matched alongside knows the lens's type, and that is
1234 * all the stage needs besides a focal. Without this, asking for a fisheye to be
1235 * rectilinear did nothing whenever distortion came from the file, which is the one
1236 * case where the two sources visibly disagreed about what the module could do.
1237 *
1238 * The crop here is the SHOOTING camera's, unlike the database path's: this modifier
1239 * normalizes radius against the half diagonal of the frame in hand, so that is the
1240 * sensor its projection focal has to be expressed against. */
1241 if((want & LS_ENABLE_GEOMETRY) && d->ls_have && d->crop > 0.f && d->focal > 0.f)
1242 {
1243 if(ls_modifier_set_projection(mod, (int)d->ls_lens.type, (int)d->target_geom,
1244 d->focal, d->crop))
1245 got |= LS_ENABLE_GEOMETRY;
1246 }
1247 }
1248 else
1249 got = ls_modifier_init(mod, &d->ls_lens, d->crop, w, h, d->focal, d->aperture,
1250 d->distance, d->scale, (int)d->target_geom, want, reverse);
1251
1252 int done = 0;
1253 if(got & LS_ENABLE_DISTORTION) done |= DT_LENS_MODIFY_DISTORTION;
1254 if(got & LS_ENABLE_TCA) done |= DT_LENS_MODIFY_TCA;
1255 if(got & LS_ENABLE_VIGNETTING) done |= DT_LENS_MODIFY_VIGNETTING;
1256 if(got & LS_ENABLE_GEOMETRY) done |= DT_LENS_MODIFY_GEOMETRY;
1257 if(got & LS_ENABLE_SCALE) done |= DT_LENS_MODIFY_SCALE;
1258
1259 /* A projection change LensSerious will not serve -- panoramic or equirectangular on
1260 * either side, which map x and y differently and are not radially expressible -- is
1261 * reported as not done rather than approximated. */
1262 if(mod->geometry_unsupported) done &= ~DT_LENS_MODIFY_GEOMETRY;
1263
1264 /* Vignetting, resolved on its own when it does not come from the same place as the
1265 * geometry. That combination is not exotic: an Olympus body embeds distortion and lateral
1266 * CA and no falloff at all, so "geometry from the file, vignetting from the database" is
1267 * the ordinary case for that maker rather than a corner of one.
1268 *
1269 * It works because the two halves of the correction never share a number. Vignetting
1270 * reads its own scale, its own centre and its own model, so a resolver that normalises
1271 * radius differently cannot disturb it -- which is exactly what
1272 * ls_eval_adopt_vignetting() relies on when the two are put back into one block. */
1273 if(vig_mod)
1274 {
1275 const dt_lens_source_t vig_source
1276 = _lens_source_decode(d->modify_flags, d->tca_override, DT_LENS_AXIS_VIGNETTING);
1277 const gboolean vig_embedded = (vig_source == DT_LENS_SOURCE_EMBEDDED) && d->knots_have;
1278 const gboolean vig_wanted = (mods_todo & DT_LENS_MODIFY_VIGNETTING) != 0;
1279
1280 if(!vig_wanted || vig_embedded == geom_embedded)
1281 {
1282 /* Same resolver, or nothing asked for: the main modifier's own state answers. When
1283 * the axis is off, `want` never carried vignetting, so this copy carries none either
1284 * -- resolving one here anyway was the bug: the GPU path grafts whatever this block
1285 * holds into its single eval block and the fused kernels apply what is enabled there,
1286 * so a falloff nobody asked for came back through the graft with the axis OFF. */
1287 *vig_mod = *mod;
1288 }
1289 else if(vig_embedded)
1290 ls_modifier_init_knots(vig_mod, &d->ls_knots, w, h, 1.f, LS_ENABLE_VIGNETTING, reverse);
1291 else
1292 ls_modifier_init(vig_mod, &d->ls_lens, d->crop, w, h, d->focal, d->aperture,
1293 d->distance, 1.f, (int)d->target_geom, LS_ENABLE_VIGNETTING, reverse);
1294
1295 /* The truth about vignetting now lives in vig_mod, whichever resolver produced it, so
1296 * the reported axis must be read from THERE. Reading it from the main modifier is how
1297 * the flagship hybrid silently lost its falloff: an Olympus table has no vignetting
1298 * knots, so the main (knots) resolver reported the axis not-done, the database
1299 * vignetting sat correctly resolved in vig_mod -- and every caller gates the falloff
1300 * on the done bit, so it was never applied. */
1301 if(vig_wanted && (vig_mod->enabled & LS_ENABLE_VIGNETTING))
1303 else
1304 done &= ~DT_LENS_MODIFY_VIGNETTING;
1305 }
1306
1307 if(mods_done) *mods_done = done;
1308 return done != 0;
1309}
1310
1311static inline void _lens_fill_vignette_row(float *const buf, const int width, const int ch)
1312{
1314 {
1315 const dt_aligned_pixel_simd_t half = dt_simd_set1(0.5f);
1316 for(int x = 0; x < width; x++) dt_store_simd_aligned(buf + (size_t)x * ch, half);
1317 }
1318 else
1319 {
1320 for(int k = 0; k < ch * width; k++) buf[k] = 0.5f;
1321 }
1322}
1323
1324/* Why do we care about being a monochrome image or not?
1325 The lensfun library does not have an algorithm for distortion or tca correction specialized for monochrome images,
1326 the builtin correction works with subtle differences for the color channels leading to some colorizing of the images.
1327 How is this fixed here:
1328 Monochrome images (from pure monochrome cameras or cameras with the color filter removed from the sensor) have
1329 all three rgb colors set to the same value by the demosaicer.
1330 Looking through lensfun code & docs the ApplySubpixelGeometryDistortion algorithm makes assumptions from given
1331 coeffs how far data are displaced for the different wavelengths of light.
1332 As green / Y channel is the most centric i took that as the canonical value instead of taking the mean.
1333*/
1334
1337 const void *const ivoid, void *const ovoid)
1338{
1339 const dt_iop_roi_t *const roi_in = &piece->roi_in;
1340 const dt_iop_roi_t *const roi_out = &piece->roi_out;
1341 const dt_iop_lensfun_data_t *const d = (dt_iop_lensfun_data_t *)piece->data;
1342
1343 const int ch = piece->dsc_in.channels;
1344 const int ch_width = ch * roi_in->width;
1345 const int mask_display = pipe->mask_display;
1346
1347
1349 {
1350 dt_iop_image_copy_by_size((float*)ovoid, (float*)ivoid, roi_out->width, roi_out->height, ch);
1351 return 0;
1352 }
1353
1354 const gboolean raw_monochrome = dt_image_is_monochrome(&self->dev->image_storage);
1355 const int used_lf_mask = _lens_mask_for_mono(raw_monochrome);
1356
1357 const float orig_w = roi_in->scale * piece->buf_in.width, orig_h = roi_in->scale * piece->buf_in.height;
1358
1359 int modflags;
1360 ls_modifier_t modifier;
1361 ls_modifier_t vig_modifier;
1362 get_modifier(&modflags, orig_w, orig_h, d, used_lf_mask, FALSE, &modifier, &vig_modifier);
1363 dt_print(DT_DEBUG_PIPE, "[lens] resolved 0x%x of 0x%x requested (%d dist, %d tca, %d vig"
1364 " calibrations, crop %.4f, focal %.1f)\n", modflags, d->modify_flags,
1365 d->ls_lens.n_dist, d->ls_lens.n_tca, d->ls_lens.n_vig, (double)d->crop,
1366 (double)d->focal);
1367
1368
1369 const struct dt_interpolation *const interpolation = dt_interpolation_new(DT_INTERPOLATION_USERPREF_WARP);
1370
1371 /* Vignetting is folded into the resampling loops below rather than run as a pass of its
1372 * own over a whole copy of the frame. ls_eval_vignette_factor() answers 1 when vignetting
1373 * is not enabled, so the loops need no second branch for it.
1374 *
1375 * Which FRAME the falloff lives in depends on the direction. Correcting, it belongs to
1376 * the source, so each channel takes the factor at ITS OWN source coordinate -- exactly
1377 * what the two-pass did, which darkened the input and then let each channel sample its
1378 * own position in it. Reversing, it is being put back onto the frame being produced, so
1379 * it is evaluated at the destination. */
1380 /* Flattened from the VIGNETTING modifier, not the geometry one. They are usually the same
1381 * object; they differ when the two axes take different sources, and this is where that
1382 * costs nothing -- the falloff already had a block of its own. */
1383 ls_eval_t vp;
1384 const gboolean have_vig = _lens_flags_have_axis(modflags, DT_LENS_AXIS_VIGNETTING)
1385 && ls_eval_from_modifier(&vig_modifier, &vp);
1386
1387 if(d->inverse)
1388 {
1389 // reverse direction (useful for renderings)
1390 if(_lens_flags_move_pixels(modflags))
1391 {
1392 // acquire temp memory for distorted pixel coords
1393 const size_t bufsize = (size_t)roi_out->width * 2 * 3;
1394
1395 size_t padded_bufsize;
1396 float *const buf = dt_pixelpipe_cache_alloc_perthread_float(bufsize, &padded_bufsize);
1397 if(IS_NULL_PTR(buf)) return 1;
1398
1399#ifdef _OPENMP
1400#pragma omp parallel for default(none) \
1401 firstprivate(roi_out, roi_in, padded_bufsize, modifier, ch, d, buf, ovoid, ivoid, ch_width, interpolation, raw_monochrome, mask_display, have_vig, vp)
1402#endif
1403 for(int y = 0; y < roi_out->height; y++)
1404 {
1405 float *bufptr = (float*)dt_get_perthread(buf, padded_bufsize);
1406 ls_modifier_apply_subpixel_geometry(&modifier, roi_out->x, roi_out->y + y, roi_out->width, 1, bufptr);
1407
1408 // reverse transform the global coords from lf to our buffer
1409 float *out = ((float *)ovoid) + (size_t)y * roi_out->width * ch;
1410 for(int x = 0; x < roi_out->width; x++, bufptr += 6, out += ch)
1411 {
1412 dt_aligned_pixel_simd_t pixel = { 0.f };
1413 for(int c = 0; c < 3; c++)
1414 {
1415 if(d->do_nan_checks && (!isfinite(bufptr[c * 2]) || !isfinite(bufptr[c * 2 + 1])))
1416 {
1417 pixel[c] = 0.0f;
1418 continue;
1419 }
1420
1421 const float *const inptr = (const float *const)ivoid + (size_t)c;
1422 const float pi0 = fmaxf(fminf(bufptr[c * 2] - roi_in->x, roi_in->width - 1.0f), 0.0f);
1423 const float pi1 = fmaxf(fminf(bufptr[c * 2 + 1] - roi_in->y, roi_in->height - 1.0f), 0.0f);
1424 pixel[c] = dt_interpolation_compute_sample(interpolation, inptr, pi0, pi1, roi_in->width,
1425 roi_in->height, ch, ch_width);
1426 }
1427
1428 if(have_vig)
1429 {
1430 /* Reversing: the falloff belongs to the frame being produced. */
1431 const float v = ls_eval_vignette_factor(&vp, (float)(roi_out->x + x),
1432 (float)(roi_out->y + y));
1433 for(int c = 0; c < 3; c++) pixel[c] *= v;
1434 }
1435 if(raw_monochrome) pixel[0] = pixel[2] = pixel[1];
1436
1437 if(mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK)
1438 {
1439 if(d->do_nan_checks && (!isfinite(bufptr[2]) || !isfinite(bufptr[3])))
1440 {
1441 pixel[3] = 0.0f;
1442 }
1443 else
1444 {
1445 // take green channel distortion also for alpha channel
1446 const float *const inptr = (const float *const)ivoid + (size_t)3;
1447 const float pi0 = fmaxf(fminf(bufptr[2] - roi_in->x, roi_in->width - 1.0f), 0.0f);
1448 const float pi1 = fmaxf(fminf(bufptr[3] - roi_in->y, roi_in->height - 1.0f), 0.0f);
1449 pixel[3] = dt_interpolation_compute_sample(interpolation, inptr, pi0, pi1, roi_in->width,
1450 roi_in->height, ch, ch_width);
1451 }
1452
1454 else for(int c = 0; c < ch; c++) out[c] = pixel[c];
1455 }
1456 else
1457 {
1458 for(int c = 0; c < 3; c++) out[c] = pixel[c];
1459 }
1460 }
1461 }
1463 }
1464 else
1465 {
1466 dt_iop_image_copy_by_size((float*)ovoid, (float*)ivoid, roi_out->width, roi_out->height, ch);
1467
1468 /* Nothing moved, so there was no resampling loop to fold the falloff into. */
1469 if(have_vig)
1470 {
1471 __OMP_PARALLEL_FOR__(firstprivate(modifier, ovoid, roi_out, ch))
1472 for(int y = 0; y < roi_out->height; y++)
1473 {
1474 float *out = ((float *)ovoid) + (size_t)y * roi_out->width * ch;
1475 ls_modifier_apply_vignetting(&modifier, roi_out->x, roi_out->y + y, roi_out->width, 1,
1476 out, (int)((ch * roi_out->width) * sizeof(float)));
1477 }
1478 }
1479 }
1480 }
1481 else // correct distortions:
1482 {
1483 /* No copy of the input, and no separate vignetting pass over it. This used to
1484 * duplicate the whole frame -- 387 MB for a 24 Mpx RGBA buffer -- darken the copy, and
1485 * resample from it. The falloff is a per-source-pixel gain, so folding it into the
1486 * resampling loop below gives the same answer while reading the caller's own buffer. */
1487
1488 if(_lens_flags_move_pixels(modflags))
1489 {
1490 // acquire temp memory for distorted pixel coords
1491 const size_t buf2size = (size_t)roi_out->width * 2 * 3;
1492 size_t padded_buf2size;
1493 float *const buf2 = dt_pixelpipe_cache_alloc_perthread_float(buf2size, &padded_buf2size);
1494 if(IS_NULL_PTR(buf2)) return 1;
1495
1496
1497#ifdef _OPENMP
1498#pragma omp parallel for default(none) \
1499 firstprivate(roi_out, roi_in, ovoid, ivoid, ch, padded_buf2size, modifier, mask_display, raw_monochrome, interpolation, ch_width, d, buf2, have_vig, vp)
1500#endif
1501 for(int y = 0; y < roi_out->height; y++)
1502 {
1503 float *buf2ptr = (float*)dt_get_perthread(buf2, padded_buf2size);
1504 ls_modifier_apply_subpixel_geometry(&modifier, roi_out->x, roi_out->y + y, roi_out->width, 1, buf2ptr);
1505 // reverse transform the global coords from lf to our buffer
1506 float *out = ((float *)ovoid) + (size_t)y * roi_out->width * ch;
1507 for(int x = 0; x < roi_out->width; x++, buf2ptr += 6, out += ch)
1508 {
1509 dt_aligned_pixel_simd_t pixel = { 0.f };
1510 for(int c = 0; c < 3; c++)
1511 {
1512 if(d->do_nan_checks && (!isfinite(buf2ptr[c * 2]) || !isfinite(buf2ptr[c * 2 + 1])))
1513 {
1514 pixel[c] = 0.0f;
1515 continue;
1516 }
1517
1518 const float *bufptr = ((const float *)ivoid) + c;
1519 const float pi0 = fmaxf(fminf(buf2ptr[c * 2] - roi_in->x, roi_in->width - 1.0f), 0.0f);
1520 const float pi1 = fmaxf(fminf(buf2ptr[c * 2 + 1] - roi_in->y, roi_in->height - 1.0f), 0.0f);
1521 pixel[c] = dt_interpolation_compute_sample(interpolation, bufptr, pi0, pi1, roi_in->width,
1522 roi_in->height, ch, ch_width);
1523 /* Correcting: the falloff belongs to the source, so each channel takes it at
1524 * its own source coordinate -- which is what sampling an already-darkened input
1525 * amounted to. */
1526 if(have_vig)
1527 pixel[c] *= ls_eval_vignette_factor(&vp, buf2ptr[c * 2], buf2ptr[c * 2 + 1]);
1528 }
1529 if(raw_monochrome) pixel[0] = pixel[2] = pixel[1];
1530 if(mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK)
1531 {
1532 if(d->do_nan_checks && (!isfinite(buf2ptr[2]) || !isfinite(buf2ptr[3])))
1533 {
1534 pixel[3] = 0.0f;
1535 }
1536 else
1537 {
1538 // take green channel distortion also for alpha channel
1539 const float *bufptr = ((const float *)ivoid) + 3;
1540 const float pi0 = fmaxf(fminf(buf2ptr[2] - roi_in->x, roi_in->width - 1.0f), 0.0f);
1541 const float pi1 = fmaxf(fminf(buf2ptr[3] - roi_in->y, roi_in->height - 1.0f), 0.0f);
1542 pixel[3] = dt_interpolation_compute_sample(interpolation, bufptr, pi0, pi1, roi_in->width,
1543 roi_in->height, ch, ch_width);
1544 }
1545
1547 else for(int c = 0; c < ch; c++) out[c] = pixel[c];
1548 }
1549 else
1550 {
1551 for(int c = 0; c < 3; c++) out[c] = pixel[c];
1552 }
1553 }
1554 }
1556 }
1557 else
1558 {
1559 dt_iop_image_copy_by_size((float *)ovoid, (float *)ivoid, roi_out->width, roi_out->height, ch);
1560
1561 /* Nothing moved, so there was no resampling loop to fold the falloff into. */
1562 if(have_vig)
1563 {
1564 __OMP_PARALLEL_FOR__(firstprivate(modifier, ovoid, roi_in, ch))
1565 for(int y = 0; y < roi_in->height; y++)
1566 {
1567 float *out = ((float *)ovoid) + (size_t)ch * roi_in->width * y;
1568 ls_modifier_apply_vignetting(&modifier, roi_in->x, roi_in->y + y, roi_in->width, 1, out,
1569 (int)((ch * roi_in->width) * sizeof(float)));
1570 }
1571 }
1572 }
1573 }
1574
1575 /* No GUI state is written here. Which corrections apply is a property of the
1576 * camera/lens/params combination, not of a rendered frame -- the label is computed on the
1577 * GUI thread by _lens_corrections_available(). */
1578 return 0;
1579}
1580
1581#ifdef HAVE_OPENCL
1582
1583
1584int process_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out)
1585{
1586 const dt_iop_roi_t *const roi_in = &piece->roi_in;
1587 const dt_iop_roi_t *const roi_out = &piece->roi_out;
1589
1590 const gboolean raw_monochrome = dt_image_is_monochrome(&self->dev->image_storage);
1591 const int used_lf_mask = _lens_mask_for_mono(raw_monochrome);
1592
1593 cl_int err = -999;
1594
1596 ls_modifier_t modifier;
1597 /* Declared before the first `goto error`: C++ forbids jumping over an initialisation. */
1598 ls_eval_t p;
1599 gboolean have_eval = FALSE, do_geom = FALSE, do_vig = FALSE;
1600
1601 const int devid = pipe->devid;
1602 const int iwidth = roi_in->width;
1603 const int iheight = roi_in->height;
1604 const int owidth = roi_out->width;
1605 const int oheight = roi_out->height;
1606 const int roi_in_x = roi_in->x;
1607 const int roi_in_y = roi_in->y;
1608 const int roi_out_x = roi_out->x;
1609 const int roi_out_y = roi_out->y;
1610
1611 const float orig_w = roi_in->scale * piece->buf_in.width, orig_h = roi_in->scale * piece->buf_in.height;
1612
1613 size_t origin[] = { 0, 0, 0 };
1614 size_t oregion[] = { (size_t)owidth, (size_t)oheight, 1 };
1615 size_t isizes[] = { (size_t)ROUNDUPDWD(iwidth, devid), (size_t)ROUNDUPDHT(iheight, devid), 1 };
1616 size_t osizes[] = { (size_t)ROUNDUPDWD(owidth, devid), (size_t)ROUNDUPDHT(oheight, devid), 1 };
1617
1618 int modflags;
1619 int ldkernel = -1;
1620 /* Declared here, ahead of every `goto error`: C++ will not let one jump over an
1621 * initialisation. Resolved once below, after get_modifier() has settled modflags. */
1623
1625 {
1626 err = dt_opencl_enqueue_copy_image(devid, dev_in, dev_out, origin, origin, oregion);
1627 if(err != CL_SUCCESS) goto error;
1628 return TRUE;
1629 }
1630
1631 switch(interpolation->id)
1632 {
1634 ldkernel = gd->kernel_lens_distort_bilinear;
1635 break;
1637 ldkernel = gd->kernel_lens_distort_bicubic;
1638 break;
1640 ldkernel = gd->kernel_lens_distort_mitchell;
1641 break;
1642 default:
1643 return FALSE;
1644 }
1645
1646
1647 ls_modifier_t vig_modifier;
1648 get_modifier(&modflags, orig_w, orig_h, d, used_lf_mask, FALSE, &modifier, &vig_modifier);
1649
1650 /* One kernel, in and out, in both directions.
1651 *
1652 * The correction crosses as an ls_eval_t -- 632 bytes of coefficients passed by value --
1653 * and each work-item evaluates its own source coordinates from it, so there is no
1654 * displacement map, no host buffer and no upload. Vignetting rides along inside the same
1655 * resampling pass rather than writing a whole intermediate image for the resampler to
1656 * read back.
1657 *
1658 * The direction lives in the block: ls_eval_map() reads p.reverse and composes the chain
1659 * accordingly, and _lens_devignette() places the falloff in the frame that direction puts
1660 * it in. So both directions are the same launch, which is why the branch that used to
1661 * distinguish them is gone. */
1662 have_eval = ls_eval_from_modifier(&modifier, &p) != 0;
1663
1664 /* One block serves both halves here, unlike the CPU path, so where the two axes took
1665 * different sources the falloff has to be grafted in from its own modifier. The two
1666 * halves of ls_eval_t share no field -- vignetting carries its own scale, centre and
1667 * model -- which is what makes the graft safe across resolvers that normalise radius
1668 * differently. A second block would be the obvious alternative and does not fit: 632
1669 * bytes each against the 1024 OpenCL 1.2 guarantees for a kernel's whole argument list. */
1670 if(have_eval)
1671 {
1672 ls_eval_t vp;
1673 if(ls_eval_from_modifier(&vig_modifier, &vp)) ls_eval_adopt_vignetting(&p, &vp);
1674 }
1675 do_geom = have_eval
1676 && _lens_flags_move_pixels(modflags);
1677 do_vig = have_eval && _lens_flags_have_axis(modflags, DT_LENS_AXIS_VIGNETTING) != 0;
1678
1679 if(do_geom)
1680 {
1681 /* Vignetting, if any, is applied inside this pass -- the kernel reads it out of p. */
1682 dt_opencl_set_kernel_arg(devid, ldkernel, 0, sizeof(cl_mem), (void *)&dev_in);
1683 dt_opencl_set_kernel_arg(devid, ldkernel, 1, sizeof(cl_mem), (void *)&dev_out);
1684 dt_opencl_set_kernel_arg(devid, ldkernel, 2, sizeof(int), (void *)&owidth);
1685 dt_opencl_set_kernel_arg(devid, ldkernel, 3, sizeof(int), (void *)&oheight);
1686 dt_opencl_set_kernel_arg(devid, ldkernel, 4, sizeof(int), (void *)&iwidth);
1687 dt_opencl_set_kernel_arg(devid, ldkernel, 5, sizeof(int), (void *)&iheight);
1688 dt_opencl_set_kernel_arg(devid, ldkernel, 6, sizeof(int), (void *)&roi_in_x);
1689 dt_opencl_set_kernel_arg(devid, ldkernel, 7, sizeof(int), (void *)&roi_in_y);
1690 dt_opencl_set_kernel_arg(devid, ldkernel, 8, sizeof(int), (void *)&roi_out_x);
1691 dt_opencl_set_kernel_arg(devid, ldkernel, 9, sizeof(int), (void *)&roi_out_y);
1692 dt_opencl_set_kernel_arg(devid, ldkernel, 10, sizeof(ls_eval_t), (void *)&p);
1693 dt_opencl_set_kernel_arg(devid, ldkernel, 11, sizeof(int), (void *)&(d->do_nan_checks));
1694 dt_opencl_set_kernel_arg(devid, ldkernel, 12, sizeof(int), (void *)&(raw_monochrome));
1695 err = dt_opencl_enqueue_kernel_2d(devid, ldkernel, osizes);
1696 if(err != CL_SUCCESS) goto error;
1697 }
1698 else if(do_vig)
1699 {
1700 /* Nothing moves, so there is nothing to resample: a dedicated pass costs one fetch per
1701 * pixel where the fused one would cost the resampler's full tap count for an identity
1702 * map. Which frame the falloff belongs to is the same question as above, and with no
1703 * geometry in play the two coincide. */
1704 const int vx = d->inverse ? roi_out_x : roi_in_x;
1705 const int vy = d->inverse ? roi_out_y : roi_in_y;
1706 const int vw = d->inverse ? owidth : iwidth;
1707 const int vh = d->inverse ? oheight : iheight;
1708 dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 0, sizeof(cl_mem), (void *)&dev_in);
1709 dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 1, sizeof(cl_mem), (void *)&dev_out);
1710 dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 2, sizeof(int), (void *)&vw);
1711 dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 3, sizeof(int), (void *)&vh);
1712 dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 4, sizeof(int), (void *)&vx);
1713 dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 5, sizeof(int), (void *)&vy);
1714 dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 6, sizeof(ls_eval_t), (void *)&p);
1716 d->inverse ? osizes : isizes);
1717 if(err != CL_SUCCESS) goto error;
1718 }
1719 else
1720 {
1721 err = dt_opencl_enqueue_copy_image(devid, dev_in, dev_out, origin, origin, oregion);
1722 if(err != CL_SUCCESS) goto error;
1723 }
1724
1725 return TRUE;
1726
1727error:
1728 dt_print(DT_DEBUG_OPENCL, "[opencl_lens] couldn't enqueue kernel! %d\n", err);
1729 return FALSE;
1730}
1731#endif
1732
1733void tiling_callback(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, const struct dt_dev_pixelpipe_iop_t *piece, struct dt_develop_tiling_t *tiling)
1734{
1735 /* CPU: in + out, and nothing else of image size.
1736 *
1737 * The whole-frame copy process() used to make -- to darken before resampling from it --
1738 * is gone with the separate vignetting pass: the falloff is folded into the resampling
1739 * loop, which now reads the caller's own input buffer. The displacement map is not a
1740 * whole-image temporary either: it is built a row at a time into a per-thread buffer of
1741 * width*6 floats, so it grows with the frame's WIDTH and the thread count rather than
1742 * its area -- ~2 MB for a 6000 px frame on 16 threads, against ~384 MB for one 24 Mpx
1743 * RGBA buffer. Counting it here would reserve memory nothing allocates.
1744 *
1745 * GPU: in + out, and nothing else at all -- one kernel reads the input and writes the
1746 * output, with vignetting folded into the same pass.
1747 *
1748 * Both figures used to be 4.5, meaning in + out + tmp + a six-float-per-pixel map buffer
1749 * (1.5x an RGBA one) that had to be built on the host and uploaded. The GPU path no
1750 * longer has that buffer -- each work-item evaluates its own coordinates from ~80 bytes
1751 * of coefficients passed as a kernel argument -- so reserving 1.5 image buffers for it
1752 * made the tile solver split frames that would have fitted whole.
1753 *
1754 * factor_cl and maxbuf_cl have to be set explicitly: dt_develop_tiling_t defaults them to
1755 * the CPU figures (develop/tiling.c), so a module that sets only `factor` silently
1756 * describes its GPU path with its CPU path's appetite. */
1757 tiling->factor = 2.0f; // in + out
1758 tiling->maxbuf = 1.0f;
1759 tiling->factor_cl = 2.0f; // in + out; no intermediate at all
1760 tiling->maxbuf_cl = 1.0f;
1761 tiling->overhead = 0;
1762 tiling->overlap = 4;
1763 tiling->xalign = 1;
1764 tiling->yalign = 1;
1765 return;
1766}
1767
1769 float *const __restrict points, size_t points_count)
1770{
1772 if(!_lens_data_available(d)) return 0;
1773
1774 const float orig_w = piece->buf_in.width, orig_h = piece->buf_in.height;
1775 int modflags;
1776
1777 const int used_lf_mask = _lens_mask_for_mono(dt_image_is_monochrome(&self->dev->image_storage));
1778
1779 ls_modifier_t modifier;
1780 get_modifier(&modflags, orig_w, orig_h, d, used_lf_mask, TRUE, &modifier, NULL);
1781
1782 if(_lens_flags_move_pixels(modflags))
1783 {
1784 __OMP_PARALLEL_FOR__(firstprivate(points, points_count, modifier) if(points_count > 100))
1785 for(size_t i = 0; i < points_count * 2; i += 2)
1786 {
1787 float DT_ALIGNED_ARRAY buf[6];
1788 ls_modifier_apply_subpixel_geometry(&modifier, points[i], points[i + 1], 1, 1, buf);
1789 // take green channel distortion, like distort_mask() does, so x and y come from the
1790 // same color channel's distortion field instead of mixing red's x with green's y.
1791 points[i] = buf[2];
1792 points[i + 1] = buf[3];
1793 }
1794 }
1795
1796
1797 return 1;
1798}
1799
1801 float *const __restrict points, size_t points_count)
1802{
1804
1805 if(!_lens_data_available(d)) return 0;
1806
1807 const int used_lf_mask = _lens_mask_for_mono(dt_image_is_monochrome(&self->dev->image_storage));
1808
1809 const float orig_w = piece->buf_in.width, orig_h = piece->buf_in.height;
1810 int modflags;
1811 ls_modifier_t modifier;
1812 get_modifier(&modflags, orig_w, orig_h, d, used_lf_mask, FALSE, &modifier, NULL);
1813
1814
1815 if(_lens_flags_move_pixels(modflags))
1816 {
1817 __OMP_PARALLEL_FOR__(firstprivate(points_count, modifier, points) if(points_count > 100))
1818 for(size_t i = 0; i < points_count * 2; i += 2)
1819 {
1820 float DT_ALIGNED_ARRAY buf[6];
1821 ls_modifier_apply_subpixel_geometry(&modifier, points[i], points[i + 1], 1, 1, buf);
1822 // take green channel distortion, like distort_mask() does, so x and y come from the
1823 // same color channel's distortion field instead of mixing red's x with green's y.
1824 points[i] = buf[2];
1825 points[i + 1] = buf[3];
1826 }
1827 }
1828
1829
1830 return 1;
1831}
1832
1833// TODO: Shall we keep DT_LENS_MODIFY_TCA in the modifiers?
1834void distort_mask(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, struct dt_dev_pixelpipe_iop_t *piece,
1835 const float *const in, float *const out, const dt_iop_roi_t *const roi_in,
1836 const dt_iop_roi_t *const roi_out)
1837{
1838 (void)pipe;
1839 const dt_iop_lensfun_data_t *const d = (dt_iop_lensfun_data_t *)piece->data;
1840
1842 {
1843 dt_iop_image_copy_by_size(out, in, roi_out->width, roi_out->height, 1);
1844 return;
1845 }
1846
1847 const float orig_w = roi_in->scale * piece->buf_in.width, orig_h = roi_in->scale * piece->buf_in.height;
1848 int modflags;
1849 ls_modifier_t modifier;
1850 get_modifier(&modflags, orig_w, orig_h, d,
1851 /*DT_LENS_MODIFY_TCA |*/ DT_LENS_MODIFY_DISTORTION | DT_LENS_MODIFY_GEOMETRY
1853 FALSE, &modifier, NULL);
1854
1855 if(!_lens_flags_move_pixels(modflags))
1856 {
1857 dt_iop_image_copy_by_size(out, in, roi_out->width, roi_out->height, 1);
1858 return;
1859 }
1860
1861 const struct dt_interpolation *const interpolation = dt_interpolation_new(DT_INTERPOLATION_USERPREF_WARP);
1862
1863 // acquire temp memory for distorted pixel coords
1864 const size_t bufsize = (size_t)roi_out->width * 2 * 3;
1865 size_t padded_bufsize;
1866 float *const buf = dt_pixelpipe_cache_alloc_perthread_float(bufsize, &padded_bufsize);
1867 if(IS_NULL_PTR(buf)) return;
1868 __OMP_PARALLEL_FOR__(firstprivate(buf, padded_bufsize, d, modifier, in, out, interpolation, roi_in, roi_out))
1869 for(int y = 0; y < roi_out->height; y++)
1870 {
1871 float *bufptr = (float*)dt_get_perthread(buf, padded_bufsize);
1872 ls_modifier_apply_subpixel_geometry(&modifier, roi_out->x, roi_out->y + y, roi_out->width, 1, bufptr);
1873
1874 // reverse transform the global coords from lf to our buffer
1875 float *_out = out + (size_t)y * roi_out->width;
1876 for(int x = 0; x < roi_out->width; x++, bufptr += 6, _out++)
1877 {
1878 if(d->do_nan_checks && (!isfinite(bufptr[2]) || !isfinite(bufptr[3])))
1879 {
1880 *_out = 0.0f;
1881 continue;
1882 }
1883
1884 // take green channel distortion also for alpha channel
1885 const float pi0 = bufptr[2] - roi_in->x;
1886 const float pi1 = bufptr[3] - roi_in->y;
1887 *_out = dt_interpolation_compute_sample(interpolation, in, pi0, pi1, roi_in->width, roi_in->height, 1,
1888 roi_in->width);
1889 }
1890 }
1891
1892
1894}
1895
1896void modify_roi_out(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe,
1897 struct dt_dev_pixelpipe_iop_t *piece, dt_iop_roi_t *roi_out,
1898 const dt_iop_roi_t *roi_in)
1899{
1900 *roi_out = *roi_in;
1901}
1902
1903void modify_roi_in(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe,
1904 struct dt_dev_pixelpipe_iop_t *piece,
1905 const dt_iop_roi_t *const roi_out, dt_iop_roi_t *roi_in)
1906{
1908 *roi_in = *roi_out;
1909 // inverse transform with given params
1910
1911 if(!_lens_data_available(d)) return;
1912
1913 const float orig_w = roi_in->scale * piece->buf_in.width;
1914 const float orig_h = roi_in->scale * piece->buf_in.height;
1915 int modflags;
1916 ls_modifier_t modifier;
1917 get_modifier(&modflags, orig_w, orig_h, d, DT_LENS_MODIFY_ALL_AXES, FALSE, &modifier, NULL);
1918
1919 if(_lens_flags_move_pixels(modflags))
1920 {
1921 const int xoff = roi_in->x;
1922 const int yoff = roi_in->y;
1923 const int width = roi_in->width;
1924 const int height = roi_in->height;
1925 const int awidth = abs(width);
1926 const int aheight = abs(height);
1927 const int xstep = (width < 0) ? -1 : 1;
1928 const int ystep = (height < 0) ? -1 : 1;
1929
1930 float xm = FLT_MAX, xM = -FLT_MAX, ym = FLT_MAX, yM = -FLT_MAX;
1931 const size_t nbpoints = 2 * awidth + 2 * aheight;
1932
1933 // ROI planning passes the active pipe now, but this temporary edge buffer only needs an
1934 // allocator bucket id, so use a stable generic bucket.
1935 float *const buf = (float *)dt_pixelpipe_cache_alloc_align_cache(sizeof(float) * nbpoints * 2 * 3,
1937 if(IS_NULL_PTR(buf)) return;
1938
1939#ifdef _OPENMP
1940#pragma omp parallel default(none) reduction(min : xm, ym) reduction(max : xM, yM) \
1941 firstprivate(modifier, xoff, yoff, awidth, aheight, width, height, nbpoints, ystep, xstep, buf)
1942#endif
1943 {
1944 __OMP_FOR__()
1945 for(int i = 0; i < awidth; i++)
1946 ls_modifier_apply_subpixel_geometry(&modifier, xoff + i * xstep, yoff, 1, 1, buf + 6 * i);
1947 __OMP_FOR__()
1948 for(int i = 0; i < awidth; i++)
1949 ls_modifier_apply_subpixel_geometry(&modifier, xoff + i * xstep, yoff + (height - 1), 1, 1, buf + 6 * (awidth + i));
1950 __OMP_FOR__()
1951 for(int j = 0; j < aheight; j++)
1952 ls_modifier_apply_subpixel_geometry(&modifier, xoff, yoff + j * ystep, 1, 1, buf + 6 * (2 * awidth + j));
1953 __OMP_FOR__()
1954 for(int j = 0; j < aheight; j++)
1955 ls_modifier_apply_subpixel_geometry(&modifier, xoff + (width - 1), yoff + j * ystep, 1, 1, buf + 6 * (2 * awidth + aheight + j));
1956
1957#ifdef _OPENMP
1958#pragma omp barrier
1959#endif
1960 __OMP_FOR__()
1961 for(size_t k = 0; k < nbpoints; k++)
1962 {
1963 // iterate over RGB channels x and y coordinates
1964 for(size_t c = 0; c < 6; c+=2)
1965 {
1966 const float x = buf[6 * k + c];
1967 const float y = buf[6 * k + c + 1];
1968 xm = isnan(x) ? xm : MIN(xm, x);
1969 xM = isnan(x) ? xM : MAX(xM, x);
1970 ym = isnan(y) ? ym : MIN(ym, y);
1971 yM = isnan(y) ? yM : MAX(yM, y);
1972 }
1973 }
1974 }
1975
1977
1978 // LensFun can return NAN coords, so we need to handle them carefully.
1979 if(!isfinite(xm) || !(0 <= xm && xm < orig_w)) xm = 0;
1980 if(!isfinite(xM) || !(1 <= xM && xM < orig_w)) xM = orig_w;
1981 if(!isfinite(ym) || !(0 <= ym && ym < orig_h)) ym = 0;
1982 if(!isfinite(yM) || !(1 <= yM && yM < orig_h)) yM = orig_h;
1983
1985 roi_in->x = fmaxf(0.0f, roundf(xm - interpolation->width));
1986 roi_in->y = fmaxf(0.0f, roundf(ym - interpolation->width));
1987 roi_in->width = roundf(fminf(orig_w - roi_in->x, xM - roi_in->x + interpolation->width));
1988 roi_in->height = roundf(fminf(orig_h - roi_in->y, yM - roi_in->y + interpolation->width));
1989
1990 // sanity check.
1991 roi_in->x = CLAMP(roi_in->x, 0, (int)floorf(orig_w));
1992 roi_in->y = CLAMP(roi_in->y, 0, (int)floorf(orig_h));
1993 roi_in->width = CLAMP(roi_in->width, 1, (int)ceilf(orig_w) - roi_in->x);
1994 roi_in->height = CLAMP(roi_in->height, 1, (int)ceilf(orig_h) - roi_in->y);
1995 }
1996}
1997
1998/* --- the shared geometry core ----------------------------------------------------------
1999 *
2000 * lens resolves its effective parameters and then builds a lensfun state out of them, and both
2001 * halves are needed twice: once for the pixel pipe, once for the record the geometry service
2002 * composes GUI coordinates from (develop/geometry/geometry.h). Expressed once here.
2003 *
2004 * Note what lens does NOT contribute: modify_roi_out() is the identity, so this module changes
2005 * no dimensions. It is on the geometry roster purely for its point transforms.
2006 */
2007
2029 const dt_iop_lensfun_params_t *const p)
2030{
2031 return (p->modified == 0) ? (const dt_iop_lensfun_params_t *)self->default_params : p;
2032}
2033
2043{
2044 (void)self;
2045 memset(&d->ls_lens, 0, sizeof(d->ls_lens));
2046 d->ls_have = FALSE;
2047
2048 /* No lock. The reader is lock-free by construction and its handle is thread-local, so a
2049 * pipeline thread resolving a lens no longer serialises against anything -- least of all
2050 * against RawSpeed decoding a file, which is what sharing dt_plugin_threadsafe_mutex()
2051 * used to mean. And nothing is owned on return: an ls_lens_t is a value, valid after the
2052 * handle that produced it is closed, so there is no deep copy to make and no delete to
2053 * forget. */
2054 long long mount_id = 0;
2055 float camera_crop = 0.f;
2056
2057 /* The image's own crop factor, which reload_defaults() takes from EXIF and the camera
2058 * picker overwrites. Seeded unconditionally because piece->data is REUSED across commits:
2059 * assigning it only inside the lookup below left a piece whose previous params resolved a
2060 * camera still carrying that camera's crop when the current ones do not resolve one. */
2061 d->crop = p->crop;
2062
2063 if(p->camera[0])
2064 {
2065 ls_camera_t camera;
2066 /* The stored camera name is a model with no maker -- what the picker writes and what
2067 * EXIF gives -- so the matcher is asked for one rather than guessing the other. */
2068 if(_ls_find_camera(NULL, p->camera, &camera))
2069 {
2070 d->crop = camera.crop_factor;
2071 camera_crop = camera.crop_factor;
2072 mount_id = camera.mount_id;
2073 }
2074 }
2075
2076 /* No camera, no database lens -- and that is a correctness rule, not caution.
2077 *
2078 * The mount is what makes a lens name mean one lens. Without it the search runs over the
2079 * whole catalogue, where hundreds of unrelated optics share the names "fixed lens" and
2080 * "festes objektiv", tie on score, and are separated by nothing at all. Correcting from
2081 * an arbitrary pick among those is how a Ricoh GR II came to be corrected with a 5.9 mm
2082 * compact's distortion.
2083 *
2084 * It also restores the panel's own invariant. gui_update() shows a lens only when a
2085 * camera resolved, so this was the one path where the pipeline corrected from something
2086 * the panel could not name and the user could not check or override. The recovery is the
2087 * picker: choose the body by hand, the mount is known, and the correct lens resolves and
2088 * is shown. */
2089 if(p->lens[0] && mount_id > 0)
2090 {
2091 const long long lens_id = _ls_find_lens(mount_id, camera_crop, p->focal, p->lens);
2092 ls_db_t *db = _ls_db();
2093 if(lens_id >= 0 && !IS_NULL_PTR(db) && ls_db_lens_by_id(db, lens_id, &d->ls_lens) == 1)
2094 {
2095 d->ls_have = TRUE;
2096 }
2097 }
2098 else if(p->lens[0])
2099 {
2100 /* DT_DEBUG_PIPE, not ALWAYS: nobody chose this, so it is a default declining to guess
2101 * rather than a request that could not be honoured -- and this runs from the GUI's
2102 * availability query too, not only at commit. The panel already says it, by showing
2103 * neither a camera nor a lens. */
2105 "[lens] `%s' is not a camera this database knows, so `%s' cannot be resolved to"
2106 " one lens; correcting from the database is declined\n", p->camera, p->lens);
2107 }
2108
2109 /* Typed coefficients are a SOURCE of their own, not an edit applied to a database row.
2110 *
2111 * They used to be written inside the lookup above, over the calibration of a lens the
2112 * database had just returned -- so on a body-and-lens pair the database does not know,
2113 * d->ls_have stayed FALSE, _lens_data_available() answered no, and process(),
2114 * process_cl(), the three distort_*() callbacks and modify_roi_in() all copied their
2115 * input and returned. The two sliders moved and the pipeline never saw them, on exactly
2116 * the images manual correction exists for.
2117 *
2118 * With a database lens the manual entry still REPLACES its aberration, which is what
2119 * manual means, and the coefficients live in that lens's calibration frame. With none,
2120 * the frame is a lens declared calibrated on THIS camera, so ls_modifier_init()'s
2121 * calibration-crop / image-crop rescaling is 1 and the two numbers act directly.
2122 * aspect_ratio is deliberately left at 0, which the library reads as its own 1.5 default
2123 * -- the same normalisation a 3:2-calibrated database entry gives, so a coefficient does
2124 * not change meaning on the day a profile for the lens appears.
2125 *
2126 * Writing it here rather than there also means it no longer depends on p->lens[0] being
2127 * non-empty: an unnamed lens is not a reason to refuse numbers the user typed. */
2129 {
2130 if(!d->ls_have)
2131 {
2132 d->ls_lens.type = LS_LENS_RECTILINEAR;
2133 d->ls_lens.crop_factor = d->crop;
2134 d->ls_lens.min_focal = p->focal;
2135 d->ls_lens.max_focal = p->focal;
2136 d->ls_have = TRUE;
2137 }
2138
2139 /* One entry at the shooting focal is exactly what the two sliders describe. ls_lens_t
2140 * is this module's own copy, so overwriting the array is both cheaper and clearer than
2141 * upstream's remove-every-entry-then-add dance on a shared object -- which is what the
2142 * code here used to do, twice, under two different lensfun APIs. */
2143 d->ls_lens.n_tca = 1;
2144 d->ls_lens.tca[0].model = LS_TCA_LINEAR;
2145 d->ls_lens.tca[0].focal = p->focal;
2146 d->ls_lens.tca[0].terms[0] = p->tca_r;
2147 d->ls_lens.tca[0].terms[1] = p->tca_b;
2148 for(int i = 2; i < 6; i++) d->ls_lens.tca[0].terms[i] = 0.f;
2149 }
2150
2151 d->modify_flags = p->modify_flags;
2152 if(dt_image_is_monochrome(&self->dev->image_storage)) d->modify_flags &= ~DT_LENS_MODIFY_TCA;
2153 d->inverse = p->inverse;
2154 d->scale = p->scale;
2155 d->focal = p->focal;
2156 d->aperture = p->aperture;
2157 d->distance = p->distance;
2158 d->target_geom = p->target_geom;
2159 d->do_nan_checks = TRUE;
2160 d->tca_override = p->tca_override;
2161
2162 /* The maker's own profile, if any axis asked for it and the file carries one.
2163 *
2164 * Resolved HERE, at commit, for the same reason the database lens is: the pixel path gets
2165 * values and does no lookups. ls_vendor_resolve() normalises whichever vendor format the
2166 * file holds -- Sony, Fuji, Olympus or a DNG opcode list -- straight into the knot table
2167 * the evaluators consume; past that call nothing here knows which maker wrote it.
2168 *
2169 * The finetune is NULL: "as the maker measured", which is the library's documented
2170 * meaning for it. The per-class strength blends the vendor GUIs offer are not exposed by
2171 * this module. */
2172 d->knots_have = FALSE;
2173 d->knots_scale = 1.f;
2174 {
2175 gboolean wants_embedded = FALSE;
2176 for(dt_lens_axis_t axis = 0; axis < DT_LENS_AXIS_LAST; axis++)
2177 if(_lens_source_is(p, axis, DT_LENS_SOURCE_EMBEDDED)) wants_embedded = TRUE;
2178
2179 if(wants_embedded)
2180 {
2181 const dt_image_t *const img = &self->dev->image_storage;
2182 float scale = 1.f;
2183 const int got = ls_vendor_resolve(&img->exif_correction, NULL,
2184 img->p_width, img->p_height, &d->ls_knots, &scale);
2185
2186 /* The two failure reports stay distinct, because they mean different things to the
2187 * person reading them: "this camera embeds nothing" is a fact about the hardware,
2188 * "we failed to read what it embedded" is a bug to report. Both then correct from
2189 * the database, and both say so -- a user who picked a source and got another one
2190 * is entitled to know. Once, at commit; not per pipe per frame. */
2191 if(got == 0)
2193 "[lens] an axis asks for the vendor profile but this file carries none;"
2194 " correcting from the community profile instead\n");
2195 else if(got < 0)
2197 "[lens] this file's vendor profile could not be decoded;"
2198 " correcting from the community profile instead\n");
2199 else
2200 {
2201 d->knots_scale = (scale > 0.f) ? scale : 1.f;
2202 d->knots_have = TRUE;
2203 }
2204 }
2205 }
2206
2207 /*
2208 * there are certain situations when LensFun can return NAN coordinated.
2209 * most common case would be when the FOV is increased.
2210 */
2211 if(d->target_geom == DT_LENS_RECTILINEAR)
2212 {
2213 d->do_nan_checks = FALSE;
2214 }
2215 else if((int)d->target_geom == (int)d->ls_lens.type)
2216 {
2217 d->do_nan_checks = FALSE;
2218 }
2219}
2220
2226
2229{
2231
2232 // FIXME: this is utter shit and should be made into a GUI "mode".
2233 // If p->modified == 0, mode = auto and hide all controls
2234 // if p->modidified == 1, mode = manual and show all controls.
2235 if(((dt_iop_lensfun_params_t *)p1)->modified == 0)
2236 {
2237 // Temporary fix pending GUI unfucking
2238 dt_iop_compute_module_hash(self, self->dev->forms);
2239 }
2240
2241 _lens_build_data(self, p, (dt_iop_lensfun_data_t *)piece->data);
2242
2243 piece->cache_output_on_ram = TRUE;
2244}
2245
2246/* --- the geometry service's view of this module (develop/geometry/geometry.h) ---------
2247 *
2248 * The one record in the service whose payload is not plain data: evaluating a lens correction
2249 * needs the resolved calibration, so the record owns a
2250 * deep copy and frees it. That is what dt_geometry_record_t::free_data exists for.
2251 */
2252
2258
2259static void _lens_free_data(void *ptr)
2260{
2262 if(!g) return;
2263 free(g);
2264}
2265
2268static int _lens_geometry_apply(const void *data, const dt_geometry_record_t *const record,
2269 float *points, size_t points_count, gboolean inverse)
2270{
2271 const dt_iop_lens_geometry_t *const g = (const dt_iop_lens_geometry_t *)data;
2272 const dt_iop_lensfun_data_t *const d = &g->data;
2273
2274 if(!_lens_data_available(d)) return 0;
2275 if(record->in.width <= 0 || record->in.height <= 0) return 0;
2276
2277 int modflags = 0;
2278 ls_modifier_t modifier;
2279 if(!get_modifier(&modflags, record->in.width, record->in.height, d, g->used_lf_mask, inverse,
2280 &modifier, NULL))
2281 return 0;
2282
2283 if(_lens_flags_move_pixels(modflags))
2284 {
2285 for(size_t i = 0; i < points_count * 2; i += 2)
2286 {
2287 float DT_ALIGNED_ARRAY buf[6];
2288 ls_modifier_apply_subpixel_geometry(&modifier, points[i], points[i + 1], 1, 1, buf);
2289 // green channel, like distort_transform() and distort_mask() do, so x and y come from the
2290 // same colour channel's distortion field instead of mixing red's x with green's y.
2291 points[i] = buf[2];
2292 points[i + 1] = buf[3];
2293 }
2294 }
2295
2296 return 1;
2297}
2298
2299static int _lens_geometry_transform(const void *data, const dt_geometry_record_t *const record,
2300 dt_geometry_chain_t *chain, float *points, size_t points_count)
2301{
2302 return _lens_geometry_apply(data, record, points, points_count, TRUE);
2303}
2304
2305static int _lens_geometry_backtransform(const void *data, const dt_geometry_record_t *const record,
2306 dt_geometry_chain_t *chain, float *points, size_t points_count)
2307{
2308 return _lens_geometry_apply(data, record, points, points_count, FALSE);
2309}
2310
2312 /* .map_size = */ NULL, // modify_roi_out() is the identity: lens changes no dimensions
2313 /* .transform = */ _lens_geometry_transform,
2314 /* .backtransform = */ _lens_geometry_backtransform,
2315};
2316
2317gboolean geometry_record(dt_iop_module_t *self, const void *params, dt_geometry_record_t *record)
2318{
2320 if(!g) return FALSE;
2321
2323 = _lens_effective_params(self, (const dt_iop_lensfun_params_t *)params);
2324 _lens_build_data(self, p, &g->data);
2325 g->used_lf_mask = _lens_used_mask(self);
2326
2327 record->data = g;
2328 record->free_data = _lens_free_data;
2329 record->vtable = &_lens_geometry_vtable;
2330 return TRUE;
2331}
2332
2334{
2335 piece->data = dt_calloc_align(sizeof(dt_iop_lensfun_data_t));
2336 piece->data_size = sizeof(dt_iop_lensfun_data_t);
2337}
2338
2340{
2341 /* init_pipe() may have failed to allocate, and cleanup runs regardless. */
2342 if(IS_NULL_PTR(piece->data)) return;
2343
2344 /* Nothing to free but the piece itself: ls_lens_t is a value living inside it, where the
2345 * lfLens it replaces was a heap object this had to remember to delete. */
2346 dt_free_align(piece->data);
2347 piece->data = NULL;
2348}
2349
2351{
2352 const int program = 2; // basic.cl, from programs.conf
2355 module->data = gd;
2356 gd->kernel_lens_distort_bilinear = dt_opencl_create_kernel(program, "lens_distort_bilinear");
2357 gd->kernel_lens_distort_bicubic = dt_opencl_create_kernel(program, "lens_distort_bicubic");
2358 gd->kernel_lens_distort_mitchell = dt_opencl_create_kernel(program, "lens_distort_mitchell");
2359 gd->kernel_lens_vignette = dt_opencl_create_kernel(program, "lens_vignette");
2360
2361 /* Nothing to pre-warm any more. Opening the calibration database is one mmap of an
2362 * already-parsed file, done lazily per thread on first use and measured at 0.18 ms --
2363 * there is no 100 ms XML parse left to hide behind a startup thread. */
2364}
2365
2367
2369{
2370 char *new_lens;
2371 const dt_image_t *img = &module->dev->image_storage;
2372
2373 // reload image specific stuff
2374 // get all we can from exif:
2376
2377 new_lens = _lens_sanitize(img->exif_lens);
2378 g_strlcpy(d->lens, new_lens, sizeof(d->lens));
2379 dt_free(new_lens);
2380 g_strlcpy(d->camera, img->exif_model, sizeof(d->camera));
2381 d->crop = img->exif_crop;
2382 d->aperture = img->exif_aperture;
2383 d->focal = img->exif_focal_length;
2384 d->scale = 1.0;
2387
2388 /* Everything decided here is now WRITTEN DOWN, rather than recomputed whenever the image
2389 * is opened. See _lens_effective_params() for what the alternative cost. */
2390 d->modified = 1;
2391
2392 /* Prefer what the camera measured about its own lens, per axis, whenever the file carries
2393 * it. The maker had the actual body and the actual lens on a bench; the database has a
2394 * community measurement of that model. Where both exist the maker's is the better default,
2395 * and where only one exists this picks the one that works.
2396 *
2397 * Per axis, because the makers do not all write the same set: an Olympus body publishes
2398 * distortion and lateral CA and no falloff, so that axis correctly keeps the database.
2399 *
2400 * Scale stays 1: the embedded resolver applies the profile's own autoscale, which already
2401 * clears the borders it leaves. Anything else here would be a second, arbitrary zoom on
2402 * top of a factor the maker chose. */
2403 for(dt_lens_axis_t axis = 0; axis < DT_LENS_AXIS_LAST; axis++)
2404 {
2406 if(_lens_image_embeds(module, axis))
2408 }
2409 // if we did not find focus_distance in EXIF, lets default to 1000
2410 d->distance = img->exif_focus_distance == 0.0f ? 1000.0f : img->exif_focus_distance;
2411 d->target_geom = DT_LENS_RECTILINEAR;
2412
2413 if(dt_image_is_monochrome(img))
2414 d->modify_flags &= ~DT_LENS_MODIFY_TCA;
2415
2416 // init crop from db:
2417 char model[100]; // truncate often complex descriptions.
2418 g_strlcpy(model, img->exif_model, sizeof(model));
2419 for(char cnt = 0, *c = model; c < model + 100 && *c != '\0'; c++)
2420 if(*c == ' ')
2421 if(++cnt == 2) *c = '\0';
2422 if(img->exif_maker[0] || model[0])
2423 {
2424 ls_camera_t cam;
2425 if(!_ls_find_camera(img->exif_maker, img->exif_model, &cam)) return;
2426
2427 /* Upstream spells a real fact into the mount NAME: a lower-case initial means a
2428 * fixed-lens camera. That is how a compact is told from an interchangeable-lens body,
2429 * and it decides both branches below. */
2430 char mount[128] = { 0 };
2431 ls_db_t *db = _ls_db();
2432 if(IS_NULL_PTR(db)) return;
2433 ls_db_mount_name(db, cam.mount_id, mount, sizeof(mount));
2434 const gboolean fixed_lens = (mount[0] != '\0') && islower((unsigned char)mount[0]);
2435
2436 long long lens_id = _ls_find_lens(cam.mount_id, cam.crop_factor, d->focal, d->lens);
2437
2438 if(lens_id < 0 && fixed_lens)
2439 {
2440 /* A fixed-lens camera whose EXIF lens string matched nothing -- it is "(65535)", or
2441 * a name upstream files as "fixed lens". The lens is whatever is built into this
2442 * mount, so ask the mount directly instead of matching a name. */
2443 g_strlcpy(d->lens, "", sizeof(d->lens));
2444
2445 const int n = ls_db_lenses_for_mount(db, cam.mount_id, NULL, 0);
2446 if(n > 0)
2447 {
2448 long long *ids = (long long *)dt_alloc_align(sizeof(long long) * (size_t)n);
2449 if(!IS_NULL_PTR(ids))
2450 {
2451 ls_db_lenses_for_mount(db, cam.mount_id, ids, n);
2452 /* The shortest model name, as before: a fixed-lens mount can carry several
2453 * entries for one physical lens and the shortest is the plain one. */
2454 size_t shortest = SIZE_MAX;
2455 for(int i = 0; i < n; i++)
2456 {
2457 char maker[128] = "", lmodel[256] = "";
2458 if(ls_db_lens_name(db, ids[i], maker, sizeof(maker), lmodel, sizeof(lmodel)) <= 0)
2459 continue;
2460 const size_t len = strlen(lmodel);
2461 if(len < shortest)
2462 {
2463 shortest = len;
2464 lens_id = ids[i];
2465 g_strlcpy(d->lens, lmodel, sizeof(d->lens));
2466 }
2467 }
2468 dt_free_align(ids);
2469 }
2470 }
2471 }
2472
2473 if(lens_id >= 0)
2474 {
2475 ls_lens_t lens;
2476 if(ls_db_lens_by_id(db, lens_id, &lens) == 1)
2477 d->target_geom = (dt_lens_type_t)lens.type;
2478 }
2479
2480 d->crop = cam.crop_factor;
2481 d->scale = get_autoscale(module, d);
2482 module->workflow_enabled = dt_image_needs_rawprepare(img);
2483 }
2484
2485 // reload_defaults() stays params-only and never touches gui_data.
2486}
2487
2489{
2491
2492 /* Before anything is freed: the pre-warm thread may still be building the database. */
2493 /* No database to tear down and no thread to join. Each thread's handle closes itself
2494 * when that thread ends, and the one-entry caches beside it die with it. */
2495
2500 dt_free(module->data);
2501}
2502
2505
2506/* simple function to compute the floating-point precision
2507 which is enough for "normal use". The criteria is to have
2508 about 3 leading digits after the initial zeros. */
2509static int precision(double x, double adj)
2510{
2511 x *= adj;
2512
2513 if(x == 0) return 1;
2514 if(x < 1.0)
2515 if(x < 0.1)
2516 if(x < 0.01)
2517 return 5;
2518 else
2519 return 4;
2520 else
2521 return 3;
2522 else if(x < 100.0)
2523 if(x < 10.0)
2524 return 2;
2525 else
2526 return 1;
2527 else
2528 return 0;
2529}
2530
2531/* -- ufraw ptr array functions -- */
2532
2533static int ptr_array_insert_sorted(GPtrArray *array, const void *item, GCompareFunc compare)
2534{
2535 int length = array->len;
2536 g_ptr_array_set_size(array, length + 1);
2537 const void **root = (const void **)array->pdata;
2538
2539 int m = 0, l = 0, r = length - 1;
2540
2541 // Skip trailing NULL, if any
2542 if(l <= r && !root[r]) r--;
2543
2544 while(l <= r)
2545 {
2546 m = (l + r) / 2;
2547 int cmp = compare(root[m], item);
2548
2549 if(cmp == 0)
2550 {
2551 ++m;
2552 goto done;
2553 }
2554 else if(cmp < 0)
2555 l = m + 1;
2556 else
2557 r = m - 1;
2558 }
2559 if(r == m) m++;
2560
2561done:
2562 memmove(root + m + 1, root + m, sizeof(void *) * (length - m));
2563 root[m] = item;
2564 return m;
2565}
2566
2567static int ptr_array_find_sorted(const GPtrArray *array, const void *item, GCompareFunc compare)
2568{
2569 int length = array->len;
2570 void **root = array->pdata;
2571
2572 int l = 0, r = length - 1;
2573 int m = 0, cmp = 0;
2574
2575 if(!length) return -1;
2576
2577 // Skip trailing NULL, if any
2578 if(!root[r]) r--;
2579
2580 while(l <= r)
2581 {
2582 m = (l + r) / 2;
2583 cmp = compare(root[m], item);
2584
2585 if(cmp == 0)
2586 return m;
2587 else if(cmp < 0)
2588 l = m + 1;
2589 else
2590 r = m - 1;
2591 }
2592
2593 return -1;
2594}
2595
2596static void ptr_array_insert_index(GPtrArray *array, const void *item, int index)
2597{
2598 const void **root;
2599 int length = array->len;
2600 g_ptr_array_set_size(array, length + 1);
2601 root = (const void **)array->pdata;
2602 memmove(root + index + 1, root + index, sizeof(void *) * (length - index));
2603 root[index] = item;
2604}
2605
2606/* -- end ufraw ptr array functions -- */
2607
2608/* -- camera -- */
2609
2620static void _lens_params_set_camera(dt_iop_module_t *self, const long long camera_id)
2621{
2623
2624 ls_db_t *db = _ls_db();
2625 char maker[128] = "", model[256] = "", variant[128] = "";
2626 ls_camera_t cam;
2627 if(camera_id < 0 || IS_NULL_PTR(db)
2628 || ls_db_camera_name(db, camera_id, maker, sizeof(maker), model, sizeof(model),
2629 variant, sizeof(variant)) != 1
2630 || ls_db_camera_by_id(db, camera_id, &cam) != 1)
2631 return;
2632
2633 g_strlcpy(p->camera, model, sizeof(p->camera));
2634 p->crop = cam.crop_factor;
2635}
2636
2645static void camera_set(dt_iop_module_t *self, long long camera_id)
2646{
2648
2649 ls_db_t *db = _ls_db();
2650 char maker[128] = "", model[256] = "", variant[128] = "";
2651 ls_camera_t cam;
2652 if(camera_id < 0 || IS_NULL_PTR(db)
2653 || ls_db_camera_name(db, camera_id, maker, sizeof(maker), model, sizeof(model),
2654 variant, sizeof(variant)) != 1
2655 || ls_db_camera_by_id(db, camera_id, &cam) != 1)
2656 {
2657 gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(g->camera_model))), "");
2658 gtk_widget_set_tooltip_text(GTK_WIDGET(g->camera_model), "");
2659 g->camera_id = -1;
2660 return;
2661 }
2662
2663 g->camera_id = camera_id;
2664
2665 gchar *fm = maker[0] ? g_strdup_printf("%s, %s", maker, model) : g_strdup(model);
2666 gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(g->camera_model))), fm);
2667 dt_free(fm);
2668
2669 // sizeof(variant) + 4, not 128: the format adds " (" and ")" around a string that can
2670 // itself fill variant[], so an equal-sized buffer drops the closing parenthesis on a long
2671 // camera variant. Derived from variant's size so it stays right if that changes.
2672 char _variant[sizeof(variant) + 4];
2673 if(variant[0])
2674 snprintf(_variant, sizeof(_variant), " (%s)", variant);
2675 else
2676 _variant[0] = 0;
2677
2678 char mount[128] = "";
2679 ls_db_mount_name(db, cam.mount_id, mount, sizeof(mount));
2680
2681 fm = g_strdup_printf(_("maker:\t\t%s\n"
2682 "model:\t\t%s%s\n"
2683 "mount:\t\t%s\n"
2684 "crop factor:\t%.1f"),
2685 maker, model, _variant, mount, cam.crop_factor);
2686 gtk_widget_set_tooltip_text(GTK_WIDGET(g->camera_model), fm);
2687 dt_free(fm);
2688}
2689
2690static void camera_menu_select(GtkMenuItem *menuitem, gpointer user_data)
2691{
2692 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
2693
2694 /* First, and for the whole handler: a suppressed callback is a programmatic widget
2695 * update, and this one writes parameters and commits history. */
2696 if(dt_gui_widgets_suppressed()) return;
2697
2698 const long long camera_id =
2699 (long long)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menuitem), "lens-camera-id"));
2700
2702 _lens_params_set_camera(self, camera_id);
2703 p->modified = 1;
2704
2705 /* View second, because it reads the parameters just written. The camera decides the mount
2706 * and the crop factor, and a lens is matched against those -- so a lens that matched the
2707 * old camera may not match the new one, and the other way round. Every other caller of
2708 * camera_set() runs a lens_set() straight afterwards and gets the rebuild from there;
2709 * this one does not, so it asks itself. */
2710 {
2712 camera_set(self, camera_id);
2715 }
2716
2717 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
2718}
2719
2725static void camera_menu_fill(dt_iop_module_t *self, const long long *ids, int n)
2726{
2728 GPtrArray *makers, *submenus;
2729
2730 if(g->camera_menu)
2731 {
2732 gtk_widget_destroy(GTK_WIDGET(g->camera_menu));
2733 g->camera_menu = NULL;
2734 }
2735
2736 ls_db_t *db = _ls_db();
2737 if(IS_NULL_PTR(db)) return;
2738
2739 /* Count all existing camera makers and create a sorted list */
2740 makers = g_ptr_array_new_with_free_func(dt_free_gpointer);
2741 submenus = g_ptr_array_new();
2742 for(int i = 0; i < n; i++)
2743 {
2744 char maker[128] = "", model[256] = "", variant[128] = "";
2745 if(ls_db_camera_name(db, ids[i], maker, sizeof(maker), model, sizeof(model),
2746 variant, sizeof(variant)) != 1)
2747 continue;
2748
2749 GtkWidget *submenu, *item;
2750 int idx = ptr_array_find_sorted(makers, maker, (GCompareFunc)g_utf8_collate);
2751 if(idx < 0)
2752 {
2753 /* No such maker yet, insert it into the array. The strings are OWNED now: they used
2754 * to point into a database that outlived the menu, and they no longer do. */
2755 idx = ptr_array_insert_sorted(makers, g_strdup(maker), (GCompareFunc)g_utf8_collate);
2756 /* Create a submenu for cameras by this maker */
2757 submenu = gtk_menu_new();
2758 ptr_array_insert_index(submenus, submenu, idx);
2759 }
2760
2761 submenu = (GtkWidget *)g_ptr_array_index(submenus, idx);
2762 /* Append current camera name to the submenu */
2763 if(!variant[0])
2764 item = gtk_menu_item_new_with_label(model);
2765 else
2766 {
2767 gchar *fm = g_strdup_printf("%s (%s)", model, variant);
2768 item = gtk_menu_item_new_with_label(fm);
2769 dt_free(fm);
2770 }
2771 gtk_widget_show(item);
2772 g_object_set_data(G_OBJECT(item), "lens-camera-id", GINT_TO_POINTER((gint)ids[i]));
2773 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(camera_menu_select), self);
2774 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), item);
2775 }
2776
2777 g->camera_menu = GTK_MENU(gtk_menu_new());
2778 for(unsigned i = 0; i < makers->len; i++)
2779 {
2780 GtkWidget *item = (GtkWidget *)gtk_menu_item_new_with_label((const gchar *)g_ptr_array_index(makers, i));
2781 gtk_widget_show(item);
2782 gtk_menu_shell_append(GTK_MENU_SHELL(g->camera_menu), item);
2783 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), (GtkWidget *)g_ptr_array_index(submenus, i));
2784 }
2785
2786 g_ptr_array_free(submenus, TRUE);
2787 g_ptr_array_free(makers, TRUE);
2788}
2789
2791static long long *_camera_all_ids(int *out_n)
2792{
2793 *out_n = 0;
2794 ls_db_t *db = _ls_db();
2795 if(IS_NULL_PTR(db)) return NULL;
2796 const int n = ls_db_list_cameras(db, NULL, 0);
2797 if(n <= 0) return NULL;
2798 long long *ids = (long long *)dt_alloc_align(sizeof(long long) * (size_t)n);
2799 if(IS_NULL_PTR(ids)) return NULL;
2800 *out_n = ls_db_list_cameras(db, ids, n);
2801 return ids;
2802}
2803
2804static void parse_model(const char *txt, char *model, size_t sz_model)
2805{
2806 while(txt[0] && isspace(txt[0])) txt++;
2807 size_t len = strlen(txt);
2808 if(len > sz_model - 1) len = sz_model - 1;
2809 memcpy(model, txt, len);
2810 model[len] = 0;
2811}
2812
2813static void camera_menusearch_clicked(GtkWidget *button, gpointer user_data)
2814{
2815 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
2817
2818 (void)button;
2819
2820 int n = 0;
2821 long long *ids = _camera_all_ids(&n);
2822 if(IS_NULL_PTR(ids)) return;
2823 camera_menu_fill(self, ids, n);
2824 dt_free_align(ids);
2825
2826 dt_gui_menu_popup(GTK_MENU(g->camera_menu), button, GDK_GRAVITY_SOUTH, GDK_GRAVITY_NORTH);
2827}
2828
2829
2830/* -- end camera -- */
2831
2833{
2835 const char *text = dt_bauhaus_combobox_get_text(widget);
2836 if(text) (void)sscanf(text, "%f", &p->focal);
2837 p->modified = 1;
2838 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
2839}
2840
2842{
2844 const char *text = dt_bauhaus_combobox_get_text(widget);
2845 if(text) (void)sscanf(text, "%f", &p->aperture);
2846 p->modified = 1;
2847 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
2848}
2849
2851{
2853 const char *text = dt_bauhaus_combobox_get_text(widget);
2854 if(text) (void)sscanf(text, "%f", &p->distance);
2855 p->modified = 1;
2856 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
2857}
2858
2859static void delete_children(GtkWidget *widget, gpointer data)
2860{
2861 (void)data;
2862 gtk_widget_destroy(widget);
2863}
2864
2866static const char *_lens_type_name(int type)
2867{
2868 switch(type)
2869 {
2870 case DT_LENS_RECTILINEAR: return _("rectilinear");
2871 case DT_LENS_FISHEYE: return _("fisheye");
2872 case DT_LENS_PANORAMIC: return _("panoramic");
2873 case DT_LENS_EQUIRECTANGULAR: return _("equirectangular");
2874 case DT_LENS_FISHEYE_ORTHOGRAPHIC: return _("orthographic fisheye");
2875 case DT_LENS_FISHEYE_STEREOGRAPHIC: return _("stereographic fisheye");
2876 case DT_LENS_FISHEYE_EQUISOLID: return _("equisolid fisheye");
2877 case DT_LENS_FISHEYE_THOBY: return _("Thoby fisheye");
2878 default: return _("unknown");
2879 }
2880}
2881
2889static void _lens_params_set_lens(dt_iop_module_t *self, const long long lens_id)
2890{
2892
2893 ls_db_t *db = _ls_db();
2894 char l_maker[128] = "", l_model[256] = "";
2895 if(lens_id < 0 || IS_NULL_PTR(db)
2896 || ls_db_lens_name(db, lens_id, l_maker, sizeof(l_maker), l_model, sizeof(l_model)) <= 0)
2897 return;
2898
2899 g_strlcpy(p->lens, l_model, sizeof(p->lens));
2900}
2901
2904static void lens_set(dt_iop_module_t *self, long long lens_id)
2905{
2907 /* const, and effective: this reads the focal length, aperture and distance to seed the
2908 * editable comboboxes, and shows what is in force rather than what is stored. */
2910 self, (const dt_iop_lensfun_params_t *)self->params);
2911
2912 gchar *fm;
2913 const char *maker, *model;
2914 unsigned i;
2915 gdouble focal_values[]
2916 = { -INFINITY, 4.5, 8, 10, 12, 14, 15, 16, 17, 18, 20, 24, 28, 30, 31, 35,
2917 38, 40, 43, 45, 50, 55, 60, 70, 75, 77, 80, 85, 90, 100, 105, 110,
2918 120, 135, 150, 200, 210, 240, 250, 300, 400, 500, 600, 800, 1000, INFINITY };
2919 gdouble aperture_values[]
2920 = { -INFINITY, 0.7, 0.8, 0.9, 1, 1.1, 1.2, 1.4, 1.8, 2, 2.2, 2.5, 2.8, 3.2, 3.4, 4, 4.5, 5.0,
2921 5.6, 6.3, 7.1, 8, 9, 10, 11, 13, 14, 16, 18, 20, 22, 25, 29, 32, 38, INFINITY };
2922
2923 ls_db_t *db = _ls_db();
2924 ls_lens_t lens_v;
2925 char l_maker[128] = "", l_model[256] = "";
2926 float min_focal = 0.f, max_focal = 0.f, min_ap = 0.f, max_ap = 0.f;
2927 const gboolean have = (lens_id >= 0) && !IS_NULL_PTR(db)
2928 && (ls_db_lens_by_id(db, lens_id, &lens_v) == 1)
2929 && (ls_db_lens_name(db, lens_id, l_maker, sizeof(l_maker),
2930 l_model, sizeof(l_model)) > 0);
2931 if(have) ls_db_lens_range(db, lens_id, &min_focal, &max_focal, &min_ap, &max_ap);
2932
2933 /* Which sources exist is a property of the lens, so it is re-decided HERE and not only
2934 * when the image is loaded: picking a different lens from the menu is exactly the moment
2935 * a database profile appears or disappears. Without this the panel kept whatever rows the
2936 * image arrived with, and choosing a lens the database does know left every axis still
2937 * offering nothing. */
2938 if(!have)
2939 {
2940 /* Nothing is disabled on this path. Availability is expressed by which ROWS a picker
2941 * holds, never by whether the picker is sensitive -- greying the whole widget withdrew
2942 * the two sources that need no database entry at all, the file's own embedded profile
2943 * and hand-typed TCA coefficients, which is precisely what a lens the database has
2944 * never heard of is left with. */
2947 return;
2948 }
2949
2950 maker = l_maker[0] ? l_maker : NULL;
2951 model = l_model[0] ? l_model : NULL;
2952
2953 if(model)
2954 {
2955 if(maker)
2956 fm = g_strdup_printf("%s, %s", maker, model);
2957 else
2958 fm = g_strdup_printf("%s", model);
2959 gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(g->lens_model))), fm);
2960 dt_free(fm);
2961 }
2962
2963 char focal[100], aperture[100], mounts[200];
2964
2965 if(min_focal < max_focal)
2966 snprintf(focal, sizeof(focal), "%g-%gmm", min_focal, max_focal);
2967 else
2968 snprintf(focal, sizeof(focal), "%gmm", min_focal);
2969 if(min_ap < max_ap)
2970 snprintf(aperture, sizeof(aperture), "%g-%g", min_ap, max_ap);
2971 else
2972 snprintf(aperture, sizeof(aperture), "%g", min_ap);
2973
2974 mounts[0] = 0;
2975 ls_db_lens_mounts(db, lens_id, mounts, sizeof(mounts));
2976
2977 fm = g_strdup_printf(_("maker:\t\t%s\n"
2978 "model:\t\t%s\n"
2979 "focal range:\t%s\n"
2980 "aperture:\t%s\n"
2981 "crop factor:\t%.1f\n"
2982 "type:\t\t%s\n"
2983 "mounts:\t%s"),
2984 maker ? maker : "?", model ? model : "?", focal, aperture,
2985 lens_v.crop_factor, _lens_type_name((int)lens_v.type), mounts);
2986
2987 gtk_widget_set_tooltip_text(GTK_WIDGET(g->lens_model), fm);
2988 dt_free(fm);
2989
2990 /* Create the focal/aperture/distance combo boxes */
2991 gtk_container_foreach(GTK_CONTAINER(g->lens_param_box), delete_children, NULL);
2992
2993 int ffi = 1, fli = -1;
2994 for(i = 1; i < sizeof(focal_values) / sizeof(gdouble) - 1; i++)
2995 {
2996 if(focal_values[i] < min_focal) ffi = i + 1;
2997 if(focal_values[i] > max_focal && fli == -1) fli = i;
2998 }
2999 if(focal_values[ffi] > min_focal)
3000 {
3001 focal_values[ffi - 1] = min_focal;
3002 ffi--;
3003 }
3004 if(max_focal == 0 || fli < 0) fli = sizeof(focal_values) / sizeof(gdouble) - 2;
3005 if(focal_values[fli + 1] < max_focal)
3006 {
3007 focal_values[fli + 1] = max_focal;
3008 ffi++;
3009 }
3010 if(fli < ffi) fli = ffi + 1;
3011
3012 GtkWidget *w;
3013 char txt[30];
3014
3015 // focal length
3017 dt_bauhaus_widget_set_label(w, N_("mm"));
3018 gtk_widget_set_tooltip_text(w, _("focal length (mm)"));
3019 snprintf(txt, sizeof(txt), "%.*f", precision(p->focal, 10.0), p->focal);
3021 for(int k = 0; k < fli - ffi; k++)
3022 {
3023 snprintf(txt, sizeof(txt), "%.*f", precision(focal_values[ffi + k], 10.0), focal_values[ffi + k]);
3025 }
3026 g_signal_connect(G_OBJECT(w), "value-changed", G_CALLBACK(lens_comboentry_focal_update), self);
3027 gtk_box_pack_start(GTK_BOX(g->lens_param_box), w, TRUE, TRUE, 0);
3029 g->cbe[0] = w;
3030
3031 // f-stop
3032 ffi = 1, fli = sizeof(aperture_values) / sizeof(gdouble) - 1;
3033 for(i = 1; i < sizeof(aperture_values) / sizeof(gdouble) - 1; i++)
3034 if(aperture_values[i] < min_ap) ffi = i + 1;
3035 if(aperture_values[ffi] > min_ap)
3036 {
3037 aperture_values[ffi - 1] = min_ap;
3038 ffi--;
3039 }
3040
3042 dt_bauhaus_widget_set_label(w, N_("f"));
3043 gtk_widget_set_tooltip_text(w, _("f-number (aperture)"));
3044 snprintf(txt, sizeof(txt), "%.*f", precision(p->aperture, 10.0), p->aperture);
3046 for(int k = 0; k < fli - ffi; k++)
3047 {
3048 snprintf(txt, sizeof(txt), "%.*f", precision(aperture_values[ffi + k], 10.0), aperture_values[ffi + k]);
3050 }
3051 g_signal_connect(G_OBJECT(w), "value-changed", G_CALLBACK(lens_comboentry_aperture_update), self);
3052 gtk_box_pack_start(GTK_BOX(g->lens_param_box), w, TRUE, TRUE, 0);
3054 g->cbe[1] = w;
3055
3057 dt_bauhaus_widget_set_label(w, N_("d"));
3058 gtk_widget_set_tooltip_text(w, _("distance to subject"));
3059 snprintf(txt, sizeof(txt), "%.*f", precision(p->distance, 10.0), p->distance);
3061 float val = 0.25f;
3062 for(int k = 0; k < 25; k++)
3063 {
3064 if(val > 1000.0f) val = 1000.0f;
3065 snprintf(txt, sizeof(txt), "%.*f", precision(val, 10.0), val);
3067 if(val >= 1000.0f) break;
3068 val *= sqrtf(2.0f);
3069 }
3070 g_signal_connect(G_OBJECT(w), "value-changed", G_CALLBACK(lens_comboentry_distance_update), self);
3071 gtk_box_pack_start(GTK_BOX(g->lens_param_box), w, TRUE, TRUE, 0);
3073 g->cbe[2] = w;
3074
3075 gtk_widget_show_all(g->lens_param_box);
3076
3077 /* Last, because the rows are read out of the params, which a caller acting on user input
3078 * has already written through _lens_params_set_lens(). */
3081}
3082
3083static void lens_menu_select(GtkMenuItem *menuitem, gpointer user_data)
3084{
3085 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
3086
3087 if(dt_gui_widgets_suppressed()) return;
3088
3091 const long long lens_id =
3092 (long long)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menuitem), "lens-id"));
3093
3094 /* Parameters first: both the autoscale below and the row rebuild inside lens_set() ask
3095 * the database what THIS lens offers, and they read it out of the params. */
3096 _lens_params_set_lens(self, lens_id);
3097 p->modified = 1;
3098 p->scale = get_autoscale(self, p);
3099
3100 {
3101 /* Widget writes, not user input. Without the freeze dt_bauhaus_slider_set() emits
3102 * value-changed, whose default callback writes the field again and commits a SECOND
3103 * history item for the one lens the user picked. */
3105 lens_set(self, lens_id);
3106 dt_bauhaus_slider_set(g->scale, p->scale);
3107 }
3108
3109 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
3110}
3111
3112static void lens_menu_fill(dt_iop_module_t *self, const long long *ids, int n)
3113{
3115 GPtrArray *makers, *submenus;
3116
3117 if(g->lens_menu)
3118 {
3119 gtk_widget_destroy(GTK_WIDGET(g->lens_menu));
3120 g->lens_menu = NULL;
3121 }
3122
3123 ls_db_t *db = _ls_db();
3124 if(IS_NULL_PTR(db)) return;
3125
3126 /* Count all existing lens makers and create a sorted list */
3127 makers = g_ptr_array_new_with_free_func(dt_free_gpointer);
3128 submenus = g_ptr_array_new();
3129 for(int i = 0; i < n; i++)
3130 {
3131 char maker[128] = "", model[256] = "";
3132 if(ls_db_lens_name(db, ids[i], maker, sizeof(maker), model, sizeof(model)) <= 0) continue;
3133
3134 GtkWidget *submenu, *item;
3135 int idx = ptr_array_find_sorted(makers, maker, (GCompareFunc)g_utf8_collate);
3136 if(idx < 0)
3137 {
3138 /* No such maker yet, insert it into the array. Owned strings: these no longer point
3139 * into a database that outlives the menu. */
3140 idx = ptr_array_insert_sorted(makers, g_strdup(maker), (GCompareFunc)g_utf8_collate);
3141 /* Create a submenu for lenses by this maker */
3142 submenu = gtk_menu_new();
3143 ptr_array_insert_index(submenus, submenu, idx);
3144 }
3145
3146 submenu = (GtkWidget *)g_ptr_array_index(submenus, idx);
3147 /* Append current lens name to the submenu */
3148 item = gtk_menu_item_new_with_label(model);
3149 gtk_widget_show(item);
3150 g_object_set_data(G_OBJECT(item), "lens-id", GINT_TO_POINTER((gint)ids[i]));
3151 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(lens_menu_select), self);
3152 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), item);
3153 }
3154
3155 g->lens_menu = GTK_MENU(gtk_menu_new());
3156 for(unsigned i = 0; i < makers->len; i++)
3157 {
3158 GtkWidget *item = gtk_menu_item_new_with_label((const gchar *)g_ptr_array_index(makers, i));
3159 gtk_widget_show(item);
3160 gtk_menu_shell_append(GTK_MENU_SHELL(g->lens_menu), item);
3161 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), (GtkWidget *)g_ptr_array_index(submenus, i));
3162 }
3163
3164 g_ptr_array_free(submenus, TRUE);
3165 g_ptr_array_free(makers, TRUE);
3166}
3167
3179static long long *_lens_ids_for_camera(dt_iop_module_t *self, const char *model, int *out_n)
3180{
3181 *out_n = 0;
3183 ls_db_t *db = _ls_db();
3184 if(IS_NULL_PTR(db)) return NULL;
3185
3186 ls_camera_t cam;
3187 const gboolean have_cam = (g->camera_id >= 0) && (ls_db_camera_by_id(db, g->camera_id, &cam) == 1);
3188
3189 if(model && model[0])
3190 {
3191 enum { MAX_HITS = 32 };
3192 ls_db_match_t m[MAX_HITS];
3193 const int n = ls_db_match_lens(db, NULL, model, have_cam ? cam.mount_id : 0,
3194 have_cam ? cam.crop_factor : 0.f, m, MAX_HITS);
3195 if(n <= 0) return NULL;
3196 long long *ids = (long long *)dt_alloc_align(sizeof(long long) * (size_t)n);
3197 if(IS_NULL_PTR(ids)) return NULL;
3198 for(int i = 0; i < n; i++) ids[i] = m[i].lens_id;
3199 *out_n = n;
3200 return ids;
3201 }
3202
3203 /* Everything that fits the camera's mount, or the whole catalogue when no camera is
3204 * selected -- which is what upstream answered for a NULL camera too. */
3205 const int total = ls_db_list_lenses(db, NULL, 0);
3206 if(total <= 0) return NULL;
3207 long long *all = (long long *)dt_alloc_align(sizeof(long long) * (size_t)total);
3208 if(IS_NULL_PTR(all)) return NULL;
3209 const int got = ls_db_list_lenses(db, all, total);
3210
3211 if(!have_cam)
3212 {
3213 *out_n = got;
3214 return all;
3215 }
3216
3217 int keep = 0;
3218 for(int i = 0; i < got; i++)
3219 if(ls_db_lens_fits_mount(db, all[i], cam.mount_id) == 1) all[keep++] = all[i];
3220 *out_n = keep;
3221 return all;
3222}
3223
3224static void lens_menusearch_clicked(GtkWidget *button, gpointer user_data)
3225{
3226 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
3228 (void)button;
3229
3230 int n = 0;
3231 long long *ids = _lens_ids_for_camera(self, NULL, &n);
3232 if(IS_NULL_PTR(ids)) return;
3233 lens_menu_fill(self, ids, n);
3234 dt_free_align(ids);
3235
3236 dt_gui_menu_popup(GTK_MENU(g->lens_menu), button, GDK_GRAVITY_SOUTH, GDK_GRAVITY_NORTH);
3237}
3238
3239
3240/* -- end lens -- */
3241
3242static void target_geometry_changed(GtkWidget *widget, gpointer user_data)
3243{
3244 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
3246
3247 int pos = dt_bauhaus_combobox_get(widget);
3248 p->target_geom = (dt_lens_type_t)(pos + DT_LENS_UNKNOWN + 1);
3249 p->modified = 1;
3250 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
3251}
3252
3262/* Defined further down, next to the data they work on. */
3263static int _lens_source_row(const dt_iop_lensfun_gui_data_t *g, const dt_lens_axis_t axis,
3264 const dt_lens_source_t source);
3266 const dt_iop_lensfun_params_t *const p);
3267
3277{
3278 dt_iop_lensfun_params_t probe = *p;
3279 for(dt_lens_axis_t axis = 0; axis < DT_LENS_AXIS_LAST; axis++)
3281 return _lens_corrections_available(self, &probe);
3282}
3283
3284
3297 const dt_lens_axis_t axis)
3298{
3299 const dt_lens_source_t source = _lens_source_get(p, axis);
3300 return (_lens_source_row(g, axis, source) >= 0) ? source : DT_LENS_SOURCE_OFF;
3301}
3302
3304{
3306 self, (const dt_iop_lensfun_params_t *)self->params);
3308 if(IS_NULL_PTR(g)) return;
3309
3312
3313 /* Projection and scaling are the other two thirds of the distortion pack, so they appear
3314 * exactly when that pack runs. Leaving the scale slider visible while it does nothing was
3315 * the same defect as the hidden geometry combobox that kept applying -- a control and its
3316 * effect disagreeing -- just the other way round. */
3317 /* Scaling belongs to the pack, so it shows whenever the pack runs -- from either source.
3318 * Projection does not: changing the lens's projection is a property of the database's
3319 * model, and a maker's profile describes the lens in the projection it shipped with. */
3320 gtk_widget_set_visible(g->scale, dist != DT_LENS_SOURCE_OFF);
3321 gtk_widget_set_visible(g->target_geom, dist == DT_LENS_SOURCE_LENSFUN);
3322
3323 gtk_widget_set_visible(g->tca_r, tca == DT_LENS_SOURCE_MANUAL);
3324 gtk_widget_set_visible(g->tca_b, tca == DT_LENS_SOURCE_MANUAL);
3325
3326 /* Soft deprecation of the distort/correct mode: shown only to an edit that already uses
3327 * it, so nobody loses a setting they made, and offered to nobody else. The module is
3328 * called lens correction; deliberately ADDING a lens's flaws is not something anyone has
3329 * been found to want, and the row cost more panel than the feature was worth. */
3330 gtk_widget_set_visible(g->reverse, p->inverse != 0);
3331
3332 /* Which sources this image can actually offer is decided in _lens_rebuild_axis_rows(),
3333 * which runs from gui_update() and from every setter that changes what the database can
3334 * match: a source with nothing behind it gets no row at all. Greying was not enough --
3335 * bauhaus honours entry sensitivity when the list is scrolled but not when a row is
3336 * clicked, so a greyed row was still selectable and still did nothing. Nor is greying the
3337 * whole picker: OFF and manual TCA are available to every image, database or not. */
3338}
3339
3340/* What each source is called in the panel. Indexed by dt_lens_source_t, so the name and
3341 * the value cannot drift apart the way two parallel lists would.
3342 *
3343 * The two that ARE profiles say whose measurement they are, because that is the choice the
3344 * user is actually making: the camera maker measured this body with this lens on a bench,
3345 * while the database is a community measurement of that lens model. "Embedded" and
3346 * "database" described where the numbers were stored, which is the one thing about them
3347 * nobody needs to know. The other two are not profiles and do not pretend to be. */
3348static const char *const _lens_source_names[DT_LENS_SOURCE_LAST] = {
3349 [DT_LENS_SOURCE_OFF] = N_("no correction"),
3350 [DT_LENS_SOURCE_LENSFUN] = N_("community profile"),
3351 [DT_LENS_SOURCE_EMBEDDED] = N_("vendor profile"),
3352 [DT_LENS_SOURCE_MANUAL] = N_("manual correction"),
3353};
3354
3355/* Which sources one axis's combobox offers, in the order they appear. OFF is always first
3356 * and LENSFUN always second, so a row's index means the same thing on every axis and the
3357 * user is not re-learning the widget three times. */
3366
3375 const dt_lens_source_t source)
3376{
3377 for(int i = 0; i < g->axis_rows[axis]; i++)
3378 if(g->axis_row[axis][i] == source) return i;
3379 return -1;
3380}
3381
3399{
3401 self, (const dt_iop_lensfun_params_t *)self->params);
3403 if(IS_NULL_PTR(g)) return;
3404
3405 const int offers = _lens_database_offers(self, p);
3406
3407 /* A monochrome sensor has no colour channels to shift against each other, so lateral CA
3408 * is not a correction that exists for this image at all -- _lens_mask_for_mono() already
3409 * withdraws it from every pipe. Offering it here would be a row that renders nothing,
3410 * which is the one thing the per-image row list exists to prevent. */
3411 const gboolean mono = !IS_NULL_PTR(self->dev)
3413
3414 {
3415 GtkWidget *w = g->axis_source[axis];
3416 int n = 0;
3417
3418 /* Filling a combobox is a programmatic update, and _lens_axis_source_changed() must not
3419 * mistake it for the user picking a row: it commits to history. Two callers are already
3420 * outside gui_update()'s own freeze -- the distortion handler rebuilding the TCA row,
3421 * and lens_set() rebuilding everything when the lens changes -- so the freeze belongs
3422 * here, where the widget is actually written, rather than at each call site. */
3424
3426 for(int i = 0; i < DT_LENS_SOURCE_LAST; i++)
3427 {
3428 const dt_lens_source_t src = _lens_axis_sources[axis][i];
3429 if(src == DT_LENS_SOURCE_LAST) break;
3430
3431 gboolean have;
3432 switch(src)
3433 {
3434 case DT_LENS_SOURCE_OFF: have = TRUE; break;
3435 case DT_LENS_SOURCE_LENSFUN: have = _lens_flags_have_axis(offers, axis); break;
3437 have = _lens_image_embeds(self, axis);
3438 /* A maker's aberration is measured against their OWN geometry, so it is offered
3439 * only when the geometry is theirs too. Stacking it on a database distortion
3440 * would be two calibrations of one lens applied to each other -- and the
3441 * evaluator cannot express it either: the table's CA lives in the same curve as
3442 * its distortion. The other direction is fine and supported: the table's geometry
3443 * happily wears a database or hand-typed aberration. */
3444 if(axis == DT_LENS_AXIS_TCA
3446 have = FALSE;
3447 break;
3448 case DT_LENS_SOURCE_MANUAL: have = (axis == DT_LENS_AXIS_TCA); break;
3449 default: have = FALSE; break;
3450 }
3451 if(mono && axis == DT_LENS_AXIS_TCA && src != DT_LENS_SOURCE_OFF) have = FALSE;
3452 if(!have) continue;
3453
3455 g->axis_row[axis][n++] = src;
3456 }
3457 g->axis_rows[axis] = n;
3458
3459 /* Show what the params say, or fall back to OFF -- which is row 0 and always there.
3460 * A stored source this image cannot supply has no row to select, and the correction
3461 * would not have run either; commit_params has already said so. */
3462 int row = _lens_source_row(g, axis, _lens_source_get(p, axis));
3463 if(row < 0) row = 0;
3465 }
3466}
3467
3470{
3471 for(dt_lens_axis_t axis = 0; axis < DT_LENS_AXIS_LAST; axis++)
3472 _lens_rebuild_axis_row(self, axis);
3473}
3474
3475
3478 const GtkWidget *w)
3479{
3480 for(dt_lens_axis_t axis = 0; axis < DT_LENS_AXIS_LAST; axis++)
3481 if(g->axis_source[axis] == w) return axis;
3482 return DT_LENS_AXIS_LAST;
3483}
3484
3485static void _lens_axis_source_changed(GtkWidget *widget, gpointer user_data)
3486{
3487 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
3488 if(dt_gui_widgets_suppressed()) return;
3491
3492 const dt_lens_axis_t axis = _lens_axis_of_widget(g, widget);
3493 if(axis == DT_LENS_AXIS_LAST) return;
3494
3495 /* Against what the widget currently holds, not the catalogue: rows the image cannot
3496 * supply are absent, so a catalogue index would name the wrong source. */
3497 const int row = dt_bauhaus_combobox_get(widget);
3498 if(row < 0 || row >= g->axis_rows[axis]) return;
3499
3500 const dt_lens_source_t source = g->axis_row[axis][row];
3501
3502 _lens_source_set(p, axis, source);
3503 p->modified = 1;
3504
3505 /* Distortion decides whether the maker's aberration may be offered, so its rows move when
3506 * distortion does. Rebuilding a DIFFERENT axis's widget from this handler is safe;
3507 * rebuilding the one being handled would not be. */
3509
3511 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
3512}
3513
3521 const dt_lens_axis_t axis, const char *label,
3522 const char *tooltip)
3523{
3525 g->axis_source[axis] = w;
3527 gtk_widget_set_tooltip_text(w, _(tooltip));
3528 gtk_box_pack_start(GTK_BOX(self->gui->widget), w, TRUE, TRUE, 0);
3529
3530 /* Left empty: the rows depend on the image, and gui_update() fills them. */
3531 g->axis_rows[axis] = 0;
3532 g_signal_connect(G_OBJECT(w), "value-changed",
3533 G_CALLBACK(_lens_axis_source_changed), (gpointer)self);
3534 return w;
3535}
3536
3537void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
3538{
3540
3541 /* Which controls are visible is decided in ONE place, _lens_gui_update_sensitivity(), out
3542 * of the sources the pickers are actually showing. It used to be decided here as well,
3543 * out of p->tca_override and the monochrome flag, and the two could disagree: this runs
3544 * LAST out of gui_update() and so had the final word, putting the manual coefficient
3545 * sliders back on screen for an image whose TCA picker was not offering manual at all. */
3546 if(w)
3547 {
3548 // user did modify something with some widget
3549 p->modified = 1;
3550 }
3551}
3552
3553
3555{
3556 float scale = 1.0f;
3557
3558 /* Built by THE constructor, not by hand. The hand-rolled version resolved only the
3559 * database lens, so with distortion taking the maker's embedded profile this measured a
3560 * correction the pipe was not applying -- and the result was then multiplied ON TOP of
3561 * the profile's own autoscale by get_modifier(), giving a doubled zoom. It also returned
3562 * 1.0 outright when no database lens matched, which is precisely the body an embedded
3563 * profile exists to serve. */
3565 memset(&d, 0, sizeof(d));
3566 _lens_build_data(self, p, &d);
3567 if(!_lens_data_available(&d)) return scale;
3568
3569 /* Measure the correction itself, not a scaling already applied to it. For the embedded
3570 * resolver this leaves the profile's own autoscale in place and asks what is still needed
3571 * on top of it -- which is what get_modifier() then composes. */
3572 d.scale = 1.0f;
3573
3574 const dt_image_t *img = &(self->dev->image_storage);
3575 // FIXME: get those from rawprepare IOP somehow !!!
3576 const int iwd = img->width - img->crop_x - img->crop_width,
3577 iht = img->height - img->crop_y - img->crop_height;
3578
3579 ls_modifier_t modifier;
3580 if(get_modifier(NULL, iwd, iht, &d, DT_LENS_MODIFY_ALL_AXES, FALSE, &modifier, NULL))
3581 scale = ls_modifier_autoscale(&modifier);
3582 return scale;
3583}
3584
3585static void autoscale_pressed(GtkWidget *button, gpointer user_data)
3586{
3587 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
3590 const float scale = get_autoscale(self, p);
3591 p->modified = 1;
3592 dt_bauhaus_slider_set(g->scale, scale);
3593 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
3594}
3595
3613 const dt_iop_lensfun_params_t *const p)
3614{
3615 if(IS_NULL_PTR(self->dev)) return 0;
3616
3618 memset(&d, 0, sizeof(d));
3619 _lens_build_data(self, p, &d);
3620 if(!_lens_data_available(&d)) return 0;
3621
3622 /* The frame the correction is expressed over. Only its aspect matters to which axes
3623 * resolve, so the full image is a fine stand-in when the pipe has not published one. */
3624 dt_iop_roi_t roi = { 0, 0, 0, 0, 1.f };
3625 if(!dt_dev_module_geometry_gui(self->dev, self, &roi, NULL) || roi.width <= 0
3626 || roi.height <= 0)
3627 {
3628 const dt_image_t *img = &self->dev->image_storage;
3629 roi.width = img->width;
3630 roi.height = img->height;
3631 }
3632 if(roi.width <= 0 || roi.height <= 0) return 0;
3633
3635 int modflags = 0;
3636 ls_modifier_t m;
3637 get_modifier(&modflags, roi.width, roi.height, &d, mask, FALSE, &m, NULL);
3638 return modflags & LENSFUN_MODFLAG_MASK;
3639}
3640
3641
3642void gui_init(struct dt_iop_module_t *self)
3643{
3645
3646 g->camera_id = -1;
3647 g->camera_menu = NULL;
3648 g->lens_menu = NULL;
3649
3650
3651 self->gui->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
3652 gtk_widget_set_name(self->gui->widget, "lens-module");
3653
3654 /* One button per row, not two. Each row used to carry a second, unlabelled arrow that
3655 * listed the Exif match instead of the whole catalogue -- for the camera, a menu of
3656 * exactly one entry, the one reload_defaults() had already applied.
3657 *
3658 * Filtering these lists by Exif is useless by construction: they are opened precisely
3659 * BECAUSE Exif matching failed. A lens with no CPU reports no lens at all, so there is
3660 * nothing to filter on; a body absent from the database is chosen by picking a near
3661 * relative deliberately -- a Mk I for a Mk II -- which an Exif filter would never
3662 * suggest. Both cases want the full list, which is what the model button has always
3663 * shown. */
3664
3665 // camera selector
3666 GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
3667 g->camera_model = dt_iop_button_new(self, N_("camera model"),
3668 G_CALLBACK(camera_menusearch_clicked), FALSE, 0, (GdkModifierType)0,
3669 NULL, 0, hbox);
3670 gtk_box_pack_start(GTK_BOX(self->gui->widget), hbox, TRUE, TRUE, 0);
3671
3672 // lens selector
3673 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
3674 g->lens_model = dt_iop_button_new(self, N_("lens model"),
3675 G_CALLBACK(lens_menusearch_clicked), FALSE, 0, (GdkModifierType)0,
3676 NULL, 0, hbox);
3677 gtk_box_pack_start(GTK_BOX(self->gui->widget), hbox, TRUE, TRUE, 0);
3678
3679 // lens properties
3680 g->lens_param_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
3681 gtk_box_pack_start(GTK_BOX(self->gui->widget), g->lens_param_box, TRUE, TRUE, 0);
3682
3683
3684 /* The panel reads top to bottom as: what was detected, how much to rescale, then one
3685 * section per correction axis. Each section is its source combobox followed by exactly
3686 * the controls that source uses -- geometry under distortion, the manual coefficients
3687 * under TCA -- so a control is never far from the thing that decides whether it applies.
3688 *
3689 * That is why these are written out one at a time rather than looped: the loop would have
3690 * to put every axis's extra controls somewhere else, and "somewhere else" is how the old
3691 * panel ended up with a TCA override checkbox three rows away from the TCA setting. */
3692
3693 // 2. vignetting
3694 _lens_add_axis_combobox(self, g, DT_LENS_AXIS_VIGNETTING, N_("vignetting"),
3695 N_("correct the lens's light falloff, and where to take it from"));
3696
3697 // 3. distortion, with the projection and the scaling that go with it
3698 _lens_add_axis_combobox(self, g, DT_LENS_AXIS_DISTORTION, N_("distortion"),
3699 N_("correct the lens's geometric distortion, and where to take it from"));
3700
3702 dt_bauhaus_widget_set_label(g->target_geom, N_("geometry"));
3703 gtk_box_pack_start(GTK_BOX(self->gui->widget), g->target_geom, TRUE, TRUE, 0);
3704 gtk_widget_set_tooltip_text(g->target_geom, _("target geometry"));
3705 dt_bauhaus_combobox_add(g->target_geom, _("rectilinear"));
3706 dt_bauhaus_combobox_add(g->target_geom, _("fish-eye"));
3707 dt_bauhaus_combobox_add(g->target_geom, _("panoramic"));
3708 dt_bauhaus_combobox_add(g->target_geom, _("equirectangular"));
3709 dt_bauhaus_combobox_add(g->target_geom, _("orthographic"));
3710 dt_bauhaus_combobox_add(g->target_geom, _("stereographic"));
3711 dt_bauhaus_combobox_add(g->target_geom, _("equisolid angle"));
3712 dt_bauhaus_combobox_add(g->target_geom, _("thoby fish-eye"));
3713 g_signal_connect(G_OBJECT(g->target_geom), "value-changed", G_CALLBACK(target_geometry_changed),
3714 (gpointer)self);
3715
3716 /* Scaling closes the distortion section rather than opening the module. It is the third
3717 * of the pack -- distortion, projection, scale -- and it only ever runs when that pack
3718 * runs, so putting it at the top left the user reading a control whose effect lived four
3719 * rows further down, under a different heading. */
3720 g->scale = dt_bauhaus_slider_from_params(self, N_("scale"));
3723 g_signal_connect(G_OBJECT(g->scale), "quad-pressed", G_CALLBACK(autoscale_pressed), self);
3724 gtk_widget_set_tooltip_text(g->scale, _("auto scale"));
3725
3726 // 4. chromatic aberrations, and the coefficients the manual source uses
3727 _lens_add_axis_combobox(self, g, DT_LENS_AXIS_TCA, N_("chromatic aberrations"),
3728 N_("correct lateral chromatic aberration, and where to take it from"));
3729
3730 /* p->tca_override has NO widget. It survives only as storage -- _lens_source_set()
3731 * writes it whenever TCA is set to manual, so an edit saved by this version is still read
3732 * correctly by one that predates the per-axis sources. The checkbox it used to drive is
3733 * gone: "TCA = manual correction" is a row of the combobox above, and one state behind two
3734 * controls is one state they can disagree about. */
3735
3736 g->tca_r = dt_bauhaus_slider_from_params(self, "tca_r");
3738 gtk_widget_set_tooltip_text(g->tca_r, _("Transversal Chromatic Aberration red"));
3739
3740 g->tca_b = dt_bauhaus_slider_from_params(self, "tca_b");
3742 gtk_widget_set_tooltip_text(g->tca_b, _("Transversal Chromatic Aberration blue"));
3743
3744 /* Last, because it is on its way out: it inverts every axis at once, nobody has been
3745 * found who uses it, and it is kept only so existing edits that set it keep rendering. */
3746 g->reverse = dt_bauhaus_combobox_from_params(self, "inverse");
3747 dt_bauhaus_combobox_add(g->reverse, _("correct"));
3748 dt_bauhaus_combobox_add(g->reverse, _("distort"));
3749 gtk_widget_set_tooltip_text(g->reverse, _("correct distortions or apply them"));
3750
3751
3752}
3753
3754void gui_update(struct dt_iop_module_t *self)
3755{
3756 // let gui elements reflect params
3758
3759 /* What is actually IN FORCE, which for an edit saved with modified == 0 is default_params
3760 * rather than params -- see _lens_effective_params(). The view shows that; it does not
3761 * write it back. This function used to memcpy default_params over self->params right
3762 * here, which mutated an edit from a panel refresh, and did not even reach the widgets it
3763 * was meant to fix: dt_iop_gui_update() runs dt_bauhaus_update_module() out of
3764 * self->params BEFORE calling this, so the copy only ever showed up one refresh late. */
3766 self, (const dt_iop_lensfun_params_t *)self->params);
3767
3768 // these are the wrong (untranslated) strings in general but that's ok, they will be overwritten further
3769 // down
3770 gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(g->camera_model))), p->camera);
3771 gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(g->lens_model))), p->lens);
3772 gtk_widget_set_tooltip_text(g->camera_model, "");
3773 gtk_widget_set_tooltip_text(g->lens_model, "");
3774
3777
3778 /* dt_bauhaus_update_module() has already synced these from self->params. Re-set them from
3779 * the effective params so a modified == 0 edit shows what it renders rather than what it
3780 * stores; for every other edit the two are the same object and this is a no-op. */
3781 dt_bauhaus_combobox_set(g->target_geom, p->target_geom - DT_LENS_UNKNOWN - 1);
3782 dt_bauhaus_combobox_set(g->reverse, p->inverse);
3783 dt_bauhaus_slider_set(g->scale, p->scale);
3784 dt_bauhaus_slider_set(g->tca_r, p->tca_r);
3785 dt_bauhaus_slider_set(g->tca_b, p->tca_b);
3786
3787 g->camera_id = -1;
3788 if(p->camera[0])
3789 {
3790 /* Resolved the same way the pipeline resolves it. Recovering the id by comparing the
3791 * params string against stored model names cannot work: matching is on the normalised
3792 * form, so "NIKON D5300" finds a row whose model column reads "D5300" -- which is why
3793 * this label was blank on every image while the correction itself was applied. */
3794 ls_camera_t cam;
3795 camera_set(self, _ls_find_camera(NULL, p->camera, &cam) ? cam.id : -1);
3796 }
3797 if(g->camera_id >= 0 && p->lens[0])
3798 {
3799 char model[200];
3800 parse_model(p->lens, model, sizeof(model));
3801 int n = 0;
3802 long long *ids = _lens_ids_for_camera(self, model[0] ? model : NULL, &n);
3803 lens_set(self, (n > 0 && !IS_NULL_PTR(ids)) ? ids[0] : -1);
3804 if(!IS_NULL_PTR(ids)) dt_free_align(ids);
3805 }
3806 else
3807 {
3808 lens_set(self, -1);
3809 }
3810
3811
3812
3813 gui_changed(self, NULL, NULL);
3814}
3815
3817{
3818
3820}
3821
3822
3823// clang-format off
3824// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
3825// vim: shiftwidth=2 expandtab tabstop=2 cindent
3826// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
3827// clang-format on
static double dist(double x1, double y1, double x2, double y2)
Definition ashift_lsd.c:250
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
#define m
Definition basecurve.c:283
void dt_bauhaus_slider_set_digits(GtkWidget *widget, int val)
Definition bauhaus.c:3343
void dt_bauhaus_combobox_clear(GtkWidget *widget)
Definition bauhaus.c:1997
void dt_bauhaus_combobox_set_editable(GtkWidget *widget, int editable)
Definition bauhaus.c:1882
int dt_bauhaus_combobox_get(GtkWidget *widget)
Definition bauhaus.c:2136
const char * dt_bauhaus_combobox_get_text(GtkWidget *widget)
Definition bauhaus.c:1970
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3331
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
Definition bauhaus.c:2090
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
Definition bauhaus.c:1504
GtkWidget * dt_bauhaus_combobox_new(dt_bauhaus_t *bh, dt_gui_module_t *self)
Definition bauhaus.c:1698
void dt_bauhaus_combobox_add(GtkWidget *widget, const char *text)
Definition bauhaus.c:1824
void dt_bauhaus_widget_set_quad_paint(GtkWidget *widget, dt_bauhaus_quad_paint_f f, int paint_flags, void *paint_data)
Definition bauhaus.c:1554
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
return vector dt_simd_set1(valid ?(scaling+NORM_MIN) :NORM_MIN)
@ IOP_CS_RGB
static const float x
const float v
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
const dt_colormatrix_t dt_aligned_pixel_t out
dt_store_simd_aligned(out, dt_mat3x4_mul_vec4(vin, dt_colormatrix_row_to_simd(matrix, 0), dt_colormatrix_row_to_simd(matrix, 1), dt_colormatrix_row_to_simd(matrix, 2)))
static const int row
gboolean dt_image_is_monochrome(const dt_image_t *img)
void * dt_alloc_align(size_t size)
Allocate cacheline-aligned memory.
Definition darktable.c:508
struct dt_bauhaus_t * dt_bauhaus_get_global(void)
Definition darktable.c:646
#define dt_dev_add_history_item(dev, module, enable, redraw)
void dt_iop_params_t
Definition dev_history.h:43
gboolean dt_dev_module_geometry_gui(dt_develop_t *dev, dt_iop_module_t *module, dt_iop_roi_t *in, dt_iop_roi_t *out)
One module's own input and output rectangles at full resolution, from the geometry service.
Definition develop.c:1751
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
Definition develop.h:123
GtkWidget * status
result of the last capture
void dt_loc_get_datadir(char *datadir, size_t bufsize)
void dt_loc_get_user_config_dir(char *configdir, size_t bufsize)
Where things are on the image, answered without a pipeline.
#define DT_GUI_MODULE(x)
const char * tooltip
Definition image.h:310
static void dt_iop_image_copy_by_size(float *const __restrict__ out, const float *const __restrict__ in, const size_t width, const size_t height, const size_t ch)
Definition imagebuf.h:91
void dt_iop_compute_module_hash(dt_iop_module_t *module, GList *masks)
Definition imageop.c:1461
const char ** dt_iop_set_description(dt_iop_module_t *module, const char *main_text, const char *purpose, const char *input, const char *process, const char *output)
Definition imageop.c:1893
@ IOP_FLAGS_ALLOW_TILING
Definition imageop.h:188
@ IOP_FLAGS_UNSAFE_COPY
Definition imageop.h:196
@ IOP_FLAGS_TILING_FULL_ROI
Definition imageop.h:190
@ IOP_GROUP_REPAIR
Definition imageop.h:159
@ IOP_TAG_DISTORT
Definition imageop.h:170
GtkWidget * dt_iop_button_new(dt_iop_module_t *self, const gchar *label, GCallback callback, gboolean local, guint accel_key, GdkModifierType mods, DTGTKCairoPaintIconFunc paint, gint paintflags, GtkWidget *box)
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
GtkWidget * dt_bauhaus_combobox_from_params(dt_iop_module_t *self, const char *param)
#define IOP_GUI_FREE
Definition imageop_gui.h:96
static dt_iop_gui_data_t * dt_iop_gui_data(const struct dt_iop_module_t *m)
The module's GUI data blob, NULL-safe for headless callers: IOP process() implementations read it for...
Definition imageop_gui.h:81
#define IOP_GUI_ALLOC(module)
Definition imageop_gui.h:93
void *const ovoid
const struct dt_interpolation * dt_interpolation_new(enum dt_interpolation_type type)
__DT_CLONE_TARGETS__ float dt_interpolation_compute_sample(const struct dt_interpolation *itor, const float *in, const float x, const float y, const int width, const int height, const int samplestride, const int linestride)
@ DT_INTERPOLATION_BICUBIC
@ DT_INTERPOLATION_BILINEAR
@ DT_INTERPOLATION_MITCHELL
@ DT_INTERPOLATION_USERPREF_WARP
const char * maker
const char * model
int operation_tags()
Definition lens.c:939
int distort_backtransform(dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, float *const __restrict points, size_t points_count)
Definition lens.c:1800
#define DT_LENS_MODIFY_ANSEL_MASK
Definition lens.c:195
void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition lens.c:2227
static const char *const _lens_source_names[DT_LENS_SOURCE_LAST]
Definition lens.c:3348
void distort_mask(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, struct dt_dev_pixelpipe_iop_t *piece, const float *const in, float *const out, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
Definition lens.c:1834
static long long * _camera_all_ids(int *out_n)
Every camera in the database, as ids the caller must free with dt_free_align().
Definition lens.c:2791
const char ** description(struct dt_iop_module_t *self)
Definition lens.c:924
int default_group()
Definition lens.c:934
static void _lens_rebuild_axis_rows(dt_iop_module_t *self)
Every axis, for a fresh image.
Definition lens.c:3469
static const char * _lens_type_name(int type)
A projection's name, replacing lfLens::GetLensTypeDesc().
Definition lens.c:2866
static void _lens_fill_vignette_row(float *const buf, const int width, const int ch)
Definition lens.c:1311
static int _lens_used_mask(dt_iop_module_t *self)
The lensfun modify mask this image allows: monochrome sensors get no TCA correction.
Definition lens.c:2222
static void _lens_build_data(dt_iop_module_t *self, const dt_iop_lensfun_params_t *const p, dt_iop_lensfun_data_t *d)
Build the lensfun state from resolved parameters. THE constructor.
Definition lens.c:2041
static int ptr_array_find_sorted(const GPtrArray *array, const void *item, GCompareFunc compare)
Definition lens.c:2567
static gboolean _lens_source_is_library_served(const dt_lens_source_t source)
Is this source served by the LensSerious path, once the data has been prepared?
Definition lens.c:497
static int _lens_mask_for_mono(const gboolean monochrome)
Which axes may be attempted on this image.
Definition lens.c:516
static int _lens_source_row(const dt_iop_lensfun_gui_data_t *g, const dt_lens_axis_t axis, const dt_lens_source_t source)
Show only the controls the current sources actually use.
Definition lens.c:3374
static void _lens_free_data(void *ptr)
Definition lens.c:2259
static void _lens_report_db_failure(const char *path, const ls_db_open_status_t status, const int found_version, const char *detail)
This thread's database handle, opened on first use.
Definition lens.c:668
static GtkWidget * _lens_add_axis_combobox(dt_iop_module_t *self, dt_iop_lensfun_gui_data_t *g, const dt_lens_axis_t axis, const char *label, const char *tooltip)
Build one axis's source combobox and pack it at the end of the module.
Definition lens.c:3520
void reload_defaults(dt_iop_module_t *module)
Definition lens.c:2368
static int _lens_axis_presence_bit(const dt_lens_axis_t axis)
The single bit that says whether an axis is on.
Definition lens.c:348
static int _lens_axis_shift(const dt_lens_axis_t axis)
How far up modify_flags this axis's two source bits sit.
Definition lens.c:354
dt_lens_modify_t
What modify_flags holds, bit by bit.
Definition lens.c:166
@ DT_LENS_MODIFY_GEOMETRY
Definition lens.c:171
@ DT_LENS_MODIFY_VIGNETTING
Definition lens.c:169
@ DT_LENS_SOURCE_SHIFT_VIGNETTING
Definition lens.c:177
@ DT_LENS_MODIFY_SCALE
Definition lens.c:172
@ DT_LENS_MODIFY_DISTORTION
Definition lens.c:170
@ DT_LENS_SOURCE_BITS
Definition lens.c:178
@ DT_LENS_SOURCE_SHIFT_TCA
Definition lens.c:175
@ DT_LENS_SOURCE_SHIFT_DISTORTION
Definition lens.c:176
@ DT_LENS_MODIFY_TCA
Definition lens.c:168
dt_lens_source_t
Where one axis takes its correction from. The same vocabulary for all three.
Definition lens.c:250
@ DT_LENS_SOURCE_MANUAL
Definition lens.c:254
@ DT_LENS_SOURCE_LAST
Definition lens.c:255
@ DT_LENS_SOURCE_LENSFUN
Definition lens.c:252
@ DT_LENS_SOURCE_EMBEDDED
Definition lens.c:253
@ DT_LENS_SOURCE_OFF
Definition lens.c:251
static void lens_comboentry_distance_update(GtkWidget *widget, dt_iop_module_t *self)
Definition lens.c:2850
static char * _lens_sanitize(const char *orig_lens)
Definition lens.c:1077
static int _lens_geometry_backtransform(const void *data, const dt_geometry_record_t *const record, dt_geometry_chain_t *chain, float *points, size_t points_count)
Definition lens.c:2305
static const dt_lens_source_t _lens_axis_sources[DT_LENS_AXIS_LAST][DT_LENS_SOURCE_LAST]
Definition lens.c:3358
static void lens_set(dt_iop_module_t *self, long long lens_id)
Show a lens in the panel, and offer the sources it makes available. VIEW ONLY – writes no parameter.
Definition lens.c:2904
static gboolean _lens_flags_move_pixels(const int modify_flags)
Does this flag word describe anything that MOVES pixels?
Definition lens.c:481
static void autoscale_pressed(GtkWidget *button, gpointer user_data)
Definition lens.c:3585
__DT_CLONE_TARGETS__ int process(dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid)
Definition lens.c:1336
static ls_db_t * _ls_db(void)
Definition lens.c:708
static int _lens_corrections_available(dt_iop_module_t *self, const dt_iop_lensfun_params_t *const p)
Which corrections this configuration will actually apply.
Definition lens.c:3612
static void camera_menu_select(GtkMenuItem *menuitem, gpointer user_data)
Definition lens.c:2690
static const dt_geometry_vtable_t _lens_geometry_vtable
Definition lens.c:2311
static gboolean _ls_find_camera(const char *maker, const char *model, ls_camera_t *out)
The camera an EXIF maker/model names.
Definition lens.c:767
const char * aliases()
Definition lens.c:919
static int _lens_axis_flags(const dt_lens_axis_t axis)
The enable bits for one axis. Three of them for distortion; see the pack.
Definition lens.c:330
static void lens_comboentry_focal_update(GtkWidget *widget, dt_iop_module_t *self)
Definition lens.c:2832
static void camera_menusearch_clicked(GtkWidget *button, gpointer user_data)
Definition lens.c:2813
static gboolean _lens_image_embeds(dt_iop_module_t *self, const dt_lens_axis_t axis)
Does THIS image carry a maker's profile for this axis?
Definition lens.c:535
static void lens_comboentry_aperture_update(GtkWidget *widget, dt_iop_module_t *self)
Definition lens.c:2841
static long long _ls_find_lens(long long mount_id, float crop, float focal, const char *lens_name)
The lens a free-text name names, as an id.
Definition lens.c:829
static void _lens_params_set_lens(dt_iop_module_t *self, const long long lens_id)
Write the lens the user picked into the params. USER INTERACTION ONLY.
Definition lens.c:2889
static void _lens_axis_source_changed(GtkWidget *widget, gpointer user_data)
Definition lens.c:3485
dt_iop_lensfun_modflag_t
Definition lens.c:284
@ LENSFUN_MODFLAG_MASK
Definition lens.c:287
static gboolean _lens_axis_enabled(const dt_iop_lensfun_params_t *p, const dt_lens_axis_t axis)
Is this axis correcting at all, from wherever?
Definition lens.c:467
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition lens.c:2333
static __DT_CLONE_TARGETS__ int get_modifier(int *mods_done, int w, int h, const dt_iop_lensfun_data_t *d, int mods_filter, gboolean force_inverse, ls_modifier_t *mod, ls_modifier_t *vig_mod)
Resolve the lens at one shooting configuration. THE modifier factory.
Definition lens.c:1125
dt_lens_axis_t
The correction axes, as an index rather than a bit – so a caller can loop.
Definition lens.c:225
@ DT_LENS_AXIS_DISTORTION
Definition lens.c:227
@ DT_LENS_AXIS_TCA
Definition lens.c:226
@ DT_LENS_AXIS_VIGNETTING
Definition lens.c:228
@ DT_LENS_AXIS_LAST
Definition lens.c:229
const char * name()
Definition lens.c:914
int distort_transform(dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, float *const __restrict points, size_t points_count)
Definition lens.c:1768
static gboolean _lens_source_applicable(const dt_lens_axis_t axis, const dt_lens_source_t source)
Whether source means anything on axis.
Definition lens.c:373
static void ptr_array_insert_index(GPtrArray *array, const void *item, int index)
Definition lens.c:2596
gboolean geometry_record(dt_iop_module_t *self, const void *params, dt_geometry_record_t *record)
Definition lens.c:2317
void gui_update(struct dt_iop_module_t *self)
Refresh GUI controls from current params and configuration.
Definition lens.c:3754
static int _lens_geometry_apply(const void *data, const dt_geometry_record_t *const record, float *points, size_t points_count, gboolean inverse)
Apply the correction to points. inverse selects the direction, as get_modifier() means it: distort_tr...
Definition lens.c:2268
static gboolean _lens_flags_have_axis(const int modify_flags, const dt_lens_axis_t axis)
Is this axis present in a flag word – typically get_modifier()'s done?
Definition lens.c:504
static int precision(double x, double adj)
Definition lens.c:2509
void gui_init(struct dt_iop_module_t *self)
Definition lens.c:3642
static void _lens_params_set_camera(dt_iop_module_t *self, const long long camera_id)
Write the camera the user picked into the params. USER INTERACTION ONLY.
Definition lens.c:2620
void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
Definition lens.c:3537
static dt_lens_axis_t _lens_axis_of_widget(const dt_iop_lensfun_gui_data_t *g, const GtkWidget *w)
Which axis a combobox belongs to, by widget identity.
Definition lens.c:3477
static long long * _lens_ids_for_camera(dt_iop_module_t *self, const char *model, int *out_n)
The lenses to offer for the camera currently shown, as ids.
Definition lens.c:3179
void tiling_callback(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, const struct dt_dev_pixelpipe_iop_t *piece, struct dt_develop_tiling_t *tiling)
Definition lens.c:1733
static int _lens_database_offers(dt_iop_module_t *self, const dt_iop_lensfun_params_t *p)
Which axes the lens DATABASE could correct for this image, whatever is selected.
Definition lens.c:3276
static void _lens_gui_update_sensitivity(dt_iop_module_t *self)
Definition lens.c:3303
void cleanup_global(dt_iop_module_so_t *module)
Definition lens.c:2488
static int _lens_geometry_transform(const void *data, const dt_geometry_record_t *const record, dt_geometry_chain_t *chain, float *points, size_t points_count)
Definition lens.c:2299
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition lens.c:949
static void lens_menu_fill(dt_iop_module_t *self, const long long *ids, int n)
Definition lens.c:3112
static dt_lens_source_t _lens_source_decode(const int modify_flags, const gboolean tca_override, const dt_lens_axis_t axis)
Decode one axis's source out of a raw flag word.
Definition lens.c:391
int flags()
Definition lens.c:944
static void lens_menusearch_clicked(GtkWidget *button, gpointer user_data)
Definition lens.c:3224
static float get_autoscale(dt_iop_module_t *self, dt_iop_lensfun_params_t *p)
Definition lens.c:3554
static GPrivate _ls_tls_key
Definition lens.c:635
void modify_roi_in(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, struct dt_dev_pixelpipe_iop_t *piece, const dt_iop_roi_t *const roi_out, dt_iop_roi_t *roi_in)
Definition lens.c:1903
void gui_cleanup(struct dt_iop_module_t *self)
Definition lens.c:3816
dt_lens_type_t
Definition lens.c:259
@ DT_LENS_RECTILINEAR
Definition lens.c:261
@ DT_LENS_FISHEYE_THOBY
Definition lens.c:268
@ DT_LENS_FISHEYE_EQUISOLID
Definition lens.c:267
@ DT_LENS_FISHEYE_ORTHOGRAPHIC
Definition lens.c:265
@ DT_LENS_UNKNOWN
Definition lens.c:260
@ DT_LENS_FISHEYE
Definition lens.c:262
@ DT_LENS_PANORAMIC
Definition lens.c:263
@ DT_LENS_FISHEYE_STEREOGRAPHIC
Definition lens.c:266
@ DT_LENS_EQUIRECTANGULAR
Definition lens.c:264
static int ptr_array_insert_sorted(GPtrArray *array, const void *item, GCompareFunc compare)
Definition lens.c:2533
static void lens_menu_select(GtkMenuItem *menuitem, gpointer user_data)
Definition lens.c:3083
static void _lens_rebuild_axis_row(dt_iop_module_t *self, const dt_lens_axis_t axis)
Rebuild each axis's combobox to hold exactly the sources THIS image can supply.
Definition lens.c:3398
static gboolean _lens_data_available(const dt_iop_lensfun_data_t *d)
Does this pipe data hold ANY resolvable correction source?
Definition lens.c:908
static void target_geometry_changed(GtkWidget *widget, gpointer user_data)
Definition lens.c:3242
static void parse_model(const char *txt, char *model, size_t sz_model)
Definition lens.c:2804
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition lens.c:2339
static dt_lens_source_t _lens_source_get(const dt_iop_lensfun_params_t *p, const dt_lens_axis_t axis)
Where this axis takes its correction from.
Definition lens.c:414
static dt_lens_source_t _lens_source_displayed(const dt_iop_lensfun_gui_data_t *g, const dt_iop_lensfun_params_t *p, const dt_lens_axis_t axis)
The source the panel is SHOWING for this axis.
Definition lens.c:3295
static void camera_set(dt_iop_module_t *self, long long camera_id)
Show a camera in the panel. VIEW ONLY – writes no parameter.
Definition lens.c:2645
static void delete_children(GtkWidget *widget, gpointer data)
Definition lens.c:2859
static void _ls_tls_free(gpointer data)
Definition lens.c:627
void init_global(dt_iop_module_so_t *module)
Definition lens.c:2350
#define DT_LENS_MODIFY_ALL_AXES
Definition lens.c:205
static gboolean _lens_source_is(const dt_iop_lensfun_params_t *p, const dt_lens_axis_t axis, const dt_lens_source_t source)
Is this axis taking its correction from source?
Definition lens.c:459
static const dt_iop_lensfun_params_t * _lens_effective_params(dt_iop_module_t *self, const dt_iop_lensfun_params_t *const p)
Which parameters are actually in force.
Definition lens.c:2028
int process_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out)
Definition lens.c:1584
#define DT_LENS_MODIFY_DISTORTION_PACK
The three lensfun axes that always move together, as one flag.
Definition lens.c:191
static _ls_tls_t * _ls_tls_get(void)
This thread's cache block, allocated on first use. NULL only if that allocation failed,...
Definition lens.c:639
void modify_roi_out(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, struct dt_dev_pixelpipe_iop_t *piece, dt_iop_roi_t *roi_out, const dt_iop_roi_t *roi_in)
Definition lens.c:1896
static void _lens_source_set(dt_iop_lensfun_params_t *p, const dt_lens_axis_t axis, dt_lens_source_t source)
Point one axis at a source, leaving every other axis alone.
Definition lens.c:431
int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params, const int new_version)
Definition lens.c:954
static void camera_menu_fill(dt_iop_module_t *self, const long long *ids, int n)
Build the camera picker from a list of database ids.
Definition lens.c:2725
_lib_location_type_t type
Definition location.c:1
@ DT_DEBUG_OPENCL
Definition logging.h:57
@ DT_DEBUG_PIPE
Definition logging.h:76
@ DT_DEBUG_ALWAYS
Definition logging.h:49
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
Definition mem_alloc.h:214
static void * dt_calloc_align(size_t size)
dt_alloc_align() followed by a zero fill.
Definition mem_alloc.h:225
#define DT_ALIGNED_ARRAY
Align an object on a cacheline boundary, so AVX2 can load it whole.
Definition mem_alloc.h:80
static void dt_free_gpointer(gpointer ptr)
g_free() one pointer, with the signature GDestroyNotify wants.
Definition mem_alloc.h:184
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
char * key
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
DT_MODULE() for a module whose params struct is introspected.
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
Definition opencl.c:2554
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2448
int dt_opencl_enqueue_copy_image(const int devid, cl_mem src, cl_mem dst, size_t *orig_src, size_t *orig_dst, size_t *region)
Definition opencl.c:2679
void dt_opencl_free_kernel(const int kernel)
Definition opencl.c:2491
int dt_opencl_set_kernel_arg(const int dev, const int kernel, const int num, const size_t size, const void *arg)
Definition opencl.c:2545
#define ROUNDUPDHT(a, b)
Definition opencl.h:86
#define ROUNDUPDWD(a, b)
Definition opencl.h:85
#define __OMP_FOR__(...)
Definition openmp.h:98
#define __OMP_PARALLEL_FOR__(...)
Definition openmp.h:95
#define DT_PATH_MAX
Buffer size for a filesystem path anywhere in Ansel.
Definition paths.h:57
void dt_concat_path_file(char destination[4096], const char path[4096], const char *const file)
Append a constant filename to a variable, stack-based, fixed-sized, directory, and add a / in-between...
Definition darktable.c:2674
@ DT_DEV_PIXELPIPE_FULL
Definition pixelpipe.h:43
#define dt_pixelpipe_cache_alloc_align_cache(size, id)
#define dt_pixelpipe_cache_free_align(mem)
#define dt_get_perthread(buf, padsize)
#define dt_pixelpipe_cache_alloc_perthread_float(n, padded_size)
void dt_gui_menu_popup(GtkMenu *menu, GtkWidget *button, GdkGravity widget_anchor, GdkGravity menu_anchor)
Definition popup.c:53
#define DT_PIXEL_SIMD_CHANNELS
Definition simd.h:63
const float r
char lens_key[512]
Definition lens.c:618
ls_camera_t cam
Definition lens.c:614
ls_db_t * db
Definition lens.c:610
gboolean cam_cached
Definition lens.c:616
long long lens_id
Definition lens.c:619
gboolean lens_cached
Definition lens.c:620
gboolean tried
Definition lens.c:611
gboolean cam_found
Definition lens.c:615
char cam_key[512]
Definition lens.c:613
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
dt_image_t image_storage
Definition develop.h:225
GList * forms
Definition develop.h:304
One module instance's contribution, as data.
Definition geometry.h:99
dt_iop_roi_t in
Definition geometry.h:117
const dt_geometry_vtable_t * vtable
Definition geometry.h:110
void(* free_data)(void *data)
Definition geometry.h:112
A module's geometry, evaluated. Pure functions of the record's own data.
Definition geometry.h:75
int32_t height
Definition image.h:397
float exif_focus_distance
Definition image.h:372
ls_vendor_data_t exif_correction
Definition image.h:469
float exif_aperture
Definition image.h:369
int32_t crop_height
Definition image.h:398
int32_t width
Definition image.h:397
float exif_focal_length
Definition image.h:371
char exif_maker[64]
Definition image.h:374
int32_t crop_y
Definition image.h:398
char exif_lens[128]
Definition image.h:376
int32_t crop_x
Definition image.h:398
int32_t p_height
Definition image.h:397
int32_t p_width
Definition image.h:397
char exif_model[64]
Definition image.h:375
int32_t crop_width
Definition image.h:398
float exif_crop
Definition image.h:373
enum dt_interpolation_type id
unsigned int channels
Definition format.h:83
dt_iop_lensfun_data_t data
Definition lens.c:2255
ls_knots_t ls_knots
Definition lens.c:890
ls_lens_t ls_lens
Definition lens.c:884
gboolean ls_have
Definition lens.c:885
dt_lens_type_t target_geom
Definition lens.c:878
gboolean do_nan_checks
Definition lens.c:879
gboolean tca_override
Definition lens.c:880
gboolean knots_have
Definition lens.c:891
dt_lens_source_t axis_row[DT_LENS_AXIS_LAST][DT_LENS_SOURCE_LAST]
Definition lens.c:564
GtkMenu * camera_menu
Definition lens.c:556
int axis_rows[DT_LENS_AXIS_LAST]
Definition lens.c:565
GtkWidget * target_geom
Definition lens.c:566
GtkWidget * tca_r
Definition lens.c:566
GtkWidget * cbe[3]
Definition lens.c:554
GtkWidget * axis_source[DT_LENS_AXIS_LAST]
Definition lens.c:560
GtkWidget * lens_param_box
Definition lens.c:553
GtkWidget * reverse
Definition lens.c:566
GtkWidget * scale
Definition lens.c:566
GtkWidget * lens_model
Definition lens.c:557
GtkWidget * tca_b
Definition lens.c:566
GtkWidget * camera_model
Definition lens.c:555
gboolean tca_override
Definition lens.c:303
dt_lens_type_t target_geom
Definition lens.c:300
GtkWidget * widget
Definition imageop_gui.h:47
dt_iop_global_data_t * data
Definition imageop.h:238
dt_iop_params_t * default_params
Definition imageop.h:333
struct dt_iop_module_gui_t * gui
Definition imageop.h:346
struct dt_develop_t * dev
Definition imageop.h:311
dt_iop_global_data_t * global_data
Definition imageop.h:337
dt_iop_params_t * params
Definition imageop.h:333
Region of interest passed through the pixelpipe.
Definition format.h:49
double scale
Definition format.h:51
int width
Definition format.h:50
int height
Definition format.h:50
#define __DT_CLONE_TARGETS__
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29
gboolean dt_gui_widgets_suppressed(void)
#define dt_gui_widget_freeze()
#define DT_GUI_BOX_SPACING
void dtgtk_cairo_paint_refresh(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)