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 Copyright (C) 2026 Guillaume Stutin.
55
56 darktable is free software: you can redistribute it and/or modify
57 it under the terms of the GNU General Public License as published by
58 the Free Software Foundation, either version 3 of the License, or
59 (at your option) any later version.
60
61 darktable is distributed in the hope that it will be useful,
62 but WITHOUT ANY WARRANTY; without even the implied warranty of
63 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
64 GNU General Public License for more details.
65
66 You should have received a copy of the GNU General Public License
67 along with darktable. If not, see <http://www.gnu.org/licenses/>.
68*/
70#include "common/paths.h" // DT_PATH_MAX
71#include "common/utility.h"
72#include "system/macros.h"
74#include "common/logging.h"
75#include "system/mem_alloc.h"
76#include "system/openmp.h"
79#include "glib.h"
80
81#ifdef HAVE_CONFIG_H
82#include "config.h"
83#endif
84#include "widgets/bauhaus.h"
85#include "pixel/interpolation.h"
87#include "common/imagebuf.h"
88#include "common/opencl.h"
89#include "develop/develop.h"
90#include "develop/imageop.h"
91#include "develop/imageop_gui.h"
92#include "develop/tiling.h"
93
94#include "iop/iop_api.h"
95#include <assert.h>
96#include <ctype.h>
97#include <gtk/gtk.h>
98#include <inttypes.h>
99#include <math.h>
100#include <stdlib.h>
101#include <string.h>
102
103#include "widgets/popup.h"
104#include "widgets/widget_style.h"
105#include "control/signal.h"
106
108
109#include "lensserious.h" // side-by-side latch against lensfun, see feat/lensserious
110#include "lensserious_db.h" // ... and its calibration database, latched the same way
111#include "lensserious_vendor.h"
112
113
114/* The correction axes and the projection numbering.
115 *
116 * These values are lensfun's, and they must stay lensfun's: `modify_flags` and
117 * `target_geom` are SERIALIZED into every user's history and every preset that has ever
118 * been saved. Defining them here rather than including them is what lets liblensfun go
119 * without rewriting anyone's edits.
120 *
121 * The projection numbering is also, entry for entry, ls_lens_type_t's -- asserted below
122 * rather than assumed, because the two now live in different repositories and nothing else
123 * would notice one of them growing a member in the middle. */
167{
168 /* --- low half: lensfun's serialized axis numbering, unchangeable --- */
169 DT_LENS_MODIFY_TCA = 0x00000001,
174
175 /* --- high half: how far up sits each axis's two-bit source field --- */
181
192#define DT_LENS_MODIFY_DISTORTION_PACK \
193 (DT_LENS_MODIFY_DISTORTION | DT_LENS_MODIFY_GEOMETRY | DT_LENS_MODIFY_SCALE)
194
196#define DT_LENS_MODIFY_ANSEL_MASK 0xFF000000u
197
206#define DT_LENS_MODIFY_ALL_AXES ((int)~DT_LENS_MODIFY_ANSEL_MASK)
207
208/* The boundary, checked rather than described. Each of these is a way the two halves could
209 * come to overlap -- a sixth axis assigned too high, a source field assigned too low, a
210 * field running into the sign bit -- and each would corrupt the other half's meaning in a
211 * way that only shows up as a wrong correction on someone's photograph. */
212_Static_assert((DT_LENS_MODIFY_ALL_AXES & (int)DT_LENS_MODIFY_ANSEL_MASK) == 0,
213 "the axis space and Ansel's own bits must not overlap");
217 "every correction axis must live in the axis half");
218_Static_assert((DT_LENS_SOURCE_BITS << DT_LENS_SOURCE_SHIFT_VIGNETTING) > 0,
219 "no source field may reach the sign bit of modify_flags");
222 "the source fields must not overlap each other");
223
232
258
271
272_Static_assert((int)DT_LENS_RECTILINEAR == (int)LS_LENS_RECTILINEAR
273 && (int)DT_LENS_FISHEYE == (int)LS_LENS_FISHEYE
274 && (int)DT_LENS_PANORAMIC == (int)LS_LENS_PANORAMIC
275 && (int)DT_LENS_EQUIRECTANGULAR == (int)LS_LENS_EQUIRECTANGULAR
276 && (int)DT_LENS_FISHEYE_ORTHOGRAPHIC == (int)LS_LENS_FISHEYE_ORTHOGRAPHIC
277 && (int)DT_LENS_FISHEYE_STEREOGRAPHIC == (int)LS_LENS_FISHEYE_STEREOGRAPHIC
278 && (int)DT_LENS_FISHEYE_EQUISOLID == (int)LS_LENS_FISHEYE_EQUISOLID
279 && (int)DT_LENS_FISHEYE_THOBY == (int)LS_LENS_FISHEYE_THOBY,
280 "projection numbering must match ls_lens_type_t: stored params depend on it");
281
283
285{
286 /* The three axes a user can actually see corrected. Geometry and scale are not in it:
287 * they are how the correction is presented, not whether a lens flaw was fixed. */
290
291
293{
295 int inverse; // $MIN: 0 $MAX: 1 $DEFAULT: 0 $DESCRIPTION: "mode"
296 float scale; // $MIN: 0.1 $MAX: 2.0 $DEFAULT: 1.0
297 float crop;
298 float focal;
299 float aperture;
300 float distance;
301 dt_lens_type_t target_geom; // $DEFAULT: DT_LENS_RECTILINEAR $DESCRIPTION: "geometry"
302 char camera[128];
303 char lens[128];
304 gboolean tca_override; // $DEFAULT: FALSE $DESCRIPTION: "TCA overwrite"
305 float tca_r; // $MIN: 0.99 $MAX: 1.01 $DEFAULT: 1.0 $DESCRIPTION: "TCA red"
306 float tca_b; // $MIN: 0.99 $MAX: 1.01 $DEFAULT: 1.0 $DESCRIPTION: "TCA blue"
311 int modified; // $DEFAULT: 1
313
314/* ========================================================================================
315 * The modify_flags interface.
316 *
317 * Everything below this banner and above the next one is the only code in this file allowed
318 * to touch a bit of modify_flags. Everything else asks in terms of an axis and a source.
319 *
320 * That rule is not tidiness. The encoding has three traps, and each has already been walked
321 * into once: the source fields are two bits at a shift that is not the axis's bit position;
322 * the distortion "bit" is really three bits, so clearing the obvious one leaves projection
323 * and scaling running with their controls hidden; and manual TCA is ALSO recorded in a
324 * separate legacy boolean, so writing one without the other makes an edit mean two different
325 * things depending on which the reader trusts.
326 * ===================================================================================== */
327
331static inline int _lens_axis_flags(const dt_lens_axis_t axis)
332{
333 switch(axis)
334 {
338 default: return 0;
339 }
340}
341
349static inline int _lens_axis_presence_bit(const dt_lens_axis_t axis)
350{
352}
353
355static inline int _lens_axis_shift(const dt_lens_axis_t axis)
356{
357 switch(axis)
358 {
362 default: return DT_LENS_SOURCE_SHIFT_TCA;
363 }
364}
365
374static inline gboolean _lens_source_applicable(const dt_lens_axis_t axis,
375 const dt_lens_source_t source)
376{
377 if(source == DT_LENS_SOURCE_MANUAL) return axis == DT_LENS_AXIS_TCA;
378 return source > DT_LENS_SOURCE_OFF && source < DT_LENS_SOURCE_LAST;
379}
380
392static dt_lens_source_t _lens_source_decode(const int modify_flags,
393 const gboolean tca_override,
394 const dt_lens_axis_t axis)
395{
396 if(!(modify_flags & _lens_axis_presence_bit(axis))) return DT_LENS_SOURCE_OFF;
397
398 dt_lens_source_t source
399 = (dt_lens_source_t)((modify_flags >> _lens_axis_shift(axis)) & DT_LENS_SOURCE_BITS);
400
401 /* Zero is the database, because that is what every edit written before this decoded to. */
402 if(source == DT_LENS_SOURCE_OFF) source = DT_LENS_SOURCE_LENSFUN;
403
404 if(axis == DT_LENS_AXIS_TCA && source == DT_LENS_SOURCE_LENSFUN && tca_override)
405 source = DT_LENS_SOURCE_MANUAL;
406
407 /* A value this build has no meaning for -- a newer Ansel wrote it, or the field was
408 * corrupted. Correcting with the database beats refusing to correct at all. */
409 if(!_lens_source_applicable(axis, source)) source = DT_LENS_SOURCE_LENSFUN;
410
411 return source;
412}
413
416 const dt_lens_axis_t axis)
417{
418 return _lens_source_decode(p->modify_flags, p->tca_override, axis);
419}
420
433 dt_lens_source_t source)
434{
435 if(!_lens_source_applicable(axis, source)) source = DT_LENS_SOURCE_OFF;
436
437 p->modify_flags &= ~(DT_LENS_SOURCE_BITS << _lens_axis_shift(axis));
438
439 if(source == DT_LENS_SOURCE_OFF)
440 {
441 /* The whole pack for distortion: leaving GEOMETRY and SCALE set is what used to keep a
442 * projection change and a zoom running after their correction had been switched off. */
443 p->modify_flags &= ~_lens_axis_flags(axis);
444 /* The source bits stay cleared, so an axis switched off and on again comes back as the
445 * database rather than as whatever it was before. */
446 }
447 else
448 {
449 p->modify_flags |= _lens_axis_flags(axis);
450 p->modify_flags |= ((int)source & DT_LENS_SOURCE_BITS) << _lens_axis_shift(axis);
451 }
452
453 /* Kept in step rather than left to rot: an older Ansel reading this edit sees only
454 * tca_override, and it should still see the truth. */
455 if(axis == DT_LENS_AXIS_TCA)
456 p->tca_override = (source == DT_LENS_SOURCE_MANUAL) ? TRUE : FALSE;
457}
458
460static inline gboolean _lens_source_is(const dt_iop_lensfun_params_t *p,
461 const dt_lens_axis_t axis,
462 const dt_lens_source_t source)
463{
464 return _lens_source_get(p, axis) == source;
465}
466
468static inline gboolean _lens_axis_enabled(const dt_iop_lensfun_params_t *p,
469 const dt_lens_axis_t axis)
470{
471 return _lens_source_get(p, axis) != DT_LENS_SOURCE_OFF;
472}
473
482static inline gboolean _lens_flags_move_pixels(const int modify_flags)
483{
484 return (modify_flags & (DT_LENS_MODIFY_TCA | DT_LENS_MODIFY_DISTORTION_PACK)) != 0;
485}
486
498static inline gboolean _lens_source_is_library_served(const dt_lens_source_t source)
499{
500 return source == DT_LENS_SOURCE_LENSFUN || source == DT_LENS_SOURCE_MANUAL
501 || source == DT_LENS_SOURCE_EMBEDDED;
502}
503
505static inline gboolean _lens_flags_have_axis(const int modify_flags, const dt_lens_axis_t axis)
506{
507 return (modify_flags & _lens_axis_presence_bit(axis)) != 0;
508}
509
517static inline int _lens_mask_for_mono(const gboolean monochrome)
518{
519 return monochrome ? (DT_LENS_MODIFY_ALL_AXES & ~DT_LENS_MODIFY_TCA) : DT_LENS_MODIFY_ALL_AXES;
520}
521
522
523/* ===================================================================================== */
524
536static gboolean _lens_image_embeds(dt_iop_module_t *self, const dt_lens_axis_t axis)
537{
538 if(IS_NULL_PTR(self->dev)) return FALSE;
539 const int axes = ls_vendor_axes(&self->dev->image_storage.exif_correction);
540
541 switch(axis)
542 {
543 case DT_LENS_AXIS_DISTORTION: return (axes & LS_ENABLE_DISTORTION) != 0;
544 case DT_LENS_AXIS_TCA: return (axes & LS_ENABLE_TCA) != 0;
545 case DT_LENS_AXIS_VIGNETTING: return (axes & LS_ENABLE_VIGNETTING) != 0;
546 default: return FALSE;
547 }
548}
549
569
570/* Defined with the widgets they act on, at the bottom of the GUI section. Declared here
571 * because every setter that changes what the database can match -- the camera, the lens --
572 * has to say so, and those sit above them. */
575
583
584/* ---------------------------------------------------------------------------------------
585 * The LensSerious calibration database, latched BESIDE liblensfun's rather than replacing
586 * it.
587 *
588 * Every lookup below is answered twice -- once by liblensfun, once by LensSerious -- and
589 * both answers are printed. lensfun keeps authoring the pixels; nothing here changes what
590 * is rendered. The point is to run the real GUI on real images and read the log, because
591 * the disagreements that matter are not the ones a synthetic harness reaches: the harness
592 * walks the database, and this walks whatever a user's EXIF actually says, spelled however
593 * the camera spelled it.
594 *
595 * Reading is lock-free by construction -- `mode=ro&immutable=1` with SQLITE_OPEN_NOMUTEX,
596 * so SQLite takes no file lock, no shared-memory segment and no mutex. The price is ONE
597 * HANDLE PER THREAD, so the handle is thread-local and closed by its destructor when the
598 * thread ends, or by cleanup_global() for the thread unloading the modules. Threads that never
599 * touch a lens never open it.
600 *
601 * The one-entry caches beside it stand in for the two process-wide memo hash tables:
602 * commit_params() resolves the camera and the lens on every pipe resync, for every pipe,
603 * and asks the SAME question every time -- an image's camera and lens do not change while
604 * it is open. A per-thread cache of the last answer serves that exactly, with no lock and
605 * no unbounded growth, where a shared table would need a mutex back.
606 * ------------------------------------------------------------------------------------ */
607typedef struct _ls_tls_t
608{
609 ls_db_t *db;
610 gboolean tried;
611
612 char cam_key[512];
613 ls_camera_t cam;
614 gboolean cam_found;
615 gboolean cam_cached;
616
617 char lens_key[512];
618 long long lens_id;
619 gboolean lens_cached;
621
622/* Closed when the thread that opened it exits, which is the whole reason this is a GPrivate
623 * and not a plain __thread pointer: the handle has to be RELEASED, and nothing else in C
624 * runs code at thread exit. iop/drawlayer.c holds its per-thread scratch buffers the same
625 * way, for the same reason. The main thread ends in exit(), which runs no such destructor:
626 * cleanup_global() releases the block of the thread that unloads the modules. */
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
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 }
1453
1454 // Alpha too: it is 0 unless the mask is displayed, and left unwritten it would be
1455 // whatever the output buffer last held.
1457 else for(int c = 0; c < ch; c++) out[c] = pixel[c];
1458 }
1459 }
1461 }
1462 else
1463 {
1464 dt_iop_image_copy_by_size((float*)ovoid, (float*)ivoid, roi_out->width, roi_out->height, ch);
1465
1466 /* Nothing moved, so there was no resampling loop to fold the falloff into. */
1467 if(have_vig)
1468 {
1469 __OMP_PARALLEL_FOR__(firstprivate(modifier, ovoid, roi_out, ch))
1470 for(int y = 0; y < roi_out->height; y++)
1471 {
1472 float *out = ((float *)ovoid) + (size_t)y * roi_out->width * ch;
1473 ls_modifier_apply_vignetting(&modifier, roi_out->x, roi_out->y + y, roi_out->width, 1,
1474 out, (int)((ch * roi_out->width) * sizeof(float)));
1475 }
1476 }
1477 }
1478 }
1479 else // correct distortions:
1480 {
1481 /* No copy of the input, and no separate vignetting pass over it. This used to
1482 * duplicate the whole frame -- 387 MB for a 24 Mpx RGBA buffer -- darken the copy, and
1483 * resample from it. The falloff is a per-source-pixel gain, so folding it into the
1484 * resampling loop below gives the same answer while reading the caller's own buffer. */
1485
1486 if(_lens_flags_move_pixels(modflags))
1487 {
1488 // acquire temp memory for distorted pixel coords
1489 const size_t buf2size = (size_t)roi_out->width * 2 * 3;
1490 size_t padded_buf2size;
1491 float *const buf2 = dt_pixelpipe_cache_alloc_perthread_float(buf2size, &padded_buf2size);
1492 if(IS_NULL_PTR(buf2)) return 1;
1493
1494
1495#ifdef _OPENMP
1496#pragma omp parallel for default(none) \
1497 firstprivate(roi_out, roi_in, ovoid, ivoid, ch, padded_buf2size, modifier, mask_display, raw_monochrome, interpolation, ch_width, d, buf2, have_vig, vp)
1498#endif
1499 for(int y = 0; y < roi_out->height; y++)
1500 {
1501 float *buf2ptr = (float*)dt_get_perthread(buf2, padded_buf2size);
1502 ls_modifier_apply_subpixel_geometry(&modifier, roi_out->x, roi_out->y + y, roi_out->width, 1, buf2ptr);
1503 // reverse transform the global coords from lf to our buffer
1504 float *out = ((float *)ovoid) + (size_t)y * roi_out->width * ch;
1505 for(int x = 0; x < roi_out->width; x++, buf2ptr += 6, out += ch)
1506 {
1507 dt_aligned_pixel_simd_t pixel = { 0.f };
1508 for(int c = 0; c < 3; c++)
1509 {
1510 if(d->do_nan_checks && (!isfinite(buf2ptr[c * 2]) || !isfinite(buf2ptr[c * 2 + 1])))
1511 {
1512 pixel[c] = 0.0f;
1513 continue;
1514 }
1515
1516 const float *bufptr = ((const float *)ivoid) + c;
1517 const float pi0 = fmaxf(fminf(buf2ptr[c * 2] - roi_in->x, roi_in->width - 1.0f), 0.0f);
1518 const float pi1 = fmaxf(fminf(buf2ptr[c * 2 + 1] - roi_in->y, roi_in->height - 1.0f), 0.0f);
1519 pixel[c] = dt_interpolation_compute_sample(interpolation, bufptr, pi0, pi1, roi_in->width,
1520 roi_in->height, ch, ch_width);
1521 /* Correcting: the falloff belongs to the source, so each channel takes it at
1522 * its own source coordinate -- which is what sampling an already-darkened input
1523 * amounted to. */
1524 if(have_vig)
1525 pixel[c] *= ls_eval_vignette_factor(&vp, buf2ptr[c * 2], buf2ptr[c * 2 + 1]);
1526 }
1527 if(raw_monochrome) pixel[0] = pixel[2] = pixel[1];
1528 if(mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK)
1529 {
1530 if(d->do_nan_checks && (!isfinite(buf2ptr[2]) || !isfinite(buf2ptr[3])))
1531 {
1532 pixel[3] = 0.0f;
1533 }
1534 else
1535 {
1536 // take green channel distortion also for alpha channel
1537 const float *bufptr = ((const float *)ivoid) + 3;
1538 const float pi0 = fmaxf(fminf(buf2ptr[2] - roi_in->x, roi_in->width - 1.0f), 0.0f);
1539 const float pi1 = fmaxf(fminf(buf2ptr[3] - roi_in->y, roi_in->height - 1.0f), 0.0f);
1540 pixel[3] = dt_interpolation_compute_sample(interpolation, bufptr, pi0, pi1, roi_in->width,
1541 roi_in->height, ch, ch_width);
1542 }
1543 }
1544
1545 // Alpha too: it is 0 unless the mask is displayed, and left unwritten it would be
1546 // whatever the output buffer last held.
1548 else for(int c = 0; c < ch; c++) out[c] = pixel[c];
1549 }
1550 }
1552 }
1553 else
1554 {
1555 dt_iop_image_copy_by_size((float *)ovoid, (float *)ivoid, roi_out->width, roi_out->height, ch);
1556
1557 /* Nothing moved, so there was no resampling loop to fold the falloff into. */
1558 if(have_vig)
1559 {
1560 __OMP_PARALLEL_FOR__(firstprivate(modifier, ovoid, roi_in, ch))
1561 for(int y = 0; y < roi_in->height; y++)
1562 {
1563 float *out = ((float *)ovoid) + (size_t)ch * roi_in->width * y;
1564 ls_modifier_apply_vignetting(&modifier, roi_in->x, roi_in->y + y, roi_in->width, 1, out,
1565 (int)((ch * roi_in->width) * sizeof(float)));
1566 }
1567 }
1568 }
1569 }
1570
1571 /* No GUI state is written here. Which corrections apply is a property of the
1572 * camera/lens/params combination, not of a rendered frame -- the label is computed on the
1573 * GUI thread by _lens_corrections_available(). */
1574 return 0;
1575}
1576
1577#ifdef HAVE_OPENCL
1578
1579
1580int 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)
1581{
1582 const dt_iop_roi_t *const roi_in = &piece->roi_in;
1583 const dt_iop_roi_t *const roi_out = &piece->roi_out;
1585
1586 const gboolean raw_monochrome = dt_image_is_monochrome(&self->dev->image_storage);
1587 const int used_lf_mask = _lens_mask_for_mono(raw_monochrome);
1588
1589 cl_int err = -999;
1590
1592 ls_modifier_t modifier;
1593 /* Declared before the first `goto error`: C++ forbids jumping over an initialisation. */
1594 ls_eval_t p;
1595 gboolean have_eval = FALSE, do_geom = FALSE, do_vig = FALSE;
1596
1597 const int devid = pipe->devid;
1598 const int iwidth = roi_in->width;
1599 const int iheight = roi_in->height;
1600 const int owidth = roi_out->width;
1601 const int oheight = roi_out->height;
1602 const int roi_in_x = roi_in->x;
1603 const int roi_in_y = roi_in->y;
1604 const int roi_out_x = roi_out->x;
1605 const int roi_out_y = roi_out->y;
1606 const int mask_display = (pipe->mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK) != 0;
1607
1608 const float orig_w = roi_in->scale * piece->buf_in.width, orig_h = roi_in->scale * piece->buf_in.height;
1609
1610 size_t origin[] = { 0, 0, 0 };
1611 size_t oregion[] = { (size_t)owidth, (size_t)oheight, 1 };
1612 size_t isizes[] = { (size_t)ROUNDUPDWD(iwidth, devid), (size_t)ROUNDUPDHT(iheight, devid), 1 };
1613 size_t osizes[] = { (size_t)ROUNDUPDWD(owidth, devid), (size_t)ROUNDUPDHT(oheight, devid), 1 };
1614
1615 int modflags;
1616 int ldkernel = -1;
1617 /* Declared here, ahead of every `goto error`: C++ will not let one jump over an
1618 * initialisation. Resolved once below, after get_modifier() has settled modflags. */
1620
1622 {
1623 err = dt_opencl_enqueue_copy_image(devid, dev_in, dev_out, origin, origin, oregion);
1624 if(err != CL_SUCCESS) goto error;
1625 return TRUE;
1626 }
1627
1628 switch(interpolation->id)
1629 {
1631 ldkernel = gd->kernel_lens_distort_bilinear;
1632 break;
1634 ldkernel = gd->kernel_lens_distort_bicubic;
1635 break;
1637 ldkernel = gd->kernel_lens_distort_mitchell;
1638 break;
1639 default:
1640 return FALSE;
1641 }
1642
1643
1644 ls_modifier_t vig_modifier;
1645 get_modifier(&modflags, orig_w, orig_h, d, used_lf_mask, FALSE, &modifier, &vig_modifier);
1646
1647 /* One kernel, in and out, in both directions.
1648 *
1649 * The correction crosses as an ls_eval_t -- 632 bytes of coefficients passed by value --
1650 * and each work-item evaluates its own source coordinates from it, so there is no
1651 * displacement map, no host buffer and no upload. Vignetting rides along inside the same
1652 * resampling pass rather than writing a whole intermediate image for the resampler to
1653 * read back.
1654 *
1655 * The direction lives in the block: ls_eval_map() reads p.reverse and composes the chain
1656 * accordingly, and _lens_devignette() places the falloff in the frame that direction puts
1657 * it in. So both directions are the same launch, which is why the branch that used to
1658 * distinguish them is gone. */
1659 have_eval = ls_eval_from_modifier(&modifier, &p) != 0;
1660
1661 /* One block serves both halves here, unlike the CPU path, so where the two axes took
1662 * different sources the falloff has to be grafted in from its own modifier. The two
1663 * halves of ls_eval_t share no field -- vignetting carries its own scale, centre and
1664 * model -- which is what makes the graft safe across resolvers that normalise radius
1665 * differently. A second block would be the obvious alternative and does not fit: 632
1666 * bytes each against the 1024 OpenCL 1.2 guarantees for a kernel's whole argument list. */
1667 if(have_eval)
1668 {
1669 ls_eval_t vp;
1670 if(ls_eval_from_modifier(&vig_modifier, &vp)) ls_eval_adopt_vignetting(&p, &vp);
1671 }
1672 do_geom = have_eval
1673 && _lens_flags_move_pixels(modflags);
1674 do_vig = have_eval && _lens_flags_have_axis(modflags, DT_LENS_AXIS_VIGNETTING) != 0;
1675
1676 if(do_geom)
1677 {
1678 /* Vignetting, if any, is applied inside this pass -- the kernel reads it out of p. */
1679 dt_opencl_set_kernel_arg(devid, ldkernel, 0, sizeof(cl_mem), (void *)&dev_in);
1680 dt_opencl_set_kernel_arg(devid, ldkernel, 1, sizeof(cl_mem), (void *)&dev_out);
1681 dt_opencl_set_kernel_arg(devid, ldkernel, 2, sizeof(int), (void *)&owidth);
1682 dt_opencl_set_kernel_arg(devid, ldkernel, 3, sizeof(int), (void *)&oheight);
1683 dt_opencl_set_kernel_arg(devid, ldkernel, 4, sizeof(int), (void *)&iwidth);
1684 dt_opencl_set_kernel_arg(devid, ldkernel, 5, sizeof(int), (void *)&iheight);
1685 dt_opencl_set_kernel_arg(devid, ldkernel, 6, sizeof(int), (void *)&roi_in_x);
1686 dt_opencl_set_kernel_arg(devid, ldkernel, 7, sizeof(int), (void *)&roi_in_y);
1687 dt_opencl_set_kernel_arg(devid, ldkernel, 8, sizeof(int), (void *)&roi_out_x);
1688 dt_opencl_set_kernel_arg(devid, ldkernel, 9, sizeof(int), (void *)&roi_out_y);
1689 dt_opencl_set_kernel_arg(devid, ldkernel, 10, sizeof(ls_eval_t), (void *)&p);
1690 dt_opencl_set_kernel_arg(devid, ldkernel, 11, sizeof(int), (void *)&(d->do_nan_checks));
1691 dt_opencl_set_kernel_arg(devid, ldkernel, 12, sizeof(int), (void *)&(raw_monochrome));
1692 dt_opencl_set_kernel_arg(devid, ldkernel, 13, sizeof(int), &mask_display);
1693 err = dt_opencl_enqueue_kernel_2d(devid, ldkernel, osizes);
1694 if(err != CL_SUCCESS) goto error;
1695 }
1696 else if(do_vig)
1697 {
1698 /* Nothing moves, so there is nothing to resample: a dedicated pass costs one fetch per
1699 * pixel where the fused one would cost the resampler's full tap count for an identity
1700 * map. Which frame the falloff belongs to is the same question as above, and with no
1701 * geometry in play the two coincide. */
1702 const int vx = d->inverse ? roi_out_x : roi_in_x;
1703 const int vy = d->inverse ? roi_out_y : roi_in_y;
1704 const int vw = d->inverse ? owidth : iwidth;
1705 const int vh = d->inverse ? oheight : iheight;
1706 dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 0, sizeof(cl_mem), (void *)&dev_in);
1707 dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 1, sizeof(cl_mem), (void *)&dev_out);
1708 dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 2, sizeof(int), (void *)&vw);
1709 dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 3, sizeof(int), (void *)&vh);
1710 dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 4, sizeof(int), (void *)&vx);
1711 dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 5, sizeof(int), (void *)&vy);
1712 dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 6, sizeof(ls_eval_t), (void *)&p);
1714 d->inverse ? osizes : isizes);
1715 if(err != CL_SUCCESS) goto error;
1716 }
1717 else
1718 {
1719 err = dt_opencl_enqueue_copy_image(devid, dev_in, dev_out, origin, origin, oregion);
1720 if(err != CL_SUCCESS) goto error;
1721 }
1722
1723 return TRUE;
1724
1725error:
1726 dt_print(DT_DEBUG_OPENCL, "[opencl_lens] couldn't enqueue kernel! %d\n", err);
1727 return FALSE;
1728}
1729#endif
1730
1732{
1733 /* CPU: in + out, and nothing else of image size.
1734 *
1735 * The whole-frame copy process() used to make -- to darken before resampling from it --
1736 * is gone with the separate vignetting pass: the falloff is folded into the resampling
1737 * loop, which now reads the caller's own input buffer. The displacement map is not a
1738 * whole-image temporary either: it is built a row at a time into a per-thread buffer of
1739 * width*6 floats, so it grows with the frame's WIDTH and the thread count rather than
1740 * its area -- ~2 MB for a 6000 px frame on 16 threads, against ~384 MB for one 24 Mpx
1741 * RGBA buffer. Counting it here would reserve memory nothing allocates.
1742 *
1743 * GPU: in + out, and nothing else at all -- one kernel reads the input and writes the
1744 * output, with vignetting folded into the same pass.
1745 *
1746 * Both figures used to be 4.5, meaning in + out + tmp + a six-float-per-pixel map buffer
1747 * (1.5x an RGBA one) that had to be built on the host and uploaded. The GPU path no
1748 * longer has that buffer -- each work-item evaluates its own coordinates from ~80 bytes
1749 * of coefficients passed as a kernel argument -- so reserving 1.5 image buffers for it
1750 * made the tile solver split frames that would have fitted whole.
1751 *
1752 * factor_cl and maxbuf_cl have to be set explicitly: dt_develop_tiling_t defaults them to
1753 * the CPU figures (develop/tiling.c), so a module that sets only `factor` silently
1754 * describes its GPU path with its CPU path's appetite. */
1755 tiling->factor = 2.0f; // in + out
1756 tiling->maxbuf = 1.0f;
1757 tiling->factor_cl = 2.0f; // in + out; no intermediate at all
1758 tiling->maxbuf_cl = 1.0f;
1759 tiling->overhead = 0;
1760 tiling->overlap = 4;
1761 tiling->xalign = 1;
1762 tiling->yalign = 1;
1763 return;
1764}
1765
1767 float *const __restrict points, size_t points_count)
1768{
1770 if(!_lens_data_available(d)) return 0;
1771
1772 const float orig_w = piece->buf_in.width, orig_h = piece->buf_in.height;
1773 int modflags;
1774
1775 const int used_lf_mask = _lens_mask_for_mono(dt_image_is_monochrome(&self->dev->image_storage));
1776
1777 ls_modifier_t modifier;
1778 get_modifier(&modflags, orig_w, orig_h, d, used_lf_mask, TRUE, &modifier, NULL);
1779
1780 if(_lens_flags_move_pixels(modflags))
1781 {
1782 __OMP_PARALLEL_FOR__(firstprivate(points, points_count, modifier) if(points_count > 100))
1783 for(size_t i = 0; i < points_count * 2; i += 2)
1784 {
1785 float DT_ALIGNED_ARRAY buf[6];
1786 ls_modifier_apply_subpixel_geometry(&modifier, points[i], points[i + 1], 1, 1, buf);
1787 // take green channel distortion, like distort_mask() does, so x and y come from the
1788 // same color channel's distortion field instead of mixing red's x with green's y.
1789 points[i] = buf[2];
1790 points[i + 1] = buf[3];
1791 }
1792 }
1793
1794
1795 return 1;
1796}
1797
1799 float *const __restrict points, size_t points_count)
1800{
1802
1803 if(!_lens_data_available(d)) return 0;
1804
1805 const int used_lf_mask = _lens_mask_for_mono(dt_image_is_monochrome(&self->dev->image_storage));
1806
1807 const float orig_w = piece->buf_in.width, orig_h = piece->buf_in.height;
1808 int modflags;
1809 ls_modifier_t modifier;
1810 get_modifier(&modflags, orig_w, orig_h, d, used_lf_mask, FALSE, &modifier, NULL);
1811
1812
1813 if(_lens_flags_move_pixels(modflags))
1814 {
1815 __OMP_PARALLEL_FOR__(firstprivate(points_count, modifier, points) if(points_count > 100))
1816 for(size_t i = 0; i < points_count * 2; i += 2)
1817 {
1818 float DT_ALIGNED_ARRAY buf[6];
1819 ls_modifier_apply_subpixel_geometry(&modifier, points[i], points[i + 1], 1, 1, buf);
1820 // take green channel distortion, like distort_mask() does, so x and y come from the
1821 // same color channel's distortion field instead of mixing red's x with green's y.
1822 points[i] = buf[2];
1823 points[i + 1] = buf[3];
1824 }
1825 }
1826
1827
1828 return 1;
1829}
1830
1831// TODO: Shall we keep DT_LENS_MODIFY_TCA in the modifiers?
1833 const float *const in, float *const out, const dt_iop_roi_t *const roi_in,
1834 const dt_iop_roi_t *const roi_out)
1835{
1836 (void)pipe;
1837 const dt_iop_lensfun_data_t *const d = (dt_iop_lensfun_data_t *)piece->data;
1838
1840 {
1841 dt_iop_image_copy_by_size(out, in, roi_out->width, roi_out->height, 1);
1842 return;
1843 }
1844
1845 const float orig_w = roi_in->scale * piece->buf_in.width, orig_h = roi_in->scale * piece->buf_in.height;
1846 int modflags;
1847 ls_modifier_t modifier;
1848 get_modifier(&modflags, orig_w, orig_h, d,
1849 /*DT_LENS_MODIFY_TCA |*/ DT_LENS_MODIFY_DISTORTION | DT_LENS_MODIFY_GEOMETRY
1851 FALSE, &modifier, NULL);
1852
1853 if(!_lens_flags_move_pixels(modflags))
1854 {
1855 dt_iop_image_copy_by_size(out, in, roi_out->width, roi_out->height, 1);
1856 return;
1857 }
1858
1859 const struct dt_interpolation *const interpolation = dt_interpolation_new(DT_INTERPOLATION_USERPREF_WARP);
1860
1861 // acquire temp memory for distorted pixel coords
1862 const size_t bufsize = (size_t)roi_out->width * 2 * 3;
1863 size_t padded_bufsize;
1864 float *const buf = dt_pixelpipe_cache_alloc_perthread_float(bufsize, &padded_bufsize);
1865 if(IS_NULL_PTR(buf)) return;
1866 __OMP_PARALLEL_FOR__(firstprivate(buf, padded_bufsize, d, modifier, in, out, interpolation, roi_in, roi_out))
1867 for(int y = 0; y < roi_out->height; y++)
1868 {
1869 float *bufptr = (float*)dt_get_perthread(buf, padded_bufsize);
1870 ls_modifier_apply_subpixel_geometry(&modifier, roi_out->x, roi_out->y + y, roi_out->width, 1, bufptr);
1871
1872 // reverse transform the global coords from lf to our buffer
1873 float *_out = out + (size_t)y * roi_out->width;
1874 for(int x = 0; x < roi_out->width; x++, bufptr += 6, _out++)
1875 {
1876 if(d->do_nan_checks && (!isfinite(bufptr[2]) || !isfinite(bufptr[3])))
1877 {
1878 *_out = 0.0f;
1879 continue;
1880 }
1881
1882 // take green channel distortion also for alpha channel
1883 const float pi0 = bufptr[2] - roi_in->x;
1884 const float pi1 = bufptr[3] - roi_in->y;
1885 *_out = dt_interpolation_compute_sample(interpolation, in, pi0, pi1, roi_in->width, roi_in->height, 1,
1886 roi_in->width);
1887 }
1888 }
1889
1890
1892}
1893
1895 struct dt_dev_pixelpipe_iop_t *piece, dt_iop_roi_t *roi_out,
1896 const dt_iop_roi_t *roi_in)
1897{
1898 *roi_out = *roi_in;
1899}
1900
1901void modify_roi_in(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe,
1903 const dt_iop_roi_t *const roi_out, dt_iop_roi_t *roi_in)
1904{
1906 *roi_in = *roi_out;
1907 // inverse transform with given params
1908
1909 if(!_lens_data_available(d)) return;
1910
1911 const float orig_w = roi_in->scale * piece->buf_in.width;
1912 const float orig_h = roi_in->scale * piece->buf_in.height;
1913 int modflags;
1914 ls_modifier_t modifier;
1915 get_modifier(&modflags, orig_w, orig_h, d, DT_LENS_MODIFY_ALL_AXES, FALSE, &modifier, NULL);
1916
1917 if(_lens_flags_move_pixels(modflags))
1918 {
1919 const int xoff = roi_in->x;
1920 const int yoff = roi_in->y;
1921 const int width = roi_in->width;
1922 const int height = roi_in->height;
1923 const int awidth = abs(width);
1924 const int aheight = abs(height);
1925 const int xstep = (width < 0) ? -1 : 1;
1926 const int ystep = (height < 0) ? -1 : 1;
1927
1928 float xm = FLT_MAX, xM = -FLT_MAX, ym = FLT_MAX, yM = -FLT_MAX;
1929 const size_t nbpoints = 2 * awidth + 2 * aheight;
1930
1931 // ROI planning passes the active pipe now, but this temporary edge buffer only needs an
1932 // allocator bucket id, so use a stable generic bucket.
1933 float *const buf = (float *)dt_pixelpipe_cache_alloc_align_cache(sizeof(float) * nbpoints * 2 * 3,
1935 if(IS_NULL_PTR(buf)) return;
1936
1937#ifdef _OPENMP
1938#pragma omp parallel default(none) reduction(min : xm, ym) reduction(max : xM, yM) \
1939 firstprivate(modifier, xoff, yoff, awidth, aheight, width, height, nbpoints, ystep, xstep, buf)
1940#endif
1941 {
1942 __OMP_FOR__()
1943 for(int i = 0; i < awidth; i++)
1944 ls_modifier_apply_subpixel_geometry(&modifier, xoff + i * xstep, yoff, 1, 1, buf + 6 * i);
1945 __OMP_FOR__()
1946 for(int i = 0; i < awidth; i++)
1947 ls_modifier_apply_subpixel_geometry(&modifier, xoff + i * xstep, yoff + (height - 1), 1, 1, buf + 6 * (awidth + i));
1948 __OMP_FOR__()
1949 for(int j = 0; j < aheight; j++)
1950 ls_modifier_apply_subpixel_geometry(&modifier, xoff, yoff + j * ystep, 1, 1, buf + 6 * (2 * awidth + j));
1951 __OMP_FOR__()
1952 for(int j = 0; j < aheight; j++)
1953 ls_modifier_apply_subpixel_geometry(&modifier, xoff + (width - 1), yoff + j * ystep, 1, 1, buf + 6 * (2 * awidth + aheight + j));
1954
1955#ifdef _OPENMP
1956#pragma omp barrier
1957#endif
1958 __OMP_FOR__()
1959 for(size_t k = 0; k < nbpoints; k++)
1960 {
1961 // iterate over RGB channels x and y coordinates
1962 for(size_t c = 0; c < 6; c+=2)
1963 {
1964 const float x = buf[6 * k + c];
1965 const float y = buf[6 * k + c + 1];
1966 xm = isnan(x) ? xm : MIN(xm, x);
1967 xM = isnan(x) ? xM : MAX(xM, x);
1968 ym = isnan(y) ? ym : MIN(ym, y);
1969 yM = isnan(y) ? yM : MAX(yM, y);
1970 }
1971 }
1972 }
1973
1975
1976 // LensFun can return NAN coords, so we need to handle them carefully.
1977 if(!isfinite(xm) || !(0 <= xm && xm < orig_w)) xm = 0;
1978 if(!isfinite(xM) || !(1 <= xM && xM < orig_w)) xM = orig_w;
1979 if(!isfinite(ym) || !(0 <= ym && ym < orig_h)) ym = 0;
1980 if(!isfinite(yM) || !(1 <= yM && yM < orig_h)) yM = orig_h;
1981
1983 roi_in->x = fmaxf(0.0f, roundf(xm - interpolation->width));
1984 roi_in->y = fmaxf(0.0f, roundf(ym - interpolation->width));
1985 roi_in->width = roundf(fminf(orig_w - roi_in->x, xM - roi_in->x + interpolation->width));
1986 roi_in->height = roundf(fminf(orig_h - roi_in->y, yM - roi_in->y + interpolation->width));
1987
1988 // sanity check.
1989 roi_in->x = CLAMP(roi_in->x, 0, (int)floorf(orig_w));
1990 roi_in->y = CLAMP(roi_in->y, 0, (int)floorf(orig_h));
1991 roi_in->width = CLAMP(roi_in->width, 1, (int)ceilf(orig_w) - roi_in->x);
1992 roi_in->height = CLAMP(roi_in->height, 1, (int)ceilf(orig_h) - roi_in->y);
1993 }
1994}
1995
1996/* --- the shared geometry core ----------------------------------------------------------
1997 *
1998 * lens resolves its effective parameters and then builds a lensfun state out of them, and both
1999 * halves are needed twice: once for the pixel pipe, once for the record the geometry service
2000 * composes GUI coordinates from (develop/geometry/geometry.h). Expressed once here.
2001 *
2002 * Note what lens does NOT contribute: modify_roi_out() is the identity, so this module changes
2003 * no dimensions. It is on the geometry roster purely for its point transforms.
2004 */
2005
2027 const dt_iop_lensfun_params_t *const p)
2028{
2029 return (p->modified == 0) ? (const dt_iop_lensfun_params_t *)self->default_params : p;
2030}
2031
2041{
2042 (void)self;
2043 memset(&d->ls_lens, 0, sizeof(d->ls_lens));
2044 d->ls_have = FALSE;
2045
2046 /* No lock. The reader is lock-free by construction and its handle is thread-local, so a
2047 * pipeline thread resolving a lens no longer serialises against anything -- least of all
2048 * against RawSpeed decoding a file, which is what sharing dt_plugin_threadsafe_mutex()
2049 * used to mean. And nothing is owned on return: an ls_lens_t is a value, valid after the
2050 * handle that produced it is closed, so there is no deep copy to make and no delete to
2051 * forget. */
2052 long long mount_id = 0;
2053 float camera_crop = 0.f;
2054
2055 /* The image's own crop factor, which reload_defaults() takes from EXIF and the camera
2056 * picker overwrites. Seeded unconditionally because piece->data is REUSED across commits:
2057 * assigning it only inside the lookup below left a piece whose previous params resolved a
2058 * camera still carrying that camera's crop when the current ones do not resolve one. */
2059 d->crop = p->crop;
2060
2061 if(p->camera[0])
2062 {
2063 ls_camera_t camera;
2064 /* The stored camera name is a model with no maker -- what the picker writes and what
2065 * EXIF gives -- so the matcher is asked for one rather than guessing the other. */
2066 if(_ls_find_camera(NULL, p->camera, &camera))
2067 {
2068 d->crop = camera.crop_factor;
2069 camera_crop = camera.crop_factor;
2070 mount_id = camera.mount_id;
2071 }
2072 }
2073
2074 /* No camera, no database lens -- and that is a correctness rule, not caution.
2075 *
2076 * The mount is what makes a lens name mean one lens. Without it the search runs over the
2077 * whole catalogue, where hundreds of unrelated optics share the names "fixed lens" and
2078 * "festes objektiv", tie on score, and are separated by nothing at all. Correcting from
2079 * an arbitrary pick among those is how a Ricoh GR II came to be corrected with a 5.9 mm
2080 * compact's distortion.
2081 *
2082 * It also restores the panel's own invariant. gui_update() shows a lens only when a
2083 * camera resolved, so this was the one path where the pipeline corrected from something
2084 * the panel could not name and the user could not check or override. The recovery is the
2085 * picker: choose the body by hand, the mount is known, and the correct lens resolves and
2086 * is shown. */
2087 if(p->lens[0] && mount_id > 0)
2088 {
2089 const long long lens_id = _ls_find_lens(mount_id, camera_crop, p->focal, p->lens);
2090 ls_db_t *db = _ls_db();
2091 if(lens_id >= 0 && !IS_NULL_PTR(db) && ls_db_lens_by_id(db, lens_id, &d->ls_lens) == 1)
2092 {
2093 d->ls_have = TRUE;
2094 }
2095 }
2096 else if(p->lens[0])
2097 {
2098 /* DT_DEBUG_PIPE, not ALWAYS: nobody chose this, so it is a default declining to guess
2099 * rather than a request that could not be honoured -- and this runs from the GUI's
2100 * availability query too, not only at commit. The panel already says it, by showing
2101 * neither a camera nor a lens. */
2103 "[lens] `%s' is not a camera this database knows, so `%s' cannot be resolved to"
2104 " one lens; correcting from the database is declined\n", p->camera, p->lens);
2105 }
2106
2107 /* Typed coefficients are a SOURCE of their own, not an edit applied to a database row.
2108 *
2109 * They used to be written inside the lookup above, over the calibration of a lens the
2110 * database had just returned -- so on a body-and-lens pair the database does not know,
2111 * d->ls_have stayed FALSE, _lens_data_available() answered no, and process(),
2112 * process_cl(), the three distort_*() callbacks and modify_roi_in() all copied their
2113 * input and returned. The two sliders moved and the pipeline never saw them, on exactly
2114 * the images manual correction exists for.
2115 *
2116 * With a database lens the manual entry still REPLACES its aberration, which is what
2117 * manual means, and the coefficients live in that lens's calibration frame. With none,
2118 * the frame is a lens declared calibrated on THIS camera, so ls_modifier_init()'s
2119 * calibration-crop / image-crop rescaling is 1 and the two numbers act directly.
2120 * aspect_ratio is deliberately left at 0, which the library reads as its own 1.5 default
2121 * -- the same normalisation a 3:2-calibrated database entry gives, so a coefficient does
2122 * not change meaning on the day a profile for the lens appears.
2123 *
2124 * Writing it here rather than there also means it no longer depends on p->lens[0] being
2125 * non-empty: an unnamed lens is not a reason to refuse numbers the user typed. */
2127 {
2128 if(!d->ls_have)
2129 {
2130 d->ls_lens.type = LS_LENS_RECTILINEAR;
2131 d->ls_lens.crop_factor = d->crop;
2132 d->ls_lens.min_focal = p->focal;
2133 d->ls_lens.max_focal = p->focal;
2134 d->ls_have = TRUE;
2135 }
2136
2137 /* One entry at the shooting focal is exactly what the two sliders describe. ls_lens_t
2138 * is this module's own copy, so overwriting the array is both cheaper and clearer than
2139 * upstream's remove-every-entry-then-add dance on a shared object -- which is what the
2140 * code here used to do, twice, under two different lensfun APIs. */
2141 d->ls_lens.n_tca = 1;
2142 d->ls_lens.tca[0].model = LS_TCA_LINEAR;
2143 d->ls_lens.tca[0].focal = p->focal;
2144 d->ls_lens.tca[0].terms[0] = p->tca_r;
2145 d->ls_lens.tca[0].terms[1] = p->tca_b;
2146 for(int i = 2; i < 6; i++) d->ls_lens.tca[0].terms[i] = 0.f;
2147 }
2148
2149 d->modify_flags = p->modify_flags;
2150 if(dt_image_is_monochrome(&self->dev->image_storage)) d->modify_flags &= ~DT_LENS_MODIFY_TCA;
2151 d->inverse = p->inverse;
2152 d->scale = p->scale;
2153 d->focal = p->focal;
2154 d->aperture = p->aperture;
2155 d->distance = p->distance;
2156 d->target_geom = p->target_geom;
2157 d->do_nan_checks = TRUE;
2158 d->tca_override = p->tca_override;
2159
2160 /* The maker's own profile, if any axis asked for it and the file carries one.
2161 *
2162 * Resolved HERE, at commit, for the same reason the database lens is: the pixel path gets
2163 * values and does no lookups. ls_vendor_resolve() normalises whichever vendor format the
2164 * file holds -- Sony, Fuji, Olympus or a DNG opcode list -- straight into the knot table
2165 * the evaluators consume; past that call nothing here knows which maker wrote it.
2166 *
2167 * The finetune is NULL: "as the maker measured", which is the library's documented
2168 * meaning for it. The per-class strength blends the vendor GUIs offer are not exposed by
2169 * this module. */
2170 d->knots_have = FALSE;
2171 d->knots_scale = 1.f;
2172 {
2173 gboolean wants_embedded = FALSE;
2174 for(dt_lens_axis_t axis = 0; axis < DT_LENS_AXIS_LAST; axis++)
2175 if(_lens_source_is(p, axis, DT_LENS_SOURCE_EMBEDDED)) wants_embedded = TRUE;
2176
2177 if(wants_embedded)
2178 {
2179 const dt_image_t *const img = &self->dev->image_storage;
2180 float scale = 1.f;
2181 const int got = ls_vendor_resolve(&img->exif_correction, NULL,
2182 img->p_width, img->p_height, &d->ls_knots, &scale);
2183
2184 /* The two failure reports stay distinct, because they mean different things to the
2185 * person reading them: "this camera embeds nothing" is a fact about the hardware,
2186 * "we failed to read what it embedded" is a bug to report. Both then correct from
2187 * the database, and both say so -- a user who picked a source and got another one
2188 * is entitled to know. Once, at commit; not per pipe per frame. */
2189 if(got == 0)
2191 "[lens] an axis asks for the vendor profile but this file carries none;"
2192 " correcting from the community profile instead\n");
2193 else if(got < 0)
2195 "[lens] this file's vendor profile could not be decoded;"
2196 " correcting from the community profile instead\n");
2197 else
2198 {
2199 d->knots_scale = (scale > 0.f) ? scale : 1.f;
2200 d->knots_have = TRUE;
2201 }
2202 }
2203 }
2204
2205 /*
2206 * there are certain situations when LensFun can return NAN coordinated.
2207 * most common case would be when the FOV is increased.
2208 */
2209 if(d->target_geom == DT_LENS_RECTILINEAR)
2210 {
2211 d->do_nan_checks = FALSE;
2212 }
2213 else if((int)d->target_geom == (int)d->ls_lens.type)
2214 {
2215 d->do_nan_checks = FALSE;
2216 }
2217}
2218
2224
2227{
2229
2230 // FIXME: this is utter shit and should be made into a GUI "mode".
2231 // If p->modified == 0, mode = auto and hide all controls
2232 // if p->modidified == 1, mode = manual and show all controls.
2233 if(((dt_iop_lensfun_params_t *)p1)->modified == 0)
2234 {
2235 // Temporary fix pending GUI unfucking
2236 dt_iop_compute_module_hash(self, self->dev->forms);
2237 }
2238
2240
2241 piece->cache_output_on_ram = TRUE;
2242}
2243
2244/* --- the geometry service's view of this module (develop/geometry/geometry.h) ---------
2245 *
2246 * The one record in the service whose payload is not plain data: evaluating a lens correction
2247 * needs the resolved calibration, so the record owns a
2248 * deep copy and frees it. That is what dt_geometry_record_t::free_data exists for.
2249 */
2250
2256
2257static void _lens_free_data(void *ptr)
2258{
2260 if(!g) return;
2261 free(g);
2262}
2263
2266static int _lens_geometry_apply(const void *data, const dt_geometry_record_t *const record,
2267 float *points, size_t points_count, gboolean inverse)
2268{
2269 const dt_iop_lens_geometry_t *const g = (const dt_iop_lens_geometry_t *)data;
2270 const dt_iop_lensfun_data_t *const d = &g->data;
2271
2272 if(!_lens_data_available(d)) return 0;
2273 if(record->in.width <= 0 || record->in.height <= 0) return 0;
2274
2275 int modflags = 0;
2276 ls_modifier_t modifier;
2277 if(!get_modifier(&modflags, record->in.width, record->in.height, d, g->used_lf_mask, inverse,
2278 &modifier, NULL))
2279 return 0;
2280
2281 if(_lens_flags_move_pixels(modflags))
2282 {
2283 for(size_t i = 0; i < points_count * 2; i += 2)
2284 {
2285 float DT_ALIGNED_ARRAY buf[6];
2286 ls_modifier_apply_subpixel_geometry(&modifier, points[i], points[i + 1], 1, 1, buf);
2287 // green channel, like distort_transform() and distort_mask() do, so x and y come from the
2288 // same colour channel's distortion field instead of mixing red's x with green's y.
2289 points[i] = buf[2];
2290 points[i + 1] = buf[3];
2291 }
2292 }
2293
2294 return 1;
2295}
2296
2297static int _lens_geometry_transform(const void *data, const dt_geometry_record_t *const record,
2298 dt_geometry_chain_t *chain, float *points, size_t points_count)
2299{
2300 return _lens_geometry_apply(data, record, points, points_count, TRUE);
2301}
2302
2303static int _lens_geometry_backtransform(const void *data, const dt_geometry_record_t *const record,
2304 dt_geometry_chain_t *chain, float *points, size_t points_count)
2305{
2306 return _lens_geometry_apply(data, record, points, points_count, FALSE);
2307}
2308
2310 /* .map_size = */ NULL, // modify_roi_out() is the identity: lens changes no dimensions
2311 /* .transform = */ _lens_geometry_transform,
2312 /* .backtransform = */ _lens_geometry_backtransform,
2313};
2314
2315gboolean geometry_record(dt_iop_module_t *self, const void *params, dt_geometry_record_t *record)
2316{
2318 if(!g) return FALSE;
2319
2322 _lens_build_data(self, p, &g->data);
2323 g->used_lf_mask = _lens_used_mask(self);
2324
2325 record->data = g;
2326 record->free_data = _lens_free_data;
2327 record->vtable = &_lens_geometry_vtable;
2328 return TRUE;
2329}
2330
2336
2338{
2339 /* init_pipe() may have failed to allocate, and cleanup runs regardless. */
2340 if(IS_NULL_PTR(piece->data)) return;
2341
2342 /* Nothing to free but the piece itself: ls_lens_t is a value living inside it, where the
2343 * lfLens it replaces was a heap object this had to remember to delete. */
2344 dt_free_align(piece->data);
2345 piece->data = NULL;
2346}
2347
2349{
2350 const int program = 2; // basic.cl, from programs.conf
2353 module->data = gd;
2354 gd->kernel_lens_distort_bilinear = dt_opencl_create_kernel(program, "lens_distort_bilinear");
2355 gd->kernel_lens_distort_bicubic = dt_opencl_create_kernel(program, "lens_distort_bicubic");
2356 gd->kernel_lens_distort_mitchell = dt_opencl_create_kernel(program, "lens_distort_mitchell");
2357 gd->kernel_lens_vignette = dt_opencl_create_kernel(program, "lens_vignette");
2358
2359 /* Nothing to pre-warm any more. Opening the calibration database is one mmap of an
2360 * already-parsed file, done lazily per thread on first use and measured at 0.18 ms --
2361 * there is no 100 ms XML parse left to hide behind a startup thread. */
2362}
2363
2365
2367{
2368 char *new_lens;
2369 const dt_image_t *img = &module->dev->image_storage;
2370
2371 // reload image specific stuff
2372 // get all we can from exif:
2374
2375 new_lens = _lens_sanitize(img->exif_lens);
2376 g_strlcpy(d->lens, new_lens, sizeof(d->lens));
2377 dt_free(new_lens);
2378 g_strlcpy(d->camera, img->exif_model, sizeof(d->camera));
2379 d->crop = img->exif_crop;
2380 d->aperture = img->exif_aperture;
2381 d->focal = img->exif_focal_length;
2382 d->scale = 1.0;
2385
2386 /* Everything decided here is now WRITTEN DOWN, rather than recomputed whenever the image
2387 * is opened. See _lens_effective_params() for what the alternative cost. */
2388 d->modified = 1;
2389
2390 /* Prefer what the camera measured about its own lens, per axis, whenever the file carries
2391 * it. The maker had the actual body and the actual lens on a bench; the database has a
2392 * community measurement of that model. Where both exist the maker's is the better default,
2393 * and where only one exists this picks the one that works.
2394 *
2395 * Per axis, because the makers do not all write the same set: an Olympus body publishes
2396 * distortion and lateral CA and no falloff, so that axis correctly keeps the database.
2397 *
2398 * Scale stays 1: the embedded resolver applies the profile's own autoscale, which already
2399 * clears the borders it leaves. Anything else here would be a second, arbitrary zoom on
2400 * top of a factor the maker chose. */
2401 for(dt_lens_axis_t axis = 0; axis < DT_LENS_AXIS_LAST; axis++)
2402 {
2404 if(_lens_image_embeds(module, axis))
2406 }
2407 // if we did not find focus_distance in EXIF, lets default to 1000
2408 d->distance = img->exif_focus_distance == 0.0f ? 1000.0f : img->exif_focus_distance;
2409 d->target_geom = DT_LENS_RECTILINEAR;
2410
2411 if(dt_image_is_monochrome(img))
2412 d->modify_flags &= ~DT_LENS_MODIFY_TCA;
2413
2414 // init crop from db:
2415 char model[100]; // truncate often complex descriptions.
2416 g_strlcpy(model, img->exif_model, sizeof(model));
2417 for(char cnt = 0, *c = model; c < model + 100 && *c != '\0'; c++)
2418 if(*c == ' ')
2419 if(++cnt == 2) *c = '\0';
2420 if(img->exif_maker[0] || model[0])
2421 {
2422 ls_camera_t cam;
2423 if(!_ls_find_camera(img->exif_maker, img->exif_model, &cam)) return;
2424
2425 /* Upstream spells a real fact into the mount NAME: a lower-case initial means a
2426 * fixed-lens camera. That is how a compact is told from an interchangeable-lens body,
2427 * and it decides both branches below. */
2428 char mount[128] = { 0 };
2429 ls_db_t *db = _ls_db();
2430 if(IS_NULL_PTR(db)) return;
2431 ls_db_mount_name(db, cam.mount_id, mount, sizeof(mount));
2432 const gboolean fixed_lens = (mount[0] != '\0') && islower((unsigned char)mount[0]);
2433
2434 long long lens_id = _ls_find_lens(cam.mount_id, cam.crop_factor, d->focal, d->lens);
2435
2436 if(lens_id < 0 && fixed_lens)
2437 {
2438 /* A fixed-lens camera whose EXIF lens string matched nothing -- it is "(65535)", or
2439 * a name upstream files as "fixed lens". The lens is whatever is built into this
2440 * mount, so ask the mount directly instead of matching a name. */
2441 g_strlcpy(d->lens, "", sizeof(d->lens));
2442
2443 const int n = ls_db_lenses_for_mount(db, cam.mount_id, NULL, 0);
2444 if(n > 0)
2445 {
2446 long long *ids = (long long *)dt_alloc_align(sizeof(long long) * (size_t)n);
2447 if(!IS_NULL_PTR(ids))
2448 {
2449 ls_db_lenses_for_mount(db, cam.mount_id, ids, n);
2450 /* The shortest model name, as before: a fixed-lens mount can carry several
2451 * entries for one physical lens and the shortest is the plain one. */
2452 size_t shortest = SIZE_MAX;
2453 for(int i = 0; i < n; i++)
2454 {
2455 char maker[128] = "", lmodel[256] = "";
2456 if(ls_db_lens_name(db, ids[i], maker, sizeof(maker), lmodel, sizeof(lmodel)) <= 0)
2457 continue;
2458 const size_t len = strlen(lmodel);
2459 if(len < shortest)
2460 {
2461 shortest = len;
2462 lens_id = ids[i];
2463 g_strlcpy(d->lens, lmodel, sizeof(d->lens));
2464 }
2465 }
2466 dt_free_align(ids);
2467 }
2468 }
2469 }
2470
2471 if(lens_id >= 0)
2472 {
2473 ls_lens_t lens;
2474 if(ls_db_lens_by_id(db, lens_id, &lens) == 1)
2475 d->target_geom = (dt_lens_type_t)lens.type;
2476 }
2477
2478 d->crop = cam.crop_factor;
2479 d->scale = get_autoscale(module, d);
2480 module->workflow_enabled = dt_image_needs_rawprepare(img);
2481 }
2482
2483 // reload_defaults() stays params-only and never touches gui_data.
2484}
2485
2487{
2489
2490 /* Each thread's handle closes when that thread ends, except the one of the thread unloading
2491 * the modules: the main thread ends in exit(), which runs no thread-local destructor. */
2492 g_private_replace(&_ls_tls_key, NULL);
2493
2498 dt_free(module->data);
2499}
2500
2503
2504/* simple function to compute the floating-point precision
2505 which is enough for "normal use". The criteria is to have
2506 about 3 leading digits after the initial zeros. */
2507static int precision(double x, double adj)
2508{
2509 x *= adj;
2510
2511 if(x == 0) return 1;
2512 if(x < 1.0)
2513 if(x < 0.1)
2514 if(x < 0.01)
2515 return 5;
2516 else
2517 return 4;
2518 else
2519 return 3;
2520 else if(x < 100.0)
2521 if(x < 10.0)
2522 return 2;
2523 else
2524 return 1;
2525 else
2526 return 0;
2527}
2528
2529/* -- ufraw ptr array functions -- */
2530
2531static int ptr_array_insert_sorted(GPtrArray *array, const void *item, GCompareFunc compare)
2532{
2533 int length = array->len;
2534 g_ptr_array_set_size(array, length + 1);
2535 const void **root = (const void **)array->pdata;
2536
2537 int m = 0, l = 0, r = length - 1;
2538
2539 // Skip trailing NULL, if any
2540 if(l <= r && !root[r]) r--;
2541
2542 while(l <= r)
2543 {
2544 m = (l + r) / 2;
2545 int cmp = compare(root[m], item);
2546
2547 if(cmp == 0)
2548 {
2549 ++m;
2550 goto done;
2551 }
2552 else if(cmp < 0)
2553 l = m + 1;
2554 else
2555 r = m - 1;
2556 }
2557 if(r == m) m++;
2558
2559done:
2560 memmove(root + m + 1, root + m, sizeof(void *) * (length - m));
2561 root[m] = item;
2562 return m;
2563}
2564
2565static int ptr_array_find_sorted(const GPtrArray *array, const void *item, GCompareFunc compare)
2566{
2567 int length = array->len;
2568 void **root = array->pdata;
2569
2570 int l = 0, r = length - 1;
2571 int m = 0, cmp = 0;
2572
2573 if(!length) return -1;
2574
2575 // Skip trailing NULL, if any
2576 if(!root[r]) r--;
2577
2578 while(l <= r)
2579 {
2580 m = (l + r) / 2;
2581 cmp = compare(root[m], item);
2582
2583 if(cmp == 0)
2584 return m;
2585 else if(cmp < 0)
2586 l = m + 1;
2587 else
2588 r = m - 1;
2589 }
2590
2591 return -1;
2592}
2593
2594static void ptr_array_insert_index(GPtrArray *array, const void *item, int index)
2595{
2596 const void **root;
2597 int length = array->len;
2598 g_ptr_array_set_size(array, length + 1);
2599 root = (const void **)array->pdata;
2600 memmove(root + index + 1, root + index, sizeof(void *) * (length - index));
2601 root[index] = item;
2602}
2603
2604/* -- end ufraw ptr array functions -- */
2605
2606/* -- camera -- */
2607
2618static void _lens_params_set_camera(dt_iop_module_t *self, const long long camera_id)
2619{
2621
2622 ls_db_t *db = _ls_db();
2623 char maker[128] = "", model[256] = "", variant[128] = "";
2624 ls_camera_t cam;
2625 if(camera_id < 0 || IS_NULL_PTR(db)
2626 || ls_db_camera_name(db, camera_id, maker, sizeof(maker), model, sizeof(model),
2627 variant, sizeof(variant)) != 1
2628 || ls_db_camera_by_id(db, camera_id, &cam) != 1)
2629 return;
2630
2631 g_strlcpy(p->camera, model, sizeof(p->camera));
2632 p->crop = cam.crop_factor;
2633}
2634
2643static void camera_set(dt_iop_module_t *self, long long camera_id)
2644{
2646
2647 ls_db_t *db = _ls_db();
2648 char maker[128] = "", model[256] = "", variant[128] = "";
2649 ls_camera_t cam;
2650 if(camera_id < 0 || IS_NULL_PTR(db)
2651 || ls_db_camera_name(db, camera_id, maker, sizeof(maker), model, sizeof(model),
2652 variant, sizeof(variant)) != 1
2653 || ls_db_camera_by_id(db, camera_id, &cam) != 1)
2654 {
2655 gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(g->camera_model))), "");
2656 gtk_widget_set_tooltip_text(GTK_WIDGET(g->camera_model), "");
2657 g->camera_id = -1;
2658 return;
2659 }
2660
2661 g->camera_id = camera_id;
2662
2663 gchar *fm = maker[0] ? g_strdup_printf("%s, %s", maker, model) : g_strdup(model);
2664 gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(g->camera_model))), fm);
2665 dt_free(fm);
2666
2667 // sizeof(variant) + 4, not 128: the format adds " (" and ")" around a string that can
2668 // itself fill variant[], so an equal-sized buffer drops the closing parenthesis on a long
2669 // camera variant. Derived from variant's size so it stays right if that changes.
2670 char _variant[sizeof(variant) + 4];
2671 if(variant[0])
2672 snprintf(_variant, sizeof(_variant), " (%s)", variant);
2673 else
2674 _variant[0] = 0;
2675
2676 char mount[128] = "";
2677 ls_db_mount_name(db, cam.mount_id, mount, sizeof(mount));
2678
2679 fm = g_strdup_printf(_("maker:\t\t%s\n"
2680 "model:\t\t%s%s\n"
2681 "mount:\t\t%s\n"
2682 "crop factor:\t%.1f"),
2683 maker, model, _variant, mount, cam.crop_factor);
2684 gtk_widget_set_tooltip_text(GTK_WIDGET(g->camera_model), fm);
2685 dt_free(fm);
2686}
2687
2688static void camera_menu_select(GtkMenuItem *menuitem, gpointer user_data)
2689{
2690 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
2691
2692 /* First, and for the whole handler: a suppressed callback is a programmatic widget
2693 * update, and this one writes parameters and commits history. */
2694 if(dt_gui_widgets_suppressed()) return;
2695
2696 const long long camera_id =
2697 (long long)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menuitem), "lens-camera-id"));
2698
2700 _lens_params_set_camera(self, camera_id);
2701 p->modified = 1;
2702
2703 /* View second, because it reads the parameters just written. The camera decides the mount
2704 * and the crop factor, and a lens is matched against those -- so a lens that matched the
2705 * old camera may not match the new one, and the other way round. Every other caller of
2706 * camera_set() runs a lens_set() straight afterwards and gets the rebuild from there;
2707 * this one does not, so it asks itself. */
2708 {
2710 camera_set(self, camera_id);
2713 }
2714
2715 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
2716}
2717
2723static void camera_menu_fill(dt_iop_module_t *self, const long long *ids, int n)
2724{
2726 GPtrArray *makers, *submenus;
2727
2728 if(g->camera_menu)
2729 {
2730 gtk_widget_destroy(GTK_WIDGET(g->camera_menu));
2731 g->camera_menu = NULL;
2732 }
2733
2734 ls_db_t *db = _ls_db();
2735 if(IS_NULL_PTR(db)) return;
2736
2737 /* Count all existing camera makers and create a sorted list */
2738 makers = g_ptr_array_new_with_free_func(dt_free_gpointer);
2739 submenus = g_ptr_array_new();
2740 for(int i = 0; i < n; i++)
2741 {
2742 char maker[128] = "", model[256] = "", variant[128] = "";
2743 if(ls_db_camera_name(db, ids[i], maker, sizeof(maker), model, sizeof(model),
2744 variant, sizeof(variant)) != 1)
2745 continue;
2746
2747 GtkWidget *submenu, *item;
2748 int idx = ptr_array_find_sorted(makers, maker, (GCompareFunc)g_utf8_collate);
2749 if(idx < 0)
2750 {
2751 /* No such maker yet, insert it into the array. The strings are OWNED now: they used
2752 * to point into a database that outlived the menu, and they no longer do. */
2753 idx = ptr_array_insert_sorted(makers, g_strdup(maker), (GCompareFunc)g_utf8_collate);
2754 /* Create a submenu for cameras by this maker */
2755 submenu = gtk_menu_new();
2756 ptr_array_insert_index(submenus, submenu, idx);
2757 }
2758
2759 submenu = (GtkWidget *)g_ptr_array_index(submenus, idx);
2760 /* Append current camera name to the submenu */
2761 if(!variant[0])
2762 item = gtk_menu_item_new_with_label(model);
2763 else
2764 {
2765 gchar *fm = g_strdup_printf("%s (%s)", model, variant);
2766 item = gtk_menu_item_new_with_label(fm);
2767 dt_free(fm);
2768 }
2769 gtk_widget_show(item);
2770 g_object_set_data(G_OBJECT(item), "lens-camera-id", GINT_TO_POINTER((gint)ids[i]));
2771 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(camera_menu_select), self);
2772 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), item);
2773 }
2774
2775 g->camera_menu = GTK_MENU(gtk_menu_new());
2776 for(unsigned i = 0; i < makers->len; i++)
2777 {
2778 GtkWidget *item = (GtkWidget *)gtk_menu_item_new_with_label((const gchar *)g_ptr_array_index(makers, i));
2779 gtk_widget_show(item);
2780 gtk_menu_shell_append(GTK_MENU_SHELL(g->camera_menu), item);
2781 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), (GtkWidget *)g_ptr_array_index(submenus, i));
2782 }
2783
2784 g_ptr_array_free(submenus, TRUE);
2785 g_ptr_array_free(makers, TRUE);
2786}
2787
2789static long long *_camera_all_ids(int *out_n)
2790{
2791 *out_n = 0;
2792 ls_db_t *db = _ls_db();
2793 if(IS_NULL_PTR(db)) return NULL;
2794 const int n = ls_db_list_cameras(db, NULL, 0);
2795 if(n <= 0) return NULL;
2796 long long *ids = (long long *)dt_alloc_align(sizeof(long long) * (size_t)n);
2797 if(IS_NULL_PTR(ids)) return NULL;
2798 *out_n = ls_db_list_cameras(db, ids, n);
2799 return ids;
2800}
2801
2802static void parse_model(const char *txt, char *model, size_t sz_model)
2803{
2804 while(txt[0] && isspace(txt[0])) txt++;
2805 size_t len = strlen(txt);
2806 if(len > sz_model - 1) len = sz_model - 1;
2807 memcpy(model, txt, len);
2808 model[len] = 0;
2809}
2810
2811static void camera_menusearch_clicked(GtkWidget *button, gpointer user_data)
2812{
2813 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
2815
2816 (void)button;
2817
2818 int n = 0;
2819 long long *ids = _camera_all_ids(&n);
2820 if(IS_NULL_PTR(ids)) return;
2821 camera_menu_fill(self, ids, n);
2822 dt_free_align(ids);
2823
2824 dt_gui_menu_popup(GTK_MENU(g->camera_menu), button, GDK_GRAVITY_SOUTH, GDK_GRAVITY_NORTH);
2825}
2826
2827
2828/* -- end camera -- */
2829
2831{
2833 const char *text = dt_bauhaus_combobox_get_text(widget);
2834 if(text) (void)sscanf(text, "%f", &p->focal);
2835 p->modified = 1;
2836 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
2837}
2838
2840{
2842 const char *text = dt_bauhaus_combobox_get_text(widget);
2843 if(text) (void)sscanf(text, "%f", &p->aperture);
2844 p->modified = 1;
2845 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
2846}
2847
2849{
2851 const char *text = dt_bauhaus_combobox_get_text(widget);
2852 if(text) (void)sscanf(text, "%f", &p->distance);
2853 p->modified = 1;
2854 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
2855}
2856
2857static void delete_children(GtkWidget *widget, gpointer data)
2858{
2859 (void)data;
2860 gtk_widget_destroy(widget);
2861}
2862
2864static const char *_lens_type_name(int type)
2865{
2866 switch(type)
2867 {
2868 case DT_LENS_RECTILINEAR: return _("rectilinear");
2869 case DT_LENS_FISHEYE: return _("fisheye");
2870 case DT_LENS_PANORAMIC: return _("panoramic");
2871 case DT_LENS_EQUIRECTANGULAR: return _("equirectangular");
2872 case DT_LENS_FISHEYE_ORTHOGRAPHIC: return _("orthographic fisheye");
2873 case DT_LENS_FISHEYE_STEREOGRAPHIC: return _("stereographic fisheye");
2874 case DT_LENS_FISHEYE_EQUISOLID: return _("equisolid fisheye");
2875 case DT_LENS_FISHEYE_THOBY: return _("Thoby fisheye");
2876 default: return _("unknown");
2877 }
2878}
2879
2887static void _lens_params_set_lens(dt_iop_module_t *self, const long long lens_id)
2888{
2890
2891 ls_db_t *db = _ls_db();
2892 char l_maker[128] = "", l_model[256] = "";
2893 if(lens_id < 0 || IS_NULL_PTR(db)
2894 || ls_db_lens_name(db, lens_id, l_maker, sizeof(l_maker), l_model, sizeof(l_model)) <= 0)
2895 return;
2896
2897 g_strlcpy(p->lens, l_model, sizeof(p->lens));
2898}
2899
2902static void lens_set(dt_iop_module_t *self, long long lens_id)
2903{
2905 /* const, and effective: this reads the focal length, aperture and distance to seed the
2906 * editable comboboxes, and shows what is in force rather than what is stored. */
2908 self, (const dt_iop_lensfun_params_t *)self->params);
2909
2910 gchar *fm;
2911 const char *maker, *model;
2912 unsigned i;
2913 gdouble focal_values[]
2914 = { -INFINITY, 4.5, 8, 10, 12, 14, 15, 16, 17, 18, 20, 24, 28, 30, 31, 35,
2915 38, 40, 43, 45, 50, 55, 60, 70, 75, 77, 80, 85, 90, 100, 105, 110,
2916 120, 135, 150, 200, 210, 240, 250, 300, 400, 500, 600, 800, 1000, INFINITY };
2917 gdouble aperture_values[]
2918 = { -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,
2919 5.6, 6.3, 7.1, 8, 9, 10, 11, 13, 14, 16, 18, 20, 22, 25, 29, 32, 38, INFINITY };
2920
2921 ls_db_t *db = _ls_db();
2922 ls_lens_t lens_v;
2923 char l_maker[128] = "", l_model[256] = "";
2924 float min_focal = 0.f, max_focal = 0.f, min_ap = 0.f, max_ap = 0.f;
2925 const gboolean have = (lens_id >= 0) && !IS_NULL_PTR(db)
2926 && (ls_db_lens_by_id(db, lens_id, &lens_v) == 1)
2927 && (ls_db_lens_name(db, lens_id, l_maker, sizeof(l_maker),
2928 l_model, sizeof(l_model)) > 0);
2929 if(have) ls_db_lens_range(db, lens_id, &min_focal, &max_focal, &min_ap, &max_ap);
2930
2931 /* Which sources exist is a property of the lens, so it is re-decided HERE and not only
2932 * when the image is loaded: picking a different lens from the menu is exactly the moment
2933 * a database profile appears or disappears. Without this the panel kept whatever rows the
2934 * image arrived with, and choosing a lens the database does know left every axis still
2935 * offering nothing. */
2936 if(!have)
2937 {
2938 /* Nothing is disabled on this path. Availability is expressed by which ROWS a picker
2939 * holds, never by whether the picker is sensitive -- greying the whole widget withdrew
2940 * the two sources that need no database entry at all, the file's own embedded profile
2941 * and hand-typed TCA coefficients, which is precisely what a lens the database has
2942 * never heard of is left with. */
2945 return;
2946 }
2947
2948 maker = l_maker[0] ? l_maker : NULL;
2949 model = l_model[0] ? l_model : NULL;
2950
2951 if(model)
2952 {
2953 if(maker)
2954 fm = g_strdup_printf("%s, %s", maker, model);
2955 else
2956 fm = g_strdup_printf("%s", model);
2957 gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(g->lens_model))), fm);
2958 dt_free(fm);
2959 }
2960
2961 char focal[100], aperture[100], mounts[200];
2962
2963 if(min_focal < max_focal)
2964 snprintf(focal, sizeof(focal), "%g-%gmm", min_focal, max_focal);
2965 else
2966 snprintf(focal, sizeof(focal), "%gmm", min_focal);
2967 if(min_ap < max_ap)
2968 snprintf(aperture, sizeof(aperture), "%g-%g", min_ap, max_ap);
2969 else
2970 snprintf(aperture, sizeof(aperture), "%g", min_ap);
2971
2972 mounts[0] = 0;
2973 ls_db_lens_mounts(db, lens_id, mounts, sizeof(mounts));
2974
2975 fm = g_strdup_printf(_("maker:\t\t%s\n"
2976 "model:\t\t%s\n"
2977 "focal range:\t%s\n"
2978 "aperture:\t%s\n"
2979 "crop factor:\t%.1f\n"
2980 "type:\t\t%s\n"
2981 "mounts:\t%s"),
2982 maker ? maker : "?", model ? model : "?", focal, aperture,
2983 lens_v.crop_factor, _lens_type_name((int)lens_v.type), mounts);
2984
2985 gtk_widget_set_tooltip_text(GTK_WIDGET(g->lens_model), fm);
2986 dt_free(fm);
2987
2988 /* Create the focal/aperture/distance combo boxes */
2989 gtk_container_foreach(GTK_CONTAINER(g->lens_param_box), delete_children, NULL);
2990
2991 int ffi = 1, fli = -1;
2992 for(i = 1; i < sizeof(focal_values) / sizeof(gdouble) - 1; i++)
2993 {
2994 if(focal_values[i] < min_focal) ffi = i + 1;
2995 if(focal_values[i] > max_focal && fli == -1) fli = i;
2996 }
2997 if(focal_values[ffi] > min_focal)
2998 {
2999 focal_values[ffi - 1] = min_focal;
3000 ffi--;
3001 }
3002 if(max_focal == 0 || fli < 0) fli = sizeof(focal_values) / sizeof(gdouble) - 2;
3003 if(focal_values[fli + 1] < max_focal)
3004 {
3005 focal_values[fli + 1] = max_focal;
3006 ffi++;
3007 }
3008 if(fli < ffi) fli = ffi + 1;
3009
3010 GtkWidget *w;
3011 char txt[30];
3012
3013 // focal length
3015 dt_bauhaus_widget_set_label(w, N_("mm"));
3016 gtk_widget_set_tooltip_text(w, _("focal length (mm)"));
3017 snprintf(txt, sizeof(txt), "%.*f", precision(p->focal, 10.0), p->focal);
3019 for(int k = 0; k < fli - ffi; k++)
3020 {
3021 snprintf(txt, sizeof(txt), "%.*f", precision(focal_values[ffi + k], 10.0), focal_values[ffi + k]);
3023 }
3024 g_signal_connect(G_OBJECT(w), "value-changed", G_CALLBACK(lens_comboentry_focal_update), self);
3025 gtk_box_pack_start(GTK_BOX(g->lens_param_box), w, TRUE, TRUE, 0);
3027 g->cbe[0] = w;
3028
3029 // f-stop
3030 ffi = 1, fli = sizeof(aperture_values) / sizeof(gdouble) - 1;
3031 for(i = 1; i < sizeof(aperture_values) / sizeof(gdouble) - 1; i++)
3032 if(aperture_values[i] < min_ap) ffi = i + 1;
3033 if(aperture_values[ffi] > min_ap)
3034 {
3035 aperture_values[ffi - 1] = min_ap;
3036 ffi--;
3037 }
3038
3040 dt_bauhaus_widget_set_label(w, N_("f"));
3041 gtk_widget_set_tooltip_text(w, _("f-number (aperture)"));
3042 snprintf(txt, sizeof(txt), "%.*f", precision(p->aperture, 10.0), p->aperture);
3044 for(int k = 0; k < fli - ffi; k++)
3045 {
3046 snprintf(txt, sizeof(txt), "%.*f", precision(aperture_values[ffi + k], 10.0), aperture_values[ffi + k]);
3048 }
3049 g_signal_connect(G_OBJECT(w), "value-changed", G_CALLBACK(lens_comboentry_aperture_update), self);
3050 gtk_box_pack_start(GTK_BOX(g->lens_param_box), w, TRUE, TRUE, 0);
3052 g->cbe[1] = w;
3053
3055 dt_bauhaus_widget_set_label(w, N_("d"));
3056 gtk_widget_set_tooltip_text(w, _("distance to subject"));
3057 snprintf(txt, sizeof(txt), "%.*f", precision(p->distance, 10.0), p->distance);
3059 float val = 0.25f;
3060 for(int k = 0; k < 25; k++)
3061 {
3062 if(val > 1000.0f) val = 1000.0f;
3063 snprintf(txt, sizeof(txt), "%.*f", precision(val, 10.0), val);
3065 if(val >= 1000.0f) break;
3066 val *= sqrtf(2.0f);
3067 }
3068 g_signal_connect(G_OBJECT(w), "value-changed", G_CALLBACK(lens_comboentry_distance_update), self);
3069 gtk_box_pack_start(GTK_BOX(g->lens_param_box), w, TRUE, TRUE, 0);
3071 g->cbe[2] = w;
3072
3073 gtk_widget_show_all(g->lens_param_box);
3074
3075 /* Last, because the rows are read out of the params, which a caller acting on user input
3076 * has already written through _lens_params_set_lens(). */
3079}
3080
3081static void lens_menu_select(GtkMenuItem *menuitem, gpointer user_data)
3082{
3083 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
3084
3085 if(dt_gui_widgets_suppressed()) return;
3086
3089 const long long lens_id =
3090 (long long)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menuitem), "lens-id"));
3091
3092 /* Parameters first: both the autoscale below and the row rebuild inside lens_set() ask
3093 * the database what THIS lens offers, and they read it out of the params. */
3094 _lens_params_set_lens(self, lens_id);
3095 p->modified = 1;
3096 p->scale = get_autoscale(self, p);
3097
3098 {
3099 /* Widget writes, not user input. Without the freeze dt_bauhaus_slider_set() emits
3100 * value-changed, whose default callback writes the field again and commits a SECOND
3101 * history item for the one lens the user picked. */
3103 lens_set(self, lens_id);
3104 dt_bauhaus_slider_set(g->scale, p->scale);
3105 }
3106
3107 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
3108}
3109
3110static void lens_menu_fill(dt_iop_module_t *self, const long long *ids, int n)
3111{
3113 GPtrArray *makers, *submenus;
3114
3115 if(g->lens_menu)
3116 {
3117 gtk_widget_destroy(GTK_WIDGET(g->lens_menu));
3118 g->lens_menu = NULL;
3119 }
3120
3121 ls_db_t *db = _ls_db();
3122 if(IS_NULL_PTR(db)) return;
3123
3124 /* Count all existing lens makers and create a sorted list */
3125 makers = g_ptr_array_new_with_free_func(dt_free_gpointer);
3126 submenus = g_ptr_array_new();
3127 for(int i = 0; i < n; i++)
3128 {
3129 char maker[128] = "", model[256] = "";
3130 if(ls_db_lens_name(db, ids[i], maker, sizeof(maker), model, sizeof(model)) <= 0) continue;
3131
3132 GtkWidget *submenu, *item;
3133 int idx = ptr_array_find_sorted(makers, maker, (GCompareFunc)g_utf8_collate);
3134 if(idx < 0)
3135 {
3136 /* No such maker yet, insert it into the array. Owned strings: these no longer point
3137 * into a database that outlives the menu. */
3138 idx = ptr_array_insert_sorted(makers, g_strdup(maker), (GCompareFunc)g_utf8_collate);
3139 /* Create a submenu for lenses by this maker */
3140 submenu = gtk_menu_new();
3141 ptr_array_insert_index(submenus, submenu, idx);
3142 }
3143
3144 submenu = (GtkWidget *)g_ptr_array_index(submenus, idx);
3145 /* Append current lens name to the submenu */
3146 item = gtk_menu_item_new_with_label(model);
3147 gtk_widget_show(item);
3148 g_object_set_data(G_OBJECT(item), "lens-id", GINT_TO_POINTER((gint)ids[i]));
3149 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(lens_menu_select), self);
3150 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), item);
3151 }
3152
3153 g->lens_menu = GTK_MENU(gtk_menu_new());
3154 for(unsigned i = 0; i < makers->len; i++)
3155 {
3156 GtkWidget *item = gtk_menu_item_new_with_label((const gchar *)g_ptr_array_index(makers, i));
3157 gtk_widget_show(item);
3158 gtk_menu_shell_append(GTK_MENU_SHELL(g->lens_menu), item);
3159 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), (GtkWidget *)g_ptr_array_index(submenus, i));
3160 }
3161
3162 g_ptr_array_free(submenus, TRUE);
3163 g_ptr_array_free(makers, TRUE);
3164}
3165
3177static long long *_lens_ids_for_camera(dt_iop_module_t *self, const char *model, int *out_n)
3178{
3179 *out_n = 0;
3181 ls_db_t *db = _ls_db();
3182 if(IS_NULL_PTR(db)) return NULL;
3183
3184 ls_camera_t cam;
3185 const gboolean have_cam = (g->camera_id >= 0) && (ls_db_camera_by_id(db, g->camera_id, &cam) == 1);
3186
3187 if(model && model[0])
3188 {
3189 enum { MAX_HITS = 32 };
3190 ls_db_match_t m[MAX_HITS];
3191 const int n = ls_db_match_lens(db, NULL, model, have_cam ? cam.mount_id : 0,
3192 have_cam ? cam.crop_factor : 0.f, m, MAX_HITS);
3193 if(n <= 0) return NULL;
3194 long long *ids = (long long *)dt_alloc_align(sizeof(long long) * (size_t)n);
3195 if(IS_NULL_PTR(ids)) return NULL;
3196 for(int i = 0; i < n; i++) ids[i] = m[i].lens_id;
3197 *out_n = n;
3198 return ids;
3199 }
3200
3201 /* Everything that fits the camera's mount, or the whole catalogue when no camera is
3202 * selected -- which is what upstream answered for a NULL camera too. */
3203 const int total = ls_db_list_lenses(db, NULL, 0);
3204 if(total <= 0) return NULL;
3205 long long *all = (long long *)dt_alloc_align(sizeof(long long) * (size_t)total);
3206 if(IS_NULL_PTR(all)) return NULL;
3207 const int got = ls_db_list_lenses(db, all, total);
3208
3209 if(!have_cam)
3210 {
3211 *out_n = got;
3212 return all;
3213 }
3214
3215 int keep = 0;
3216 for(int i = 0; i < got; i++)
3217 if(ls_db_lens_fits_mount(db, all[i], cam.mount_id) == 1) all[keep++] = all[i];
3218 *out_n = keep;
3219 return all;
3220}
3221
3222static void lens_menusearch_clicked(GtkWidget *button, gpointer user_data)
3223{
3224 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
3226 (void)button;
3227
3228 int n = 0;
3229 long long *ids = _lens_ids_for_camera(self, NULL, &n);
3230 if(IS_NULL_PTR(ids)) return;
3231 lens_menu_fill(self, ids, n);
3232 dt_free_align(ids);
3233
3234 dt_gui_menu_popup(GTK_MENU(g->lens_menu), button, GDK_GRAVITY_SOUTH, GDK_GRAVITY_NORTH);
3235}
3236
3237
3238/* -- end lens -- */
3239
3240static void target_geometry_changed(GtkWidget *widget, gpointer user_data)
3241{
3242 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
3244
3245 int pos = dt_bauhaus_combobox_get(widget);
3246 p->target_geom = (dt_lens_type_t)(pos + DT_LENS_UNKNOWN + 1);
3247 p->modified = 1;
3248 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
3249}
3250
3260/* Defined further down, next to the data they work on. */
3261static int _lens_source_row(const dt_iop_lensfun_gui_data_t *g, const dt_lens_axis_t axis,
3262 const dt_lens_source_t source);
3264 const dt_iop_lensfun_params_t *const p);
3265
3275{
3276 dt_iop_lensfun_params_t probe = *p;
3277 for(dt_lens_axis_t axis = 0; axis < DT_LENS_AXIS_LAST; axis++)
3279 return _lens_corrections_available(self, &probe);
3280}
3281
3282
3295 const dt_lens_axis_t axis)
3296{
3297 const dt_lens_source_t source = _lens_source_get(p, axis);
3298 return (_lens_source_row(g, axis, source) >= 0) ? source : DT_LENS_SOURCE_OFF;
3299}
3300
3302{
3304 self, (const dt_iop_lensfun_params_t *)self->params);
3306 if(IS_NULL_PTR(g)) return;
3307
3310
3311 /* Projection and scaling are the other two thirds of the distortion pack, so they appear
3312 * exactly when that pack runs. Leaving the scale slider visible while it does nothing was
3313 * the same defect as the hidden geometry combobox that kept applying -- a control and its
3314 * effect disagreeing -- just the other way round. */
3315 /* Scaling belongs to the pack, so it shows whenever the pack runs -- from either source.
3316 * Projection does not: changing the lens's projection is a property of the database's
3317 * model, and a maker's profile describes the lens in the projection it shipped with. */
3318 gtk_widget_set_visible(g->scale, dist != DT_LENS_SOURCE_OFF);
3319 gtk_widget_set_visible(g->target_geom, dist == DT_LENS_SOURCE_LENSFUN);
3320
3321 gtk_widget_set_visible(g->tca_r, tca == DT_LENS_SOURCE_MANUAL);
3322 gtk_widget_set_visible(g->tca_b, tca == DT_LENS_SOURCE_MANUAL);
3323
3324 /* Soft deprecation of the distort/correct mode: shown only to an edit that already uses
3325 * it, so nobody loses a setting they made, and offered to nobody else. The module is
3326 * called lens correction; deliberately ADDING a lens's flaws is not something anyone has
3327 * been found to want, and the row cost more panel than the feature was worth. */
3328 gtk_widget_set_visible(g->reverse, p->inverse != 0);
3329
3330 /* Which sources this image can actually offer is decided in _lens_rebuild_axis_rows(),
3331 * which runs from gui_update() and from every setter that changes what the database can
3332 * match: a source with nothing behind it gets no row at all. Greying was not enough --
3333 * bauhaus honours entry sensitivity when the list is scrolled but not when a row is
3334 * clicked, so a greyed row was still selectable and still did nothing. Nor is greying the
3335 * whole picker: OFF and manual TCA are available to every image, database or not. */
3336}
3337
3338/* What each source is called in the panel. Indexed by dt_lens_source_t, so the name and
3339 * the value cannot drift apart the way two parallel lists would.
3340 *
3341 * The two that ARE profiles say whose measurement they are, because that is the choice the
3342 * user is actually making: the camera maker measured this body with this lens on a bench,
3343 * while the database is a community measurement of that lens model. "Embedded" and
3344 * "database" described where the numbers were stored, which is the one thing about them
3345 * nobody needs to know. The other two are not profiles and do not pretend to be. */
3346static const char *const _lens_source_names[DT_LENS_SOURCE_LAST] = {
3347 [DT_LENS_SOURCE_OFF] = N_("no correction"),
3348 [DT_LENS_SOURCE_LENSFUN] = N_("community profile"),
3349 [DT_LENS_SOURCE_EMBEDDED] = N_("vendor profile"),
3350 [DT_LENS_SOURCE_MANUAL] = N_("manual correction"),
3351};
3352
3353/* Which sources one axis's combobox offers, in the order they appear. OFF is always first
3354 * and LENSFUN always second, so a row's index means the same thing on every axis and the
3355 * user is not re-learning the widget three times. */
3364
3373 const dt_lens_source_t source)
3374{
3375 for(int i = 0; i < g->axis_rows[axis]; i++)
3376 if(g->axis_row[axis][i] == source) return i;
3377 return -1;
3378}
3379
3397{
3399 self, (const dt_iop_lensfun_params_t *)self->params);
3401 if(IS_NULL_PTR(g)) return;
3402
3403 const int offers = _lens_database_offers(self, p);
3404
3405 /* A monochrome sensor has no colour channels to shift against each other, so lateral CA
3406 * is not a correction that exists for this image at all -- _lens_mask_for_mono() already
3407 * withdraws it from every pipe. Offering it here would be a row that renders nothing,
3408 * which is the one thing the per-image row list exists to prevent. */
3409 const gboolean mono = !IS_NULL_PTR(self->dev)
3411
3412 {
3413 GtkWidget *w = g->axis_source[axis];
3414 int n = 0;
3415
3416 /* Filling a combobox is a programmatic update, and _lens_axis_source_changed() must not
3417 * mistake it for the user picking a row: it commits to history. Two callers are already
3418 * outside gui_update()'s own freeze -- the distortion handler rebuilding the TCA row,
3419 * and lens_set() rebuilding everything when the lens changes -- so the freeze belongs
3420 * here, where the widget is actually written, rather than at each call site. */
3422
3424 for(int i = 0; i < DT_LENS_SOURCE_LAST; i++)
3425 {
3426 const dt_lens_source_t src = _lens_axis_sources[axis][i];
3427 if(src == DT_LENS_SOURCE_LAST) break;
3428
3429 gboolean have;
3430 switch(src)
3431 {
3432 case DT_LENS_SOURCE_OFF: have = TRUE; break;
3433 case DT_LENS_SOURCE_LENSFUN: have = _lens_flags_have_axis(offers, axis); break;
3435 have = _lens_image_embeds(self, axis);
3436 /* A maker's aberration is measured against their OWN geometry, so it is offered
3437 * only when the geometry is theirs too. Stacking it on a database distortion
3438 * would be two calibrations of one lens applied to each other -- and the
3439 * evaluator cannot express it either: the table's CA lives in the same curve as
3440 * its distortion. The other direction is fine and supported: the table's geometry
3441 * happily wears a database or hand-typed aberration. */
3442 if(axis == DT_LENS_AXIS_TCA
3444 have = FALSE;
3445 break;
3446 case DT_LENS_SOURCE_MANUAL: have = (axis == DT_LENS_AXIS_TCA); break;
3447 default: have = FALSE; break;
3448 }
3449 if(mono && axis == DT_LENS_AXIS_TCA && src != DT_LENS_SOURCE_OFF) have = FALSE;
3450 if(!have) continue;
3451
3453 g->axis_row[axis][n++] = src;
3454 }
3455 g->axis_rows[axis] = n;
3456
3457 /* Show what the params say, or fall back to OFF -- which is row 0 and always there.
3458 * A stored source this image cannot supply has no row to select, and the correction
3459 * would not have run either; commit_params has already said so. */
3460 int row = _lens_source_row(g, axis, _lens_source_get(p, axis));
3461 if(row < 0) row = 0;
3463 }
3464}
3465
3468{
3469 for(dt_lens_axis_t axis = 0; axis < DT_LENS_AXIS_LAST; axis++)
3470 _lens_rebuild_axis_row(self, axis);
3471}
3472
3473
3476 const GtkWidget *w)
3477{
3478 for(dt_lens_axis_t axis = 0; axis < DT_LENS_AXIS_LAST; axis++)
3479 if(g->axis_source[axis] == w) return axis;
3480 return DT_LENS_AXIS_LAST;
3481}
3482
3483static void _lens_axis_source_changed(GtkWidget *widget, gpointer user_data)
3484{
3485 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
3486 if(dt_gui_widgets_suppressed()) return;
3489
3490 const dt_lens_axis_t axis = _lens_axis_of_widget(g, widget);
3491 if(axis == DT_LENS_AXIS_LAST) return;
3492
3493 /* Against what the widget currently holds, not the catalogue: rows the image cannot
3494 * supply are absent, so a catalogue index would name the wrong source. */
3495 const int row = dt_bauhaus_combobox_get(widget);
3496 if(row < 0 || row >= g->axis_rows[axis]) return;
3497
3498 const dt_lens_source_t source = g->axis_row[axis][row];
3499
3500 _lens_source_set(p, axis, source);
3501 p->modified = 1;
3502
3503 /* Distortion decides whether the maker's aberration may be offered, so its rows move when
3504 * distortion does. Rebuilding a DIFFERENT axis's widget from this handler is safe;
3505 * rebuilding the one being handled would not be. */
3507
3509 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
3510}
3511
3519 const dt_lens_axis_t axis, const char *label,
3520 const char *tooltip)
3521{
3523 g->axis_source[axis] = w;
3525 gtk_widget_set_tooltip_text(w, _(tooltip));
3526 gtk_box_pack_start(GTK_BOX(self->gui->widget), w, TRUE, TRUE, 0);
3527
3528 /* Left empty: the rows depend on the image, and gui_update() fills them. */
3529 g->axis_rows[axis] = 0;
3530 g_signal_connect(G_OBJECT(w), "value-changed",
3531 G_CALLBACK(_lens_axis_source_changed), (gpointer)self);
3532 return w;
3533}
3534
3535void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
3536{
3538
3539 /* Which controls are visible is decided in ONE place, _lens_gui_update_sensitivity(), out
3540 * of the sources the pickers are actually showing. It used to be decided here as well,
3541 * out of p->tca_override and the monochrome flag, and the two could disagree: this runs
3542 * LAST out of gui_update() and so had the final word, putting the manual coefficient
3543 * sliders back on screen for an image whose TCA picker was not offering manual at all. */
3544 if(w)
3545 {
3546 // user did modify something with some widget
3547 p->modified = 1;
3548 }
3549}
3550
3551
3553{
3554 float scale = 1.0f;
3555
3556 /* Built by THE constructor, not by hand. The hand-rolled version resolved only the
3557 * database lens, so with distortion taking the maker's embedded profile this measured a
3558 * correction the pipe was not applying -- and the result was then multiplied ON TOP of
3559 * the profile's own autoscale by get_modifier(), giving a doubled zoom. It also returned
3560 * 1.0 outright when no database lens matched, which is precisely the body an embedded
3561 * profile exists to serve. */
3563 memset(&d, 0, sizeof(d));
3564 _lens_build_data(self, p, &d);
3565 if(!_lens_data_available(&d)) return scale;
3566
3567 /* Measure the correction itself, not a scaling already applied to it. For the embedded
3568 * resolver this leaves the profile's own autoscale in place and asks what is still needed
3569 * on top of it -- which is what get_modifier() then composes. */
3570 d.scale = 1.0f;
3571
3572 const dt_image_t *img = &(self->dev->image_storage);
3573 // FIXME: get those from rawprepare IOP somehow !!!
3574 const int iwd = img->width - img->crop_x - img->crop_width,
3575 iht = img->height - img->crop_y - img->crop_height;
3576
3577 ls_modifier_t modifier;
3578 if(get_modifier(NULL, iwd, iht, &d, DT_LENS_MODIFY_ALL_AXES, FALSE, &modifier, NULL))
3579 scale = ls_modifier_autoscale(&modifier);
3580 return scale;
3581}
3582
3583static void autoscale_pressed(GtkWidget *button, gpointer user_data)
3584{
3585 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
3588 const float scale = get_autoscale(self, p);
3589 p->modified = 1;
3590 dt_bauhaus_slider_set(g->scale, scale);
3591 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
3592}
3593
3611 const dt_iop_lensfun_params_t *const p)
3612{
3613 if(IS_NULL_PTR(self->dev)) return 0;
3614
3616 memset(&d, 0, sizeof(d));
3617 _lens_build_data(self, p, &d);
3618 if(!_lens_data_available(&d)) return 0;
3619
3620 /* The frame the correction is expressed over. Only its aspect matters to which axes
3621 * resolve, so the full image is a fine stand-in when the pipe has not published one. */
3622 dt_iop_roi_t roi = { 0, 0, 0, 0, 1.f };
3623 if(!dt_dev_module_geometry_gui(self->dev, self, &roi, NULL) || roi.width <= 0
3624 || roi.height <= 0)
3625 {
3626 const dt_image_t *img = &self->dev->image_storage;
3627 roi.width = img->width;
3628 roi.height = img->height;
3629 }
3630 if(roi.width <= 0 || roi.height <= 0) return 0;
3631
3633 int modflags = 0;
3634 ls_modifier_t m;
3635 get_modifier(&modflags, roi.width, roi.height, &d, mask, FALSE, &m, NULL);
3636 return modflags & LENSFUN_MODFLAG_MASK;
3637}
3638
3639
3640void gui_init(struct dt_iop_module_t *self)
3641{
3643
3644 g->camera_id = -1;
3645 g->camera_menu = NULL;
3646 g->lens_menu = NULL;
3647
3648
3649 self->gui->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
3650 gtk_widget_set_name(self->gui->widget, "lens-module");
3651
3652 /* One button per row, not two. Each row used to carry a second, unlabelled arrow that
3653 * listed the Exif match instead of the whole catalogue -- for the camera, a menu of
3654 * exactly one entry, the one reload_defaults() had already applied.
3655 *
3656 * Filtering these lists by Exif is useless by construction: they are opened precisely
3657 * BECAUSE Exif matching failed. A lens with no CPU reports no lens at all, so there is
3658 * nothing to filter on; a body absent from the database is chosen by picking a near
3659 * relative deliberately -- a Mk I for a Mk II -- which an Exif filter would never
3660 * suggest. Both cases want the full list, which is what the model button has always
3661 * shown. */
3662
3663 // camera selector
3664 GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
3665 g->camera_model = dt_iop_button_new(self, N_("camera model"),
3666 G_CALLBACK(camera_menusearch_clicked), FALSE, 0, (GdkModifierType)0,
3667 NULL, 0, hbox);
3668 gtk_box_pack_start(GTK_BOX(self->gui->widget), hbox, TRUE, TRUE, 0);
3669
3670 // lens selector
3671 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
3672 g->lens_model = dt_iop_button_new(self, N_("lens model"),
3673 G_CALLBACK(lens_menusearch_clicked), FALSE, 0, (GdkModifierType)0,
3674 NULL, 0, hbox);
3675 gtk_box_pack_start(GTK_BOX(self->gui->widget), hbox, TRUE, TRUE, 0);
3676
3677 // lens properties
3678 g->lens_param_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
3679 gtk_box_pack_start(GTK_BOX(self->gui->widget), g->lens_param_box, TRUE, TRUE, 0);
3680
3681
3682 /* The panel reads top to bottom as: what was detected, how much to rescale, then one
3683 * section per correction axis. Each section is its source combobox followed by exactly
3684 * the controls that source uses -- geometry under distortion, the manual coefficients
3685 * under TCA -- so a control is never far from the thing that decides whether it applies.
3686 *
3687 * That is why these are written out one at a time rather than looped: the loop would have
3688 * to put every axis's extra controls somewhere else, and "somewhere else" is how the old
3689 * panel ended up with a TCA override checkbox three rows away from the TCA setting. */
3690
3691 // 2. vignetting
3692 _lens_add_axis_combobox(self, g, DT_LENS_AXIS_VIGNETTING, N_("vignetting"),
3693 N_("correct the lens's light falloff, and where to take it from"));
3694
3695 // 3. distortion, with the projection and the scaling that go with it
3696 _lens_add_axis_combobox(self, g, DT_LENS_AXIS_DISTORTION, N_("distortion"),
3697 N_("correct the lens's geometric distortion, and where to take it from"));
3698
3700 dt_bauhaus_widget_set_label(g->target_geom, N_("geometry"));
3701 gtk_box_pack_start(GTK_BOX(self->gui->widget), g->target_geom, TRUE, TRUE, 0);
3702 gtk_widget_set_tooltip_text(g->target_geom, _("target geometry"));
3703 dt_bauhaus_combobox_add(g->target_geom, _("rectilinear"));
3704 dt_bauhaus_combobox_add(g->target_geom, _("fish-eye"));
3705 dt_bauhaus_combobox_add(g->target_geom, _("panoramic"));
3706 dt_bauhaus_combobox_add(g->target_geom, _("equirectangular"));
3707 dt_bauhaus_combobox_add(g->target_geom, _("orthographic"));
3708 dt_bauhaus_combobox_add(g->target_geom, _("stereographic"));
3709 dt_bauhaus_combobox_add(g->target_geom, _("equisolid angle"));
3710 dt_bauhaus_combobox_add(g->target_geom, _("thoby fish-eye"));
3711 g_signal_connect(G_OBJECT(g->target_geom), "value-changed", G_CALLBACK(target_geometry_changed),
3712 (gpointer)self);
3713
3714 /* Scaling closes the distortion section rather than opening the module. It is the third
3715 * of the pack -- distortion, projection, scale -- and it only ever runs when that pack
3716 * runs, so putting it at the top left the user reading a control whose effect lived four
3717 * rows further down, under a different heading. */
3718 g->scale = dt_bauhaus_slider_from_params(self, N_("scale"));
3721 g_signal_connect(G_OBJECT(g->scale), "quad-pressed", G_CALLBACK(autoscale_pressed), self);
3722 gtk_widget_set_tooltip_text(g->scale, _("auto scale"));
3723
3724 // 4. chromatic aberrations, and the coefficients the manual source uses
3725 _lens_add_axis_combobox(self, g, DT_LENS_AXIS_TCA, N_("chromatic aberrations"),
3726 N_("correct lateral chromatic aberration, and where to take it from"));
3727
3728 /* p->tca_override has NO widget. It survives only as storage -- _lens_source_set()
3729 * writes it whenever TCA is set to manual, so an edit saved by this version is still read
3730 * correctly by one that predates the per-axis sources. The checkbox it used to drive is
3731 * gone: "TCA = manual correction" is a row of the combobox above, and one state behind two
3732 * controls is one state they can disagree about. */
3733
3734 g->tca_r = dt_bauhaus_slider_from_params(self, "tca_r");
3736 gtk_widget_set_tooltip_text(g->tca_r, _("Transversal Chromatic Aberration red"));
3737
3738 g->tca_b = dt_bauhaus_slider_from_params(self, "tca_b");
3740 gtk_widget_set_tooltip_text(g->tca_b, _("Transversal Chromatic Aberration blue"));
3741
3742 /* Last, because it is on its way out: it inverts every axis at once, nobody has been
3743 * found who uses it, and it is kept only so existing edits that set it keep rendering. */
3744 g->reverse = dt_bauhaus_combobox_from_params(self, "inverse");
3745 dt_bauhaus_combobox_add(g->reverse, _("correct"));
3746 dt_bauhaus_combobox_add(g->reverse, _("distort"));
3747 gtk_widget_set_tooltip_text(g->reverse, _("correct distortions or apply them"));
3748
3749
3750}
3751
3752void gui_update(struct dt_iop_module_t *self)
3753{
3754 // let gui elements reflect params
3756
3757 /* What is actually IN FORCE, which for an edit saved with modified == 0 is default_params
3758 * rather than params -- see _lens_effective_params(). The view shows that; it does not
3759 * write it back. This function used to memcpy default_params over self->params right
3760 * here, which mutated an edit from a panel refresh, and did not even reach the widgets it
3761 * was meant to fix: dt_iop_gui_update() runs dt_bauhaus_update_module() out of
3762 * self->params BEFORE calling this, so the copy only ever showed up one refresh late. */
3764 self, (const dt_iop_lensfun_params_t *)self->params);
3765
3766 // these are the wrong (untranslated) strings in general but that's ok, they will be overwritten further
3767 // down
3768 gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(g->camera_model))), p->camera);
3769 gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(g->lens_model))), p->lens);
3770 gtk_widget_set_tooltip_text(g->camera_model, "");
3771 gtk_widget_set_tooltip_text(g->lens_model, "");
3772
3775
3776 /* dt_bauhaus_update_module() has already synced these from self->params. Re-set them from
3777 * the effective params so a modified == 0 edit shows what it renders rather than what it
3778 * stores; for every other edit the two are the same object and this is a no-op. */
3779 dt_bauhaus_combobox_set(g->target_geom, p->target_geom - DT_LENS_UNKNOWN - 1);
3780 dt_bauhaus_combobox_set(g->reverse, p->inverse);
3781 dt_bauhaus_slider_set(g->scale, p->scale);
3782 dt_bauhaus_slider_set(g->tca_r, p->tca_r);
3783 dt_bauhaus_slider_set(g->tca_b, p->tca_b);
3784
3785 g->camera_id = -1;
3786 if(p->camera[0])
3787 {
3788 /* Resolved the same way the pipeline resolves it. Recovering the id by comparing the
3789 * params string against stored model names cannot work: matching is on the normalised
3790 * form, so "NIKON D5300" finds a row whose model column reads "D5300" -- which is why
3791 * this label was blank on every image while the correction itself was applied. */
3792 ls_camera_t cam;
3793 camera_set(self, _ls_find_camera(NULL, p->camera, &cam) ? cam.id : -1);
3794 }
3795 if(g->camera_id >= 0 && p->lens[0])
3796 {
3797 char model[200];
3798 parse_model(p->lens, model, sizeof(model));
3799 int n = 0;
3800 long long *ids = _lens_ids_for_camera(self, model[0] ? model : NULL, &n);
3801 lens_set(self, (n > 0 && !IS_NULL_PTR(ids)) ? ids[0] : -1);
3802 if(!IS_NULL_PTR(ids)) dt_free_align(ids);
3803 }
3804 else
3805 {
3806 lens_set(self, -1);
3807 }
3808
3809
3810
3811 gui_changed(self, NULL, NULL);
3812}
3813
3815{
3816
3818}
3819
3820
3821// clang-format off
3822// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
3823// vim: shiftwidth=2 expandtab tabstop=2 cindent
3824// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
3825// 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:3346
void dt_bauhaus_combobox_clear(GtkWidget *widget)
Definition bauhaus.c:2000
void dt_bauhaus_combobox_set_editable(GtkWidget *widget, int editable)
Definition bauhaus.c:1885
int dt_bauhaus_combobox_get(GtkWidget *widget)
Definition bauhaus.c:2139
const char * dt_bauhaus_combobox_get_text(GtkWidget *widget)
Definition bauhaus.c:1973
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3334
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
Definition bauhaus.c:2093
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:1701
void dt_bauhaus_combobox_add(GtkWidget *widget, const char *text)
Definition bauhaus.c:1827
void dt_bauhaus_widget_set_quad_paint(GtkWidget *widget, dt_bauhaus_quad_paint_f f, int paint_flags, void *paint_data)
Definition bauhaus.c:1557
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:511
struct dt_bauhaus_t * dt_bauhaus_get_global(void)
Definition darktable.c:649
#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:1818
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
Definition develop.h:123
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:319
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:1909
@ 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
static const dt_dev_pixelpipe_t * pipe
Definition layers.h:49
static const dt_dev_pixelpipe_t const dt_dev_pixelpipe_iop_t * piece
Definition layers.h:50
static const dt_iop_drawlayer_params_t * params
Definition layers.h:80
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:1798
#define DT_LENS_MODIFY_ANSEL_MASK
Definition lens.c:196
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:2225
static const char *const _lens_source_names[DT_LENS_SOURCE_LAST]
Definition lens.c:3346
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:1832
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:2789
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:3467
static const char * _lens_type_name(int type)
A projection's name, replacing lfLens::GetLensTypeDesc().
Definition lens.c:2864
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:2220
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:2039
static int ptr_array_find_sorted(const GPtrArray *array, const void *item, GCompareFunc compare)
Definition lens.c:2565
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:498
static int _lens_mask_for_mono(const gboolean monochrome)
Which axes may be attempted on this image.
Definition lens.c:517
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:3372
static void _lens_free_data(void *ptr)
Definition lens.c:2257
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:3518
void reload_defaults(dt_iop_module_t *module)
Definition lens.c:2366
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:349
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:355
dt_lens_modify_t
What modify_flags holds, bit by bit.
Definition lens.c:167
@ DT_LENS_MODIFY_GEOMETRY
Definition lens.c:172
@ DT_LENS_MODIFY_VIGNETTING
Definition lens.c:170
@ DT_LENS_SOURCE_SHIFT_VIGNETTING
Definition lens.c:178
@ DT_LENS_MODIFY_SCALE
Definition lens.c:173
@ DT_LENS_MODIFY_DISTORTION
Definition lens.c:171
@ DT_LENS_SOURCE_BITS
Definition lens.c:179
@ DT_LENS_SOURCE_SHIFT_TCA
Definition lens.c:176
@ DT_LENS_SOURCE_SHIFT_DISTORTION
Definition lens.c:177
@ DT_LENS_MODIFY_TCA
Definition lens.c:169
dt_lens_source_t
Where one axis takes its correction from. The same vocabulary for all three.
Definition lens.c:251
@ DT_LENS_SOURCE_MANUAL
Definition lens.c:255
@ DT_LENS_SOURCE_LAST
Definition lens.c:256
@ DT_LENS_SOURCE_LENSFUN
Definition lens.c:253
@ DT_LENS_SOURCE_EMBEDDED
Definition lens.c:254
@ DT_LENS_SOURCE_OFF
Definition lens.c:252
static void lens_comboentry_distance_update(GtkWidget *widget, dt_iop_module_t *self)
Definition lens.c:2848
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:2303
static const dt_lens_source_t _lens_axis_sources[DT_LENS_AXIS_LAST][DT_LENS_SOURCE_LAST]
Definition lens.c:3356
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:2902
static gboolean _lens_flags_move_pixels(const int modify_flags)
Does this flag word describe anything that MOVES pixels?
Definition lens.c:482
static void autoscale_pressed(GtkWidget *button, gpointer user_data)
Definition lens.c:3583
__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:3610
static void camera_menu_select(GtkMenuItem *menuitem, gpointer user_data)
Definition lens.c:2688
static const dt_geometry_vtable_t _lens_geometry_vtable
Definition lens.c:2309
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:331
static void lens_comboentry_focal_update(GtkWidget *widget, dt_iop_module_t *self)
Definition lens.c:2830
static void camera_menusearch_clicked(GtkWidget *button, gpointer user_data)
Definition lens.c:2811
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:536
static void lens_comboentry_aperture_update(GtkWidget *widget, dt_iop_module_t *self)
Definition lens.c:2839
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:2887
static void _lens_axis_source_changed(GtkWidget *widget, gpointer user_data)
Definition lens.c:3483
dt_iop_lensfun_modflag_t
Definition lens.c:285
@ LENSFUN_MODFLAG_MASK
Definition lens.c:288
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:468
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition lens.c:2331
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:226
@ DT_LENS_AXIS_DISTORTION
Definition lens.c:228
@ DT_LENS_AXIS_TCA
Definition lens.c:227
@ DT_LENS_AXIS_VIGNETTING
Definition lens.c:229
@ DT_LENS_AXIS_LAST
Definition lens.c:230
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:1766
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:374
static void ptr_array_insert_index(GPtrArray *array, const void *item, int index)
Definition lens.c:2594
gboolean geometry_record(dt_iop_module_t *self, const void *params, dt_geometry_record_t *record)
Definition lens.c:2315
void gui_update(struct dt_iop_module_t *self)
Refresh GUI controls from current params and configuration.
Definition lens.c:3752
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:2266
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:505
static int precision(double x, double adj)
Definition lens.c:2507
void gui_init(struct dt_iop_module_t *self)
Definition lens.c:3640
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:2618
void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
Definition lens.c:3535
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:3475
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:3177
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:1731
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:3274
static void _lens_gui_update_sensitivity(dt_iop_module_t *self)
Definition lens.c:3301
void cleanup_global(dt_iop_module_so_t *module)
Definition lens.c:2486
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:2297
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:3110
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:392
int flags()
Definition lens.c:944
static void lens_menusearch_clicked(GtkWidget *button, gpointer user_data)
Definition lens.c:3222
static float get_autoscale(dt_iop_module_t *self, dt_iop_lensfun_params_t *p)
Definition lens.c:3552
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:1901
void gui_cleanup(struct dt_iop_module_t *self)
Definition lens.c:3814
dt_lens_type_t
Definition lens.c:260
@ DT_LENS_RECTILINEAR
Definition lens.c:262
@ DT_LENS_FISHEYE_THOBY
Definition lens.c:269
@ DT_LENS_FISHEYE_EQUISOLID
Definition lens.c:268
@ DT_LENS_FISHEYE_ORTHOGRAPHIC
Definition lens.c:266
@ DT_LENS_UNKNOWN
Definition lens.c:261
@ DT_LENS_FISHEYE
Definition lens.c:263
@ DT_LENS_PANORAMIC
Definition lens.c:264
@ DT_LENS_FISHEYE_STEREOGRAPHIC
Definition lens.c:267
@ DT_LENS_EQUIRECTANGULAR
Definition lens.c:265
static int ptr_array_insert_sorted(GPtrArray *array, const void *item, GCompareFunc compare)
Definition lens.c:2531
static void lens_menu_select(GtkMenuItem *menuitem, gpointer user_data)
Definition lens.c:3081
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:3396
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:3240
static void parse_model(const char *txt, char *model, size_t sz_model)
Definition lens.c:2802
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition lens.c:2337
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:415
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:3293
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:2643
static void delete_children(GtkWidget *widget, gpointer data)
Definition lens.c:2857
static void _ls_tls_free(gpointer data)
Definition lens.c:627
void init_global(dt_iop_module_so_t *module)
Definition lens.c:2348
#define DT_LENS_MODIFY_ALL_AXES
Definition lens.c:206
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:460
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:2026
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:1580
#define DT_LENS_MODIFY_DISTORTION_PACK
The three lensfun axes that always move together, as one flag.
Definition lens.c:192
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:1894
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:432
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:2723
_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
dt_imageio_retval_t status
Definition mipmap_cache.c:8
uint32_t height
Definition mipmap_cache.c:1
#define o
#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:2612
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2506
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:2737
void dt_opencl_free_kernel(const int kernel)
Definition opencl.c:2549
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:2603
#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:2683
@ 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:617
ls_camera_t cam
Definition lens.c:613
ls_db_t * db
Definition lens.c:609
gboolean cam_cached
Definition lens.c:615
long long lens_id
Definition lens.c:618
gboolean lens_cached
Definition lens.c:619
gboolean tried
Definition lens.c:610
gboolean cam_found
Definition lens.c:614
char cam_key[512]
Definition lens.c:612
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:406
float exif_focus_distance
Definition image.h:381
ls_vendor_data_t exif_correction
Definition image.h:478
float exif_aperture
Definition image.h:378
int32_t crop_height
Definition image.h:407
int32_t width
Definition image.h:406
float exif_focal_length
Definition image.h:380
char exif_maker[64]
Definition image.h:383
int32_t crop_y
Definition image.h:407
char exif_lens[128]
Definition image.h:385
int32_t crop_x
Definition image.h:407
int32_t p_height
Definition image.h:406
int32_t p_width
Definition image.h:406
char exif_model[64]
Definition image.h:384
int32_t crop_width
Definition image.h:407
float exif_crop
Definition image.h:382
enum dt_interpolation_type id
dt_iop_lensfun_data_t data
Definition lens.c:2253
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:565
GtkMenu * camera_menu
Definition lens.c:557
int axis_rows[DT_LENS_AXIS_LAST]
Definition lens.c:566
GtkWidget * target_geom
Definition lens.c:567
GtkWidget * tca_r
Definition lens.c:567
GtkWidget * cbe[3]
Definition lens.c:555
GtkWidget * axis_source[DT_LENS_AXIS_LAST]
Definition lens.c:561
GtkWidget * lens_param_box
Definition lens.c:554
GtkWidget * reverse
Definition lens.c:567
GtkWidget * scale
Definition lens.c:567
GtkWidget * lens_model
Definition lens.c:558
GtkWidget * tca_b
Definition lens.c:567
GtkWidget * camera_model
Definition lens.c:556
gboolean tca_override
Definition lens.c:304
dt_lens_type_t target_geom
Definition lens.c:301
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)