1 /*
2     This file is part of darktable,
3     Copyright (C) 2019-2021 darktable developers.
4 
5     darktable is free software: you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation, either version 3 of the License, or
8     (at your option) any later version.
9 
10     darktable is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with darktable.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 extern "C" {
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 #include "bauhaus/bauhaus.h"
25 #include "common/interpolation.h"
26 #include "common/file_location.h"
27 #include "common/imagebuf.h"
28 #include "common/opencl.h"
29 #include "control/control.h"
30 #include "develop/develop.h"
31 #include "develop/imageop.h"
32 #include "develop/imageop_gui.h"
33 #include "develop/tiling.h"
34 #include "dtgtk/button.h"
35 #include "dtgtk/resetlabel.h"
36 #include "gui/accelerators.h"
37 #include "gui/draw.h"
38 #include "gui/gtk.h"
39 #include "iop/iop_api.h"
40 #include <assert.h>
41 #include <ctype.h>
42 #include <gtk/gtk.h>
43 #include <inttypes.h>
44 #include <math.h>
45 #include <stdlib.h>
46 #include <string.h>
47 }
48 
49 #include <lensfun.h>
50 
51 extern "C" {
52 
53 #if LF_VERSION < ((0 << 24) | (2 << 16) | (9 << 8) | 0)
54 #define LF_SEARCH_SORT_AND_UNIQUIFY 2
55 #endif
56 
57 #if LF_VERSION == ((0 << 24) | (3 << 16) | (95 << 8) | 0)
58 #define LF_0395
59 #endif
60 
61 DT_MODULE_INTROSPECTION(5, dt_iop_lensfun_params_t)
62 
63 typedef enum dt_iop_lensfun_modflag_t
64 {
65   LENSFUN_MODFLAG_NONE = 0,
66   LENSFUN_MODFLAG_ALL = LF_MODIFY_DISTORTION | LF_MODIFY_TCA | LF_MODIFY_VIGNETTING,
67   LENSFUN_MODFLAG_DIST_TCA = LF_MODIFY_DISTORTION | LF_MODIFY_TCA,
68   LENSFUN_MODFLAG_DIST_VIGN = LF_MODIFY_DISTORTION | LF_MODIFY_VIGNETTING,
69   LENSFUN_MODFLAG_TCA_VIGN = LF_MODIFY_TCA | LF_MODIFY_VIGNETTING,
70   LENSFUN_MODFLAG_DIST = LF_MODIFY_DISTORTION,
71   LENSFUN_MODFLAG_TCA = LF_MODIFY_TCA,
72   LENSFUN_MODFLAG_VIGN = LF_MODIFY_VIGNETTING,
73   LENSFUN_MODFLAG_MASK = LF_MODIFY_DISTORTION | LF_MODIFY_TCA | LF_MODIFY_VIGNETTING
74 } dt_iop_lensfun_modflag_t;
75 
76 typedef struct dt_iop_lensfun_modifier_t
77 {
78   char name[80];
79   int pos; // position in combo box
80   int modflag;
81 } dt_iop_lensfun_modifier_t;
82 
83 typedef struct dt_iop_lensfun_params_t
84 {
85   int modify_flags;
86   int inverse; // $MIN: 0 $MAX: 1 $DEFAULT: 0 $DESCRIPTION: "mode"
87   float scale; // $MIN: 0.1 $MAX: 2.0 $DEFAULT: 1.0
88   float crop;
89   float focal;
90   float aperture;
91   float distance;
92   lfLensType target_geom; // $DEFAULT: LF_RECTILINEAR $DESCRIPTION: "geometry"
93   char camera[128];
94   char lens[128];
95   gboolean tca_override; // $DEFAULT: FALSE $DESCRIPTION: "TCA overwrite"
96   float tca_r; // $MIN: 0.99 $MAX: 1.01 $DEFAULT: 1.0 $DESCRIPTION: "TCA red"
97   float tca_b; // $MIN: 0.99 $MAX: 1.01 $DEFAULT: 1.0 $DESCRIPTION: "TCA blue"
98   int modified; // $DEFAULT: 0 did user changed anything from automatically detected?
99 } dt_iop_lensfun_params_t;
100 
101 typedef struct dt_iop_lensfun_gui_data_t
102 {
103   const lfCamera *camera;
104   GtkWidget *lens_param_box;
105   GtkWidget *detection_warning;
106   GtkWidget *cbe[3];
107   GtkWidget *camera_model;
108   GtkMenu *camera_menu;
109   GtkWidget *lens_model;
110   GtkMenu *lens_menu;
111   GtkWidget *modflags, *target_geom, *reverse, *tca_override, *tca_r, *tca_b, *scale;
112   GtkWidget *find_lens_button;
113   GtkWidget *find_camera_button;
114   GList *modifiers;
115   GtkLabel *message;
116   int corrections_done;
117 } dt_iop_lensfun_gui_data_t;
118 
119 typedef struct dt_iop_lensfun_global_data_t
120 {
121   lfDatabase *db;
122   int kernel_lens_distort_bilinear;
123   int kernel_lens_distort_bicubic;
124   int kernel_lens_distort_lanczos2;
125   int kernel_lens_distort_lanczos3;
126   int kernel_lens_vignette;
127 } dt_iop_lensfun_global_data_t;
128 
129 typedef struct dt_iop_lensfun_data_t
130 {
131   lfLens *lens;
132   int modify_flags;
133   int inverse;
134   float scale;
135   float crop;
136   float focal;
137   float aperture;
138   float distance;
139   lfLensType target_geom;
140   gboolean do_nan_checks;
141   gboolean tca_override;
142   lfLensCalibTCA custom_tca;
143 } dt_iop_lensfun_data_t;
144 
145 
name()146 const char *name()
147 {
148   return _("lens correction");
149 }
150 
aliases()151 const char *aliases()
152 {
153   return _("vignette|chromatic aberrations|distortion");
154 }
155 
description(struct dt_iop_module_t * self)156 const char *description(struct dt_iop_module_t *self)
157 {
158   return dt_iop_set_description(self, _("correct lenses optical flaws"),
159                                       _("corrective"),
160                                       _("linear, RGB, scene-referred"),
161                                       _("geometric and reconstruction, RGB"),
162                                       _("linear, RGB, scene-referred"));
163 }
164 
165 
default_group()166 int default_group()
167 {
168   return IOP_GROUP_CORRECT | IOP_GROUP_TECHNICAL;
169 }
170 
operation_tags()171 int operation_tags()
172 {
173   return IOP_TAG_DISTORT;
174 }
175 
flags()176 int flags()
177 {
178   return IOP_FLAGS_ALLOW_TILING | IOP_FLAGS_TILING_FULL_ROI | IOP_FLAGS_UNSAFE_COPY;
179 }
180 
default_colorspace(dt_iop_module_t * self,dt_dev_pixelpipe_t * pipe,dt_dev_pixelpipe_iop_t * piece)181 int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
182 {
183   return iop_cs_rgb;
184 }
185 
legacy_params(dt_iop_module_t * self,const void * const old_params,const int old_version,void * new_params,const int new_version)186 int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version,
187                   void *new_params, const int new_version)
188 {
189   if(old_version == 2 && new_version == 5)
190   {
191     // legacy params of version 2; version 1 comes from ancient times and seems to be forgotten by now
192     typedef struct
193     {
194       int modify_flags;
195       int inverse;
196       float scale;
197       float crop;
198       float focal;
199       float aperture;
200       float distance;
201       lfLensType target_geom;
202       char camera[52];
203       char lens[52];
204       int tca_override;
205       float tca_r, tca_b;
206     } dt_iop_lensfun_params_v2_t;
207 
208     const dt_iop_lensfun_params_v2_t *o = (dt_iop_lensfun_params_v2_t *)old_params;
209     dt_iop_lensfun_params_t *n = (dt_iop_lensfun_params_t *)new_params;
210     dt_iop_lensfun_params_t *d = (dt_iop_lensfun_params_t *)self->default_params;
211 
212     *n = *d; // start with a fresh copy of default parameters
213 
214     n->modify_flags = o->modify_flags;
215     n->inverse = o->inverse;
216     n->scale = o->scale;
217     n->crop = o->crop;
218     n->focal = o->focal;
219     n->aperture = o->aperture;
220     n->distance = o->distance;
221     n->target_geom = o->target_geom;
222     n->tca_override = o->tca_override;
223     g_strlcpy(n->camera, o->camera, sizeof(n->camera));
224     g_strlcpy(n->lens, o->lens, sizeof(n->lens));
225     n->modified = 1;
226 
227     // old versions had R and B swapped
228     n->tca_r = o->tca_b;
229     n->tca_b = o->tca_r;
230 
231     return 0;
232   }
233   if(old_version == 3 && new_version == 5)
234   {
235     typedef struct
236     {
237       int modify_flags;
238       int inverse;
239       float scale;
240       float crop;
241       float focal;
242       float aperture;
243       float distance;
244       lfLensType target_geom;
245       char camera[128];
246       char lens[128];
247       int tca_override;
248       float tca_r, tca_b;
249     } dt_iop_lensfun_params_v3_t;
250 
251     const dt_iop_lensfun_params_v3_t *o = (dt_iop_lensfun_params_v3_t *)old_params;
252     dt_iop_lensfun_params_t *n = (dt_iop_lensfun_params_t *)new_params;
253     dt_iop_lensfun_params_t *d = (dt_iop_lensfun_params_t *)self->default_params;
254 
255     *n = *d; // start with a fresh copy of default parameters
256 
257     memcpy(n, o, sizeof(dt_iop_lensfun_params_t) - sizeof(int));
258 
259     // one more parameter and changed parameters in case we autodetect
260     n->modified = 1;
261 
262     // old versions had R and B swapped
263     n->tca_r = o->tca_b;
264     n->tca_b = o->tca_r;
265 
266     return 0;
267   }
268 
269   if(old_version == 4 && new_version == 5)
270   {
271     typedef struct
272     {
273       int modify_flags;
274       int inverse;
275       float scale;
276       float crop;
277       float focal;
278       float aperture;
279       float distance;
280       lfLensType target_geom;
281       char camera[128];
282       char lens[128];
283       int tca_override;
284       float tca_r, tca_b;
285       int modified;
286     } dt_iop_lensfun_params_v4_t;
287 
288     const dt_iop_lensfun_params_v4_t *o = (dt_iop_lensfun_params_v4_t *)old_params;
289     dt_iop_lensfun_params_t *n = (dt_iop_lensfun_params_t *)new_params;
290     dt_iop_lensfun_params_t *d = (dt_iop_lensfun_params_t *)self->default_params;
291 
292     *n = *d; // start with a fresh copy of default parameters
293 
294     memcpy(n, o, sizeof(dt_iop_lensfun_params_t));
295 
296     // old versions had R and B swapped
297     n->tca_r = o->tca_b;
298     n->tca_b = o->tca_r;
299 
300     return 0;
301   }
302 
303   return 1;
304 }
305 
_lens_sanitize(const char * orig_lens)306 static char *_lens_sanitize(const char *orig_lens)
307 {
308   const char *found_or = strstr(orig_lens, " or ");
309   const char *found_parenthesis = strstr(orig_lens, " (");
310 
311   if(found_or || found_parenthesis)
312   {
313     size_t pos_or = (size_t)(found_or - orig_lens);
314     size_t pos_parenthesis = (size_t)(found_parenthesis - orig_lens);
315     size_t pos = pos_or < pos_parenthesis ? pos_or : pos_parenthesis;
316 
317     if(pos > 0)
318     {
319       char *new_lens = (char *)malloc(pos + 1);
320 
321       strncpy(new_lens, orig_lens, pos);
322       new_lens[pos] = '\0';
323 
324       return new_lens;
325     }
326     else
327     {
328       char *new_lens = strdup(orig_lens);
329       return new_lens;
330     }
331   }
332   else
333   {
334     char *new_lens = strdup(orig_lens);
335     return new_lens;
336   }
337 }
338 
get_modifier(int * mods_done,int w,int h,const dt_iop_lensfun_data_t * d,int mods_filter,gboolean force_inverse)339 static lfModifier * get_modifier(int *mods_done, int w, int h, const dt_iop_lensfun_data_t *d, int mods_filter, gboolean force_inverse)
340 {
341   lfModifier *mod;
342   int mods_todo = d->modify_flags & mods_filter;
343   int mods_done_tmp = 0;
344 
345 #ifdef LF_0395
346   mod = new lfModifier(d->crop, w, h, LF_PF_F32, (force_inverse) ? !d->inverse : d->inverse);
347   if(mods_todo & LF_MODIFY_DISTORTION)
348     mods_done_tmp |= mod->EnableDistortionCorrection(d->lens, d->focal);
349   if((mods_todo & LF_MODIFY_GEOMETRY) && (d->lens->Type != d->target_geom))
350     mods_done_tmp |= mod->EnableProjectionTransform(d->lens, d->focal, d->target_geom);
351   if((mods_todo & LF_MODIFY_SCALE) && (d->scale != 1.0))
352     mods_done_tmp |= mod->EnableScaling(d->scale);
353   if(mods_todo & LF_MODIFY_TCA)
354   {
355     if(d->tca_override) mods_done_tmp |= mod->EnableTCACorrection(d->custom_tca);
356     else mods_done_tmp |= mod->EnableTCACorrection(d->lens, d->focal);
357   }
358   if(mods_todo & LF_MODIFY_VIGNETTING)
359     mods_done_tmp |= mod->EnableVignettingCorrection(d->lens, d->focal, d->aperture, d->distance);
360 #else
361   mod = new lfModifier(d->lens, d->crop, w, h);
362   mods_done_tmp = mod->Initialize(d->lens, LF_PF_F32, d->focal, d->aperture, d->distance, d->scale, d->target_geom, mods_todo,
363                                   (force_inverse) ? !d->inverse : d->inverse);
364 #endif
365 
366   if(mods_done) *mods_done = mods_done_tmp;
367   return mod;
368 }
369 
process(dt_iop_module_t * self,dt_dev_pixelpipe_iop_t * piece,const void * const ivoid,void * const ovoid,const dt_iop_roi_t * const roi_in,const dt_iop_roi_t * const roi_out)370 void process(dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid,
371              const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
372 {
373   const dt_iop_lensfun_data_t *const d = (dt_iop_lensfun_data_t *)piece->data;
374   dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
375 
376   const int ch = piece->colors;
377   const int ch_width = ch * roi_in->width;
378   const int mask_display = piece->pipe->mask_display;
379 
380   const unsigned int pixelformat = ch == 3 ? LF_CR_3(RED, GREEN, BLUE) : LF_CR_4(RED, GREEN, BLUE, UNKNOWN);
381 
382   if(!d->lens || !d->lens->Maker || d->crop <= 0.0f)
383   {
384     dt_iop_image_copy_by_size((float*)ovoid, (float*)ivoid, roi_out->width, roi_out->height, ch);
385     return;
386   }
387 
388   const float orig_w = roi_in->scale * piece->buf_in.width, orig_h = roi_in->scale * piece->buf_in.height;
389 
390   dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
391 
392   int modflags;
393   const lfModifier *modifier = get_modifier(&modflags, orig_w, orig_h, d, LF_MODIFY_ALL, FALSE);
394 
395   dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
396 
397   const struct dt_interpolation *const interpolation = dt_interpolation_new(DT_INTERPOLATION_USERPREF_WARP);
398 
399   if(d->inverse)
400   {
401     // reverse direction (useful for renderings)
402     if(modflags & (LF_MODIFY_TCA | LF_MODIFY_DISTORTION | LF_MODIFY_GEOMETRY | LF_MODIFY_SCALE))
403     {
404       // acquire temp memory for distorted pixel coords
405       const size_t bufsize = (size_t)roi_out->width * 2 * 3;
406 
407       size_t padded_bufsize;
408       float *const buf = dt_alloc_perthread_float(bufsize, &padded_bufsize);
409 
410 #ifdef _OPENMP
411 #pragma omp parallel for default(none) \
412       dt_omp_firstprivate(padded_bufsize, ch, ch_width, d, interpolation, ivoid, mask_display, ovoid, roi_in, roi_out)	\
413       dt_omp_sharedconst(buf)						\
414       shared(modifier)							\
415       schedule(static)
416 #endif
417       for(int y = 0; y < roi_out->height; y++)
418       {
419         float *bufptr = (float*)dt_get_perthread(buf, padded_bufsize);
420         modifier->ApplySubpixelGeometryDistortion(roi_out->x, roi_out->y + y, roi_out->width, 1, bufptr);
421 
422         // reverse transform the global coords from lf to our buffer
423         float *out = ((float *)ovoid) + (size_t)y * roi_out->width * ch;
424         for(int x = 0; x < roi_out->width; x++, bufptr += 6, out += ch)
425         {
426           for(int c = 0; c < 3; c++)
427           {
428             if(d->do_nan_checks && (!isfinite(bufptr[c * 2]) || !isfinite(bufptr[c * 2 + 1])))
429             {
430               out[c] = 0.0f;
431               continue;
432             }
433 
434             const float *const inptr = (const float *const)ivoid + (size_t)c;
435             const float pi0 = fmaxf(fminf(bufptr[c * 2] - roi_in->x, roi_in->width - 1.0f), 0.0f);
436             const float pi1 = fmaxf(fminf(bufptr[c * 2 + 1] - roi_in->y, roi_in->height - 1.0f), 0.0f);
437             out[c] = dt_interpolation_compute_sample(interpolation, inptr, pi0, pi1, roi_in->width,
438                                                      roi_in->height, ch, ch_width);
439           }
440 
441           if(mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK)
442           {
443             if(d->do_nan_checks && (!isfinite(bufptr[2]) || !isfinite(bufptr[3])))
444             {
445               out[3] = 0.0f;
446               continue;
447             }
448 
449             // take green channel distortion also for alpha channel
450             const float *const inptr = (const float *const)ivoid + (size_t)3;
451             const float pi0 = fmaxf(fminf(bufptr[2] - roi_in->x, roi_in->width - 1.0f), 0.0f);
452             const float pi1 = fmaxf(fminf(bufptr[3] - roi_in->y, roi_in->height - 1.0f), 0.0f);
453             out[3] = dt_interpolation_compute_sample(interpolation, inptr, pi0, pi1, roi_in->width,
454                                                      roi_in->height, ch, ch_width);
455           }
456         }
457       }
458       dt_free_align(buf);
459     }
460     else
461     {
462       dt_iop_image_copy_by_size((float*)ovoid, (float*)ivoid, roi_out->width, roi_out->height, ch);
463     }
464 
465     if(modflags & LF_MODIFY_VIGNETTING)
466     {
467 #ifdef _OPENMP
468 #pragma omp parallel for default(none) \
469       dt_omp_firstprivate(ch, pixelformat, roi_out, ovoid) \
470       shared(modifier) \
471       schedule(static)
472 #endif
473       for(int y = 0; y < roi_out->height; y++)
474       {
475         /* Colour correction: vignetting */
476         // actually this way row stride does not matter.
477         float *out = ((float *)ovoid) + (size_t)y * roi_out->width * ch;
478         modifier->ApplyColorModification(out, roi_out->x, roi_out->y + y, roi_out->width, 1,
479                                          pixelformat, ch * roi_out->width);
480       }
481     }
482   }
483   else // correct distortions:
484   {
485     // acquire temp memory for image buffer
486     const size_t bufsize = (size_t)roi_in->width * roi_in->height * ch * sizeof(float);
487     void *buf = dt_alloc_align(64, bufsize);
488     memcpy(buf, ivoid, bufsize);
489 
490     if(modflags & LF_MODIFY_VIGNETTING)
491     {
492 #ifdef _OPENMP
493 #pragma omp parallel for default(none) \
494       dt_omp_firstprivate(ch, pixelformat, roi_in) \
495       shared(buf, modifier) \
496       schedule(static)
497 #endif
498       for(int y = 0; y < roi_in->height; y++)
499       {
500         /* Colour correction: vignetting */
501         // actually this way row stride does not matter.
502         float *bufptr = ((float *)buf) + (size_t)ch * roi_in->width * y;
503         modifier->ApplyColorModification(bufptr, roi_in->x, roi_in->y + y, roi_in->width, 1,
504                                          pixelformat, ch * roi_in->width);
505       }
506     }
507 
508     if(modflags & (LF_MODIFY_TCA | LF_MODIFY_DISTORTION | LF_MODIFY_GEOMETRY | LF_MODIFY_SCALE))
509     {
510       // acquire temp memory for distorted pixel coords
511       const size_t buf2size = (size_t)roi_out->width * 2 * 3;
512       size_t padded_buf2size;
513       float *const buf2 = dt_alloc_perthread_float(buf2size, &padded_buf2size);
514 
515 #ifdef _OPENMP
516 #pragma omp parallel for default(none) \
517       dt_omp_firstprivate(padded_buf2size, ch, ch_width, d, interpolation, mask_display, ovoid, roi_in, roi_out) \
518       dt_omp_sharedconst(buf2)						\
519       shared(buf, modifier)						\
520       schedule(static)
521 #endif
522       for(int y = 0; y < roi_out->height; y++)
523       {
524         float *buf2ptr = (float*)dt_get_perthread(buf2, padded_buf2size);
525         modifier->ApplySubpixelGeometryDistortion(roi_out->x, roi_out->y + y, roi_out->width,
526                                                   1, buf2ptr);
527         // reverse transform the global coords from lf to our buffer
528         float *out = ((float *)ovoid) + (size_t)y * roi_out->width * ch;
529         for(int x = 0; x < roi_out->width; x++, buf2ptr += 6, out += ch)
530         {
531           for(int c = 0; c < 3; c++)
532           {
533             if(d->do_nan_checks && (!isfinite(buf2ptr[c * 2]) || !isfinite(buf2ptr[c * 2 + 1])))
534             {
535               out[c] = 0.0f;
536               continue;
537             }
538 
539             float *bufptr = ((float *)buf) + c;
540             const float pi0 = fmaxf(fminf(buf2ptr[c * 2] - roi_in->x, roi_in->width - 1.0f), 0.0f);
541             const float pi1 = fmaxf(fminf(buf2ptr[c * 2 + 1] - roi_in->y, roi_in->height - 1.0f), 0.0f);
542             out[c] = dt_interpolation_compute_sample(interpolation, bufptr, pi0, pi1, roi_in->width,
543                                                      roi_in->height, ch, ch_width);
544           }
545 
546           if(mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK)
547           {
548             if(d->do_nan_checks && (!isfinite(buf2ptr[2]) || !isfinite(buf2ptr[3])))
549             {
550               out[3] = 0.0f;
551               continue;
552             }
553 
554             // take green channel distortion also for alpha channel
555             float *bufptr = ((float *)buf) + 3;
556             const float pi0 = fmaxf(fminf(buf2ptr[2] - roi_in->x, roi_in->width - 1.0f), 0.0f);
557             const float pi1 = fmaxf(fminf(buf2ptr[3] - roi_in->y, roi_in->height - 1.0f), 0.0f);
558             out[3] = dt_interpolation_compute_sample(interpolation, bufptr, pi0, pi1, roi_in->width,
559                                                      roi_in->height, ch, ch_width);
560           }
561         }
562       }
563       dt_free_align(buf2);
564     }
565     else
566     {
567       memcpy(ovoid, buf, bufsize);
568     }
569     dt_free_align(buf);
570   }
571   delete modifier;
572 
573   if(self->dev->gui_attached && g && (piece->pipe->type & DT_DEV_PIXELPIPE_PREVIEW) == DT_DEV_PIXELPIPE_PREVIEW)
574   {
575     dt_iop_gui_enter_critical_section(self);
576     g->corrections_done = (modflags & LENSFUN_MODFLAG_MASK);
577     dt_iop_gui_leave_critical_section(self);
578   }
579 }
580 
581 #ifdef HAVE_OPENCL
process_cl(struct dt_iop_module_t * self,dt_dev_pixelpipe_iop_t * piece,cl_mem dev_in,cl_mem dev_out,const dt_iop_roi_t * const roi_in,const dt_iop_roi_t * const roi_out)582 int process_cl(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out,
583                const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
584 {
585   dt_iop_lensfun_data_t *d = (dt_iop_lensfun_data_t *)piece->data;
586   dt_iop_lensfun_global_data_t *gd = (dt_iop_lensfun_global_data_t *)self->global_data;
587   dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
588 
589   cl_mem dev_tmpbuf = NULL;
590   cl_mem dev_tmp = NULL;
591   cl_int err = -999;
592 
593   float *tmpbuf = NULL;
594   lfModifier *modifier = NULL;
595 
596   const int devid = piece->pipe->devid;
597   const int iwidth = roi_in->width;
598   const int iheight = roi_in->height;
599   const int owidth = roi_out->width;
600   const int oheight = roi_out->height;
601   const int roi_in_x = roi_in->x;
602   const int roi_in_y = roi_in->y;
603   const int width = MAX(iwidth, owidth);
604   const int height = MAX(iheight, oheight);
605   const int ch = piece->colors;
606   const int tmpbufwidth = owidth * 2 * 3;
607   const size_t tmpbuflen = d->inverse ? (size_t)oheight * owidth * 2 * 3 * sizeof(float)
608                                       : MAX((size_t)oheight * owidth * 2 * 3, (size_t)iheight * iwidth * ch)
609                                         * sizeof(float);
610   const unsigned int pixelformat = ch == 3 ? LF_CR_3(RED, GREEN, BLUE) : LF_CR_4(RED, GREEN, BLUE, UNKNOWN);
611 
612   const float orig_w = roi_in->scale * piece->buf_in.width, orig_h = roi_in->scale * piece->buf_in.height;
613 
614   size_t origin[] = { 0, 0, 0 };
615   size_t iregion[] = { (size_t)iwidth, (size_t)iheight, 1 };
616   size_t oregion[] = { (size_t)owidth, (size_t)oheight, 1 };
617   size_t isizes[] = { (size_t)ROUNDUPWD(iwidth), (size_t)ROUNDUPHT(iheight), 1 };
618   size_t osizes[] = { (size_t)ROUNDUPWD(owidth), (size_t)ROUNDUPHT(oheight), 1 };
619 
620   int modflags;
621   int ldkernel = -1;
622   const struct dt_interpolation *interpolation = dt_interpolation_new(DT_INTERPOLATION_USERPREF_WARP);
623 
624   if(!d->lens || !d->lens->Maker || d->crop <= 0.0f)
625   {
626     err = dt_opencl_enqueue_copy_image(devid, dev_in, dev_out, origin, origin, oregion);
627     if(err != CL_SUCCESS) goto error;
628     return TRUE;
629   }
630 
631   switch(interpolation->id)
632   {
633     case DT_INTERPOLATION_BILINEAR:
634       ldkernel = gd->kernel_lens_distort_bilinear;
635       break;
636     case DT_INTERPOLATION_BICUBIC:
637       ldkernel = gd->kernel_lens_distort_bicubic;
638       break;
639     case DT_INTERPOLATION_LANCZOS2:
640       ldkernel = gd->kernel_lens_distort_lanczos2;
641       break;
642     case DT_INTERPOLATION_LANCZOS3:
643       ldkernel = gd->kernel_lens_distort_lanczos3;
644       break;
645     default:
646       return FALSE;
647   }
648 
649   tmpbuf = (float *)dt_alloc_align(64, tmpbuflen);
650   if(tmpbuf == NULL) goto error;
651 
652   dev_tmp = (cl_mem)dt_opencl_alloc_device(devid, width, height, sizeof(float) * 4);
653   if(dev_tmp == NULL) goto error;
654 
655   dev_tmpbuf = (cl_mem)dt_opencl_alloc_device_buffer(devid, tmpbuflen);
656   if(dev_tmpbuf == NULL) goto error;
657 
658   dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
659   modifier = get_modifier(&modflags, orig_w, orig_h, d, LF_MODIFY_ALL, FALSE);
660   dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
661 
662   if(d->inverse)
663   {
664     // reverse direction (useful for renderings)
665     if(modflags & (LF_MODIFY_TCA | LF_MODIFY_DISTORTION | LF_MODIFY_GEOMETRY | LF_MODIFY_SCALE))
666     {
667 #ifdef _OPENMP
668 #pragma omp parallel for default(none) \
669       dt_omp_firstprivate(tmpbufwidth, roi_out) \
670       shared(tmpbuf, d, modifier) \
671       schedule(static)
672 #endif
673       for(int y = 0; y < roi_out->height; y++)
674       {
675         float *pi = tmpbuf + (size_t)y * tmpbufwidth;
676         modifier->ApplySubpixelGeometryDistortion(roi_out->x, roi_out->y + y, roi_out->width, 1, pi);
677       }
678 
679       /* _blocking_ memory transfer: host tmpbuf buffer -> opencl dev_tmpbuf */
680       err = dt_opencl_write_buffer_to_device(devid, tmpbuf, dev_tmpbuf, 0,
681                                              (size_t)owidth * oheight * 2 * 3 * sizeof(float), CL_TRUE);
682       if(err != CL_SUCCESS) goto error;
683 
684       dt_opencl_set_kernel_arg(devid, ldkernel, 0, sizeof(cl_mem), (void *)&dev_in);
685       dt_opencl_set_kernel_arg(devid, ldkernel, 1, sizeof(cl_mem), (void *)&dev_tmp);
686       dt_opencl_set_kernel_arg(devid, ldkernel, 2, sizeof(int), (void *)&owidth);
687       dt_opencl_set_kernel_arg(devid, ldkernel, 3, sizeof(int), (void *)&oheight);
688       dt_opencl_set_kernel_arg(devid, ldkernel, 4, sizeof(int), (void *)&iwidth);
689       dt_opencl_set_kernel_arg(devid, ldkernel, 5, sizeof(int), (void *)&iheight);
690       dt_opencl_set_kernel_arg(devid, ldkernel, 6, sizeof(int), (void *)&roi_in_x);
691       dt_opencl_set_kernel_arg(devid, ldkernel, 7, sizeof(int), (void *)&roi_in_y);
692       dt_opencl_set_kernel_arg(devid, ldkernel, 8, sizeof(cl_mem), (void *)&dev_tmpbuf);
693       dt_opencl_set_kernel_arg(devid, ldkernel, 9, sizeof(int), (void *)&(d->do_nan_checks));
694       err = dt_opencl_enqueue_kernel_2d(devid, ldkernel, osizes);
695       if(err != CL_SUCCESS) goto error;
696     }
697     else
698     {
699       err = dt_opencl_enqueue_copy_image(devid, dev_in, dev_tmp, origin, origin, oregion);
700       if(err != CL_SUCCESS) goto error;
701     }
702 
703     if(modflags & LF_MODIFY_VIGNETTING)
704     {
705 #ifdef _OPENMP
706 #pragma omp parallel for default(none) \
707       dt_omp_firstprivate(ch, pixelformat, roi_out) \
708       shared(tmpbuf, modifier, d) \
709       schedule(static)
710 #endif
711       for(int y = 0; y < roi_out->height; y++)
712       {
713         /* Colour correction: vignetting */
714         // actually this way row stride does not matter.
715         float *buf = tmpbuf + (size_t)y * ch * roi_out->width;
716         for(int k = 0; k < ch * roi_out->width; k++) buf[k] = 0.5f;
717         modifier->ApplyColorModification(buf, roi_out->x, roi_out->y + y, roi_out->width, 1,
718                                          pixelformat, ch * roi_out->width);
719       }
720 
721       /* _blocking_ memory transfer: host tmpbuf buffer -> opencl dev_tmpbuf */
722       err = dt_opencl_write_buffer_to_device(devid, tmpbuf, dev_tmpbuf, 0,
723                                              (size_t)ch * roi_out->width * roi_out->height * sizeof(float),
724                                              CL_TRUE);
725       if(err != CL_SUCCESS) goto error;
726 
727       dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 0, sizeof(cl_mem), (void *)&dev_tmp);
728       dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 1, sizeof(cl_mem), (void *)&dev_out);
729       dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 2, sizeof(int), (void *)&owidth);
730       dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 3, sizeof(int), (void *)&oheight);
731       dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 4, sizeof(cl_mem), (void *)&dev_tmpbuf);
732       err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_lens_vignette, osizes);
733       if(err != CL_SUCCESS) goto error;
734     }
735     else
736     {
737       err = dt_opencl_enqueue_copy_image(devid, dev_tmp, dev_out, origin, origin, oregion);
738       if(err != CL_SUCCESS) goto error;
739     }
740   }
741 
742   else // correct distortions:
743   {
744 
745     if(modflags & LF_MODIFY_VIGNETTING)
746     {
747 #ifdef _OPENMP
748 #pragma omp parallel for default(none) \
749       dt_omp_firstprivate(ch, pixelformat, roi_in) \
750       shared(tmpbuf, modifier, d) \
751       schedule(static)
752 #endif
753       for(int y = 0; y < roi_in->height; y++)
754       {
755         /* Colour correction: vignetting */
756         // actually this way row stride does not matter.
757         float *buf = tmpbuf + (size_t)y * ch * roi_in->width;
758         for(int k = 0; k < ch * roi_in->width; k++) buf[k] = 0.5f;
759         modifier->ApplyColorModification(buf, roi_in->x, roi_in->y + y, roi_in->width, 1,
760                                          pixelformat, ch * roi_in->width);
761       }
762 
763       /* _blocking_ memory transfer: host tmpbuf buffer -> opencl dev_tmpbuf */
764       err = dt_opencl_write_buffer_to_device(
765           devid, tmpbuf, dev_tmpbuf, 0, (size_t)ch * roi_in->width * roi_in->height * sizeof(float), CL_TRUE);
766       if(err != CL_SUCCESS) goto error;
767 
768       dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 0, sizeof(cl_mem), (void *)&dev_in);
769       dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 1, sizeof(cl_mem), (void *)&dev_tmp);
770       dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 2, sizeof(int), (void *)&iwidth);
771       dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 3, sizeof(int), (void *)&iheight);
772       dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 4, sizeof(cl_mem), (void *)&dev_tmpbuf);
773       err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_lens_vignette, isizes);
774       if(err != CL_SUCCESS) goto error;
775     }
776     else
777     {
778       err = dt_opencl_enqueue_copy_image(devid, dev_in, dev_tmp, origin, origin, iregion);
779       if(err != CL_SUCCESS) goto error;
780     }
781 
782     if(modflags & (LF_MODIFY_TCA | LF_MODIFY_DISTORTION | LF_MODIFY_GEOMETRY | LF_MODIFY_SCALE))
783     {
784 #ifdef _OPENMP
785 #pragma omp parallel for default(none) \
786       dt_omp_firstprivate(tmpbufwidth, roi_out) \
787       shared(tmpbuf, d, modifier) \
788       schedule(static)
789 #endif
790       for(int y = 0; y < roi_out->height; y++)
791       {
792         float *pi = tmpbuf + (size_t)y * tmpbufwidth;
793         modifier->ApplySubpixelGeometryDistortion(roi_out->x, roi_out->y + y, roi_out->width, 1, pi);
794       }
795 
796       /* _blocking_ memory transfer: host tmpbuf buffer -> opencl dev_tmpbuf */
797       err = dt_opencl_write_buffer_to_device(devid, tmpbuf, dev_tmpbuf, 0,
798                                              (size_t)owidth * oheight * 2 * 3 * sizeof(float), CL_TRUE);
799       if(err != CL_SUCCESS) goto error;
800 
801       dt_opencl_set_kernel_arg(devid, ldkernel, 0, sizeof(cl_mem), (void *)&dev_tmp);
802       dt_opencl_set_kernel_arg(devid, ldkernel, 1, sizeof(cl_mem), (void *)&dev_out);
803       dt_opencl_set_kernel_arg(devid, ldkernel, 2, sizeof(int), (void *)&owidth);
804       dt_opencl_set_kernel_arg(devid, ldkernel, 3, sizeof(int), (void *)&oheight);
805       dt_opencl_set_kernel_arg(devid, ldkernel, 4, sizeof(int), (void *)&iwidth);
806       dt_opencl_set_kernel_arg(devid, ldkernel, 5, sizeof(int), (void *)&iheight);
807       dt_opencl_set_kernel_arg(devid, ldkernel, 6, sizeof(int), (void *)&roi_in_x);
808       dt_opencl_set_kernel_arg(devid, ldkernel, 7, sizeof(int), (void *)&roi_in_y);
809       dt_opencl_set_kernel_arg(devid, ldkernel, 8, sizeof(cl_mem), (void *)&dev_tmpbuf);
810       dt_opencl_set_kernel_arg(devid, ldkernel, 9, sizeof(int), (void *)&(d->do_nan_checks));
811       err = dt_opencl_enqueue_kernel_2d(devid, ldkernel, osizes);
812       if(err != CL_SUCCESS) goto error;
813     }
814     else
815     {
816       err = dt_opencl_enqueue_copy_image(devid, dev_tmp, dev_out, origin, origin, oregion);
817       if(err != CL_SUCCESS) goto error;
818     }
819   }
820 
821   if(self->dev->gui_attached && g && (piece->pipe->type & DT_DEV_PIXELPIPE_PREVIEW) == DT_DEV_PIXELPIPE_PREVIEW)
822   {
823     dt_iop_gui_enter_critical_section(self);
824     g->corrections_done = (modflags & LENSFUN_MODFLAG_MASK);
825     dt_iop_gui_leave_critical_section(self);
826   }
827 
828   dt_opencl_release_mem_object(dev_tmpbuf);
829   dt_opencl_release_mem_object(dev_tmp);
830   if(tmpbuf != NULL) dt_free_align(tmpbuf);
831   if(modifier != NULL) delete modifier;
832   return TRUE;
833 
834 error:
835   dt_opencl_release_mem_object(dev_tmp);
836   dt_opencl_release_mem_object(dev_tmpbuf);
837   if(tmpbuf != NULL) dt_free_align(tmpbuf);
838   if(modifier != NULL) delete modifier;
839   dt_print(DT_DEBUG_OPENCL, "[opencl_lens] couldn't enqueue kernel! %d\n", err);
840   return FALSE;
841 }
842 #endif
843 
tiling_callback(struct dt_iop_module_t * self,struct dt_dev_pixelpipe_iop_t * piece,const dt_iop_roi_t * roi_in,const dt_iop_roi_t * roi_out,struct dt_develop_tiling_t * tiling)844 void tiling_callback(struct dt_iop_module_t *self, struct dt_dev_pixelpipe_iop_t *piece,
845                      const dt_iop_roi_t *roi_in, const dt_iop_roi_t *roi_out,
846                      struct dt_develop_tiling_t *tiling)
847 {
848   tiling->factor = 4.5f; // in + out + tmp + tmpbuf
849   tiling->maxbuf = 1.5f;
850   tiling->overhead = 0;
851   tiling->overlap = 4;
852   tiling->xalign = 1;
853   tiling->yalign = 1;
854   return;
855 }
856 
distort_transform(dt_iop_module_t * self,dt_dev_pixelpipe_iop_t * piece,float * const __restrict points,size_t points_count)857 int distort_transform(dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, float *const __restrict points, size_t points_count)
858 {
859   dt_iop_lensfun_data_t *d = (dt_iop_lensfun_data_t *)piece->data;
860   if(!d->lens || !d->lens->Maker || d->crop <= 0.0f) return 0;
861 
862   const float orig_w = piece->buf_in.width, orig_h = piece->buf_in.height;
863   int modflags;
864 
865   const lfModifier *modifier = get_modifier(&modflags, orig_w, orig_h, d, LF_MODIFY_ALL, TRUE);
866   if(modflags & (LF_MODIFY_TCA | LF_MODIFY_DISTORTION | LF_MODIFY_GEOMETRY | LF_MODIFY_SCALE))
867   {
868 
869 #ifdef _OPENMP
870 #pragma omp parallel for default(none) \
871     dt_omp_firstprivate(points_count, points, modifier) \
872     schedule(static) if(points_count > 100)
873 #endif
874     for(size_t i = 0; i < points_count * 2; i += 2)
875     {
876       float DT_ALIGNED_ARRAY buf[6];
877       modifier->ApplySubpixelGeometryDistortion(points[i], points[i + 1], 1, 1, buf);
878       points[i] = buf[0];
879       points[i + 1] = buf[3];
880     }
881   }
882 
883   delete modifier;
884   return 1;
885 }
886 
distort_backtransform(dt_iop_module_t * self,dt_dev_pixelpipe_iop_t * piece,float * const __restrict points,size_t points_count)887 int distort_backtransform(dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, float *const __restrict points,
888                           size_t points_count)
889 {
890   dt_iop_lensfun_data_t *d = (dt_iop_lensfun_data_t *)piece->data;
891 
892   if(!d->lens || !d->lens->Maker || d->crop <= 0.0f) return 0;
893 
894   const float orig_w = piece->buf_in.width, orig_h = piece->buf_in.height;
895   int modflags;
896   const lfModifier *modifier = get_modifier(&modflags, orig_w, orig_h, d, LF_MODIFY_ALL, FALSE);
897 
898   if(modflags & (LF_MODIFY_TCA | LF_MODIFY_DISTORTION | LF_MODIFY_GEOMETRY | LF_MODIFY_SCALE))
899   {
900 
901 #ifdef _OPENMP
902 #pragma omp parallel for default(none) \
903     dt_omp_firstprivate(points_count, points, modifier) \
904     schedule(static) if(points_count > 100)
905 #endif
906     for(size_t i = 0; i < points_count * 2; i += 2)
907     {
908       float DT_ALIGNED_ARRAY buf[6];
909       modifier->ApplySubpixelGeometryDistortion(points[i], points[i + 1], 1, 1, buf);
910       points[i] = buf[0];
911       points[i + 1] = buf[3];
912     }
913   }
914 
915   delete modifier;
916   return 1;
917 }
918 
919 // TODO: Shall we keep LF_MODIFY_TCA in the modifiers?
distort_mask(struct dt_iop_module_t * self,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)920 void distort_mask(struct dt_iop_module_t *self, struct dt_dev_pixelpipe_iop_t *piece, const float *const in,
921                   float *const out, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
922 {
923   const dt_iop_lensfun_data_t *const d = (dt_iop_lensfun_data_t *)piece->data;
924 
925   if(!d->lens || !d->lens->Maker || d->crop <= 0.0f)
926   {
927     dt_iop_image_copy_by_size(out, in, roi_out->width, roi_out->height, 1);
928     return;
929   }
930 
931   const float orig_w = roi_in->scale * piece->buf_in.width, orig_h = roi_in->scale * piece->buf_in.height;
932   dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
933   int modflags;
934   const lfModifier *modifier = get_modifier(&modflags, orig_w, orig_h, d, /*LF_MODIFY_TCA |*/ LF_MODIFY_DISTORTION | LF_MODIFY_GEOMETRY | LF_MODIFY_SCALE, FALSE);
935 
936   dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
937 
938   if(!(modflags & (LF_MODIFY_TCA | LF_MODIFY_DISTORTION | LF_MODIFY_GEOMETRY | LF_MODIFY_SCALE)))
939   {
940     dt_iop_image_copy_by_size(out, in, roi_out->width, roi_out->height, 1);
941     delete modifier;
942     return;
943   }
944 
945   const struct dt_interpolation *const interpolation = dt_interpolation_new(DT_INTERPOLATION_USERPREF_WARP);
946 
947   // acquire temp memory for distorted pixel coords
948   const size_t bufsize = (size_t)roi_out->width * 2 * 3;
949   size_t padded_bufsize;
950   float *const buf = dt_alloc_perthread_float(bufsize, &padded_bufsize);
951 
952 #ifdef _OPENMP
953 #pragma omp parallel for default(none) \
954   dt_omp_firstprivate(padded_bufsize, d, in, interpolation, out, roi_in, roi_out) \
955   dt_omp_sharedconst(buf) \
956   shared(modifier) \
957   schedule(static)
958 #endif
959   for(int y = 0; y < roi_out->height; y++)
960   {
961     float *bufptr = (float*)dt_get_perthread(buf, padded_bufsize);
962     modifier->ApplySubpixelGeometryDistortion(roi_out->x, roi_out->y + y, roi_out->width, 1, bufptr);
963 
964     // reverse transform the global coords from lf to our buffer
965     float *_out = out + (size_t)y * roi_out->width;
966     for(int x = 0; x < roi_out->width; x++, bufptr += 6, _out++)
967     {
968       if(d->do_nan_checks && (!isfinite(bufptr[2]) || !isfinite(bufptr[3])))
969       {
970         *_out = 0.0f;
971         continue;
972       }
973 
974       // take green channel distortion also for alpha channel
975       const float pi0 = bufptr[2] - roi_in->x;
976       const float pi1 = bufptr[3] - roi_in->y;
977       *_out = dt_interpolation_compute_sample(interpolation, in, pi0, pi1, roi_in->width, roi_in->height, 1,
978                                               roi_in->width);
979     }
980   }
981   dt_free_align(buf);
982   delete modifier;
983 }
984 
modify_roi_out(struct dt_iop_module_t * self,struct dt_dev_pixelpipe_iop_t * piece,dt_iop_roi_t * roi_out,const dt_iop_roi_t * roi_in)985 void modify_roi_out(struct dt_iop_module_t *self, struct dt_dev_pixelpipe_iop_t *piece, dt_iop_roi_t *roi_out,
986                     const dt_iop_roi_t *roi_in)
987 {
988   *roi_out = *roi_in;
989 }
990 
modify_roi_in(struct dt_iop_module_t * self,struct dt_dev_pixelpipe_iop_t * piece,const dt_iop_roi_t * const roi_out,dt_iop_roi_t * roi_in)991 void modify_roi_in(struct dt_iop_module_t *self, struct dt_dev_pixelpipe_iop_t *piece,
992                    const dt_iop_roi_t *const roi_out, dt_iop_roi_t *roi_in)
993 {
994   dt_iop_lensfun_data_t *d = (dt_iop_lensfun_data_t *)piece->data;
995   *roi_in = *roi_out;
996   // inverse transform with given params
997 
998   if(!d->lens || !d->lens->Maker || d->crop <= 0.0f) return;
999 
1000   const float orig_w = roi_in->scale * piece->buf_in.width, orig_h = roi_in->scale * piece->buf_in.height;
1001   int modflags;
1002   const lfModifier *modifier = get_modifier(&modflags, orig_w, orig_h, d, LF_MODIFY_ALL, FALSE);
1003 
1004   if(modflags & (LF_MODIFY_TCA | LF_MODIFY_DISTORTION | LF_MODIFY_GEOMETRY | LF_MODIFY_SCALE))
1005   {
1006     const int xoff = roi_in->x;
1007     const int yoff = roi_in->y;
1008     const int width = roi_in->width;
1009     const int height = roi_in->height;
1010     const int awidth = abs(width);
1011     const int aheight = abs(height);
1012     const int xstep = (width < 0) ? -1 : 1;
1013     const int ystep = (height < 0) ? -1 : 1;
1014 
1015     float xm = FLT_MAX, xM = -FLT_MAX, ym = FLT_MAX, yM = -FLT_MAX;
1016     const size_t nbpoints = 2 * awidth + 2 * aheight;
1017 
1018     float *const buf = (float *)dt_alloc_align(64, sizeof(float) * nbpoints * 2 * 3);
1019 
1020 #ifdef _OPENMP
1021 #pragma omp parallel default(none) \
1022     dt_omp_firstprivate(aheight, awidth, buf, height, nbpoints, width, xoff, \
1023                         xstep, yoff, ystep) \
1024     shared(modifier) reduction(min : xm, ym) reduction(max : xM, yM)
1025 #endif
1026     {
1027 #ifdef _OPENMP
1028 #pragma omp for schedule(static)
1029 #endif
1030       for(int i = 0; i < awidth; i++)
1031         modifier->ApplySubpixelGeometryDistortion(xoff + i * xstep, yoff, 1, 1, buf + 6 * i);
1032 
1033 #ifdef _OPENMP
1034 #pragma omp for schedule(static)
1035 #endif
1036       for(int i = 0; i < awidth; i++)
1037         modifier->ApplySubpixelGeometryDistortion(xoff + i * xstep, yoff + (height - 1), 1, 1, buf + 6 * (awidth + i));
1038 
1039 #ifdef _OPENMP
1040 #pragma omp for schedule(static)
1041 #endif
1042       for(int j = 0; j < aheight; j++)
1043         modifier->ApplySubpixelGeometryDistortion(xoff, yoff + j * ystep, 1, 1, buf + 6 * (2 * awidth + j));
1044 
1045 #ifdef _OPENMP
1046 #pragma omp for schedule(static)
1047 #endif
1048       for(int j = 0; j < aheight; j++)
1049         modifier->ApplySubpixelGeometryDistortion(xoff + (width - 1), yoff + j * ystep, 1, 1, buf + 6 * (2 * awidth + aheight + j));
1050 
1051 #ifdef _OPENMP
1052 #pragma omp barrier
1053 #endif
1054 
1055 #ifdef _OPENMP
1056 #pragma omp for schedule(static)
1057 #endif
1058       for(size_t k = 0; k < nbpoints; k++)
1059       {
1060         // iterate over RGB channels x and y coordinates
1061         for(size_t c = 0; c < 6; c+=2)
1062         {
1063           const float x = buf[6 * k + c];
1064           const float y = buf[6 * k + c + 1];
1065           xm = isnan(x) ? xm : MIN(xm, x);
1066           xM = isnan(x) ? xM : MAX(xM, x);
1067           ym = isnan(y) ? ym : MIN(ym, y);
1068           yM = isnan(y) ? yM : MAX(yM, y);
1069         }
1070       }
1071     }
1072 
1073     dt_free_align(buf);
1074 
1075     // LensFun can return NAN coords, so we need to handle them carefully.
1076     if(!isfinite(xm) || !(0 <= xm && xm < orig_w)) xm = 0;
1077     if(!isfinite(xM) || !(1 <= xM && xM < orig_w)) xM = orig_w;
1078     if(!isfinite(ym) || !(0 <= ym && ym < orig_h)) ym = 0;
1079     if(!isfinite(yM) || !(1 <= yM && yM < orig_h)) yM = orig_h;
1080 
1081     const struct dt_interpolation *interpolation = dt_interpolation_new(DT_INTERPOLATION_USERPREF_WARP);
1082     roi_in->x = fmaxf(0.0f, xm - interpolation->width);
1083     roi_in->y = fmaxf(0.0f, ym - interpolation->width);
1084     roi_in->width = fminf(orig_w - roi_in->x, xM - roi_in->x + interpolation->width);
1085     roi_in->height = fminf(orig_h - roi_in->y, yM - roi_in->y + interpolation->width);
1086 
1087     // sanity check.
1088     roi_in->x = CLAMP(roi_in->x, 0, (int)floorf(orig_w));
1089     roi_in->y = CLAMP(roi_in->y, 0, (int)floorf(orig_h));
1090     roi_in->width = CLAMP(roi_in->width, 1, (int)ceilf(orig_w) - roi_in->x);
1091     roi_in->height = CLAMP(roi_in->height, 1, (int)ceilf(orig_h) - roi_in->y);
1092   }
1093   delete modifier;
1094 }
1095 
commit_params(struct dt_iop_module_t * self,dt_iop_params_t * p1,dt_dev_pixelpipe_t * pipe,dt_dev_pixelpipe_iop_t * piece)1096 void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe,
1097                    dt_dev_pixelpipe_iop_t *piece)
1098 {
1099   dt_iop_lensfun_params_t *p = (dt_iop_lensfun_params_t *)p1;
1100 
1101   if(p->modified == 0)
1102   {
1103     /*
1104      * user did not modify anything in gui after autodetection - let's
1105      * use current default_params as params - for presets and mass-export
1106      */
1107     p = (dt_iop_lensfun_params_t *)self->default_params;
1108   }
1109 
1110   dt_iop_lensfun_data_t *d = (dt_iop_lensfun_data_t *)piece->data;
1111 
1112   dt_iop_lensfun_global_data_t *gd = (dt_iop_lensfun_global_data_t *)self->global_data;
1113   lfDatabase *dt_iop_lensfun_db = (lfDatabase *)gd->db;
1114   const lfCamera *camera = NULL;
1115   const lfCamera **cam = NULL;
1116 
1117   if(d->lens)
1118   {
1119     delete d->lens;
1120     d->lens = NULL;
1121   }
1122   d->lens = new lfLens;
1123 
1124   if(p->camera[0])
1125   {
1126     dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
1127     cam = dt_iop_lensfun_db->FindCamerasExt(NULL, p->camera, 0);
1128     if(cam)
1129     {
1130       camera = cam[0];
1131       d->crop = cam[0]->CropFactor;
1132     }
1133     dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
1134   }
1135   if(p->lens[0])
1136   {
1137     dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
1138     const lfLens **lens
1139         = dt_iop_lensfun_db->FindLenses(camera, NULL, p->lens, 0);
1140     dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
1141     if(lens)
1142     {
1143       *d->lens = *lens[0];
1144       if(p->tca_override)
1145       {
1146 #ifdef LF_0395
1147         const dt_image_t *img = &(self->dev->image_storage);
1148 
1149         d->custom_tca =
1150           {
1151            .Model     = LF_TCA_MODEL_LINEAR,
1152            .Focal     = p->focal,
1153            .Terms     = { p->tca_r, p->tca_b },
1154            .CalibAttr = {
1155                          .CenterX = 0.0f,
1156                          .CenterY = 0.0f,
1157                          .CropFactor = d->crop,
1158                          .AspectRatio = (float)img->width / (float)img->height
1159                          }
1160           };
1161 #else
1162         // add manual d->lens stuff:
1163         lfLensCalibTCA tca = { LF_TCA_MODEL_NONE };
1164         tca.Focal = 0;
1165         tca.Model = LF_TCA_MODEL_LINEAR;
1166         tca.Terms[0] = p->tca_r;
1167         tca.Terms[1] = p->tca_b;
1168         if(d->lens->CalibTCA)
1169           while(d->lens->CalibTCA[0]) d->lens->RemoveCalibTCA(0);
1170         d->lens->AddCalibTCA(&tca);
1171 #endif
1172       }
1173       lf_free(lens);
1174     }
1175   }
1176   lf_free(cam);
1177   d->modify_flags = p->modify_flags;
1178   d->inverse = p->inverse;
1179   d->scale = p->scale;
1180   d->focal = p->focal;
1181   d->aperture = p->aperture;
1182   d->distance = p->distance;
1183   d->target_geom = p->target_geom;
1184   d->do_nan_checks = TRUE;
1185   d->tca_override = p->tca_override;
1186 
1187   /*
1188    * there are certain situations when LensFun can return NAN coordinated.
1189    * most common case would be when the FOV is increased.
1190    */
1191   if(d->target_geom == LF_RECTILINEAR)
1192   {
1193     d->do_nan_checks = FALSE;
1194   }
1195   else if(d->target_geom == d->lens->Type)
1196   {
1197     d->do_nan_checks = FALSE;
1198   }
1199 }
1200 
init_pipe(struct dt_iop_module_t * self,dt_dev_pixelpipe_t * pipe,dt_dev_pixelpipe_iop_t * piece)1201 void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
1202 {
1203   piece->data = calloc(1, sizeof(dt_iop_lensfun_data_t));
1204 }
1205 
cleanup_pipe(struct dt_iop_module_t * self,dt_dev_pixelpipe_t * pipe,dt_dev_pixelpipe_iop_t * piece)1206 void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
1207 {
1208   dt_iop_lensfun_data_t *d = (dt_iop_lensfun_data_t *)piece->data;
1209 
1210   if(d->lens)
1211   {
1212     delete d->lens;
1213     d->lens = NULL;
1214   }
1215   free(piece->data);
1216   piece->data = NULL;
1217 }
1218 
init_global(dt_iop_module_so_t * module)1219 void init_global(dt_iop_module_so_t *module)
1220 {
1221   const int program = 2; // basic.cl, from programs.conf
1222   dt_iop_lensfun_global_data_t *gd
1223       = (dt_iop_lensfun_global_data_t *)calloc(1, sizeof(dt_iop_lensfun_global_data_t));
1224   module->data = gd;
1225   gd->kernel_lens_distort_bilinear = dt_opencl_create_kernel(program, "lens_distort_bilinear");
1226   gd->kernel_lens_distort_bicubic = dt_opencl_create_kernel(program, "lens_distort_bicubic");
1227   gd->kernel_lens_distort_lanczos2 = dt_opencl_create_kernel(program, "lens_distort_lanczos2");
1228   gd->kernel_lens_distort_lanczos3 = dt_opencl_create_kernel(program, "lens_distort_lanczos3");
1229   gd->kernel_lens_vignette = dt_opencl_create_kernel(program, "lens_vignette");
1230 
1231   lfDatabase *dt_iop_lensfun_db = new lfDatabase;
1232   gd->db = (lfDatabase *)dt_iop_lensfun_db;
1233 
1234 #if defined(__MACH__) || defined(__APPLE__)
1235 #else
1236   if(dt_iop_lensfun_db->Load() != LF_NO_ERROR)
1237 #endif
1238   {
1239     char datadir[PATH_MAX] = { 0 };
1240     dt_loc_get_datadir(datadir, sizeof(datadir));
1241 
1242     // get parent directory
1243     GFile *file = g_file_parse_name(datadir);
1244     gchar *path = g_file_get_path(g_file_get_parent(file));
1245     g_object_unref(file);
1246 #ifdef LF_MAX_DATABASE_VERSION
1247     gchar *sysdbpath = g_build_filename(path, "lensfun", "version_" STR(LF_MAX_DATABASE_VERSION), (char *)NULL);
1248 #endif
1249 
1250 #ifdef LF_0395
1251     const long userdbts = dt_iop_lensfun_db->ReadTimestamp(dt_iop_lensfun_db->UserUpdatesLocation);
1252     const long sysdbts = dt_iop_lensfun_db->ReadTimestamp(sysdbpath);
1253     const char *dbpath = userdbts > sysdbts ? dt_iop_lensfun_db->UserUpdatesLocation : sysdbpath;
1254     if(dt_iop_lensfun_db->Load(dbpath) != LF_NO_ERROR)
1255       fprintf(stderr, "[iop_lens]: could not load lensfun database in `%s'!\n", dbpath);
1256     else
1257       dt_iop_lensfun_db->Load(dt_iop_lensfun_db->UserLocation);
1258 #else
1259     // code for older lensfun preserved as-is
1260 #ifdef LF_MAX_DATABASE_VERSION
1261     g_free(dt_iop_lensfun_db->HomeDataDir);
1262     dt_iop_lensfun_db->HomeDataDir = g_strdup(sysdbpath);
1263     if(dt_iop_lensfun_db->Load() != LF_NO_ERROR)
1264     {
1265       fprintf(stderr, "[iop_lens]: could not load lensfun database in `%s'!\n", sysdbpath);
1266 #endif
1267       g_free(dt_iop_lensfun_db->HomeDataDir);
1268       dt_iop_lensfun_db->HomeDataDir = g_build_filename(path, "lensfun", (char *)NULL);
1269       if(dt_iop_lensfun_db->Load() != LF_NO_ERROR)
1270         fprintf(stderr, "[iop_lens]: could not load lensfun database in `%s'!\n", dt_iop_lensfun_db->HomeDataDir);
1271 #ifdef LF_MAX_DATABASE_VERSION
1272     }
1273 #endif
1274 #endif
1275 
1276 #ifdef LF_MAX_DATABASE_VERSION
1277     g_free(sysdbpath);
1278 #endif
1279     g_free(path);
1280   }
1281 }
1282 
1283 static float get_autoscale(dt_iop_module_t *self, dt_iop_lensfun_params_t *p, const lfCamera *camera);
1284 
reload_defaults(dt_iop_module_t * module)1285 void reload_defaults(dt_iop_module_t *module)
1286 {
1287   char *new_lens;
1288   const dt_image_t *img = &module->dev->image_storage;
1289 
1290   // reload image specific stuff
1291   // get all we can from exif:
1292   dt_iop_lensfun_params_t *d = (dt_iop_lensfun_params_t *)module->default_params;
1293 
1294   new_lens = _lens_sanitize(img->exif_lens);
1295   g_strlcpy(d->lens, new_lens, sizeof(d->lens));
1296   free(new_lens);
1297   g_strlcpy(d->camera, img->exif_model, sizeof(d->camera));
1298   d->crop = img->exif_crop;
1299   d->aperture = img->exif_aperture;
1300   d->focal = img->exif_focal_length;
1301   d->scale = 1.0;
1302   d->modify_flags = LF_MODIFY_TCA | LF_MODIFY_VIGNETTING | LF_MODIFY_DISTORTION |
1303                     LF_MODIFY_GEOMETRY | LF_MODIFY_SCALE;
1304   // if we did not find focus_distance in EXIF, lets default to 1000
1305   d->distance = img->exif_focus_distance == 0.0f ? 1000.0f : img->exif_focus_distance;
1306   d->target_geom = LF_RECTILINEAR;
1307 
1308   if(dt_image_monochrome_flags(img) & (DT_IMAGE_MONOCHROME | DT_IMAGE_MONOCHROME_BAYER)) d->modify_flags &= ~LF_MODIFY_TCA;
1309 
1310   // init crop from db:
1311   char model[100]; // truncate often complex descriptions.
1312   g_strlcpy(model, img->exif_model, sizeof(model));
1313   for(char cnt = 0, *c = model; c < model + 100 && *c != '\0'; c++)
1314     if(*c == ' ')
1315       if(++cnt == 2) *c = '\0';
1316   if(img->exif_maker[0] || model[0])
1317   {
1318     dt_iop_lensfun_global_data_t *gd = (dt_iop_lensfun_global_data_t *)module->global_data;
1319 
1320     // just to be sure
1321     if(!gd || !gd->db) return;
1322 
1323     dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
1324     const lfCamera **cam = gd->db->FindCamerasExt(img->exif_maker, img->exif_model, 0);
1325     dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
1326     if(cam)
1327     {
1328       dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
1329       const lfLens **lens = gd->db->FindLenses(cam[0], NULL, d->lens, 0);
1330       dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
1331 
1332       if(!lens && islower(cam[0]->Mount[0]))
1333       {
1334         /*
1335          * This is a fixed-lens camera, and LF returned no lens.
1336          * (reasons: lens is "(65535)" or lens is correct lens name,
1337          *  but LF have it as "fixed lens")
1338          *
1339          * Let's unset lens name and re-run lens query
1340          */
1341         g_strlcpy(d->lens, "", sizeof(d->lens));
1342 
1343         dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
1344         lens = gd->db->FindLenses(cam[0], NULL, d->lens, 0);
1345         dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
1346       }
1347 
1348       if(lens)
1349       {
1350         int lens_i = 0;
1351 
1352         /*
1353          * Current SVN lensfun lets you test for a fixed-lens camera by looking
1354          * at the zeroth character in the mount's name:
1355          * If it is a lower case letter, it is a fixed-lens camera.
1356          */
1357         if(!d->lens[0] && islower(cam[0]->Mount[0]))
1358         {
1359           /*
1360            * no lens info in EXIF, and this is fixed-lens camera,
1361            * let's find shortest lens model in the list of possible lenses
1362            */
1363           size_t min_model_len = SIZE_MAX;
1364           for(int i = 0; lens[i]; i++)
1365           {
1366             if(strlen(lens[i]->Model) < min_model_len)
1367             {
1368               min_model_len = strlen(lens[i]->Model);
1369               lens_i = i;
1370             }
1371           }
1372 
1373           // and set lens to it
1374           g_strlcpy(d->lens, lens[lens_i]->Model, sizeof(d->lens));
1375         }
1376 
1377         d->target_geom = lens[lens_i]->Type;
1378         lf_free(lens);
1379       }
1380 
1381       d->crop = cam[0]->CropFactor;
1382       d->scale = get_autoscale(module, d, cam[0]);
1383 
1384       lf_free(cam);
1385     }
1386   }
1387 
1388   // if we have a gui -> reset corrections_done message
1389   dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)module->gui_data;
1390   if(g)
1391   {
1392     dt_iop_gui_enter_critical_section(module);
1393     g->corrections_done = -1;
1394     dt_iop_gui_leave_critical_section(module);
1395     gtk_label_set_text(g->message, "");
1396   }
1397 }
1398 
cleanup_global(dt_iop_module_so_t * module)1399 void cleanup_global(dt_iop_module_so_t *module)
1400 {
1401   dt_iop_lensfun_global_data_t *gd = (dt_iop_lensfun_global_data_t *)module->data;
1402   lfDatabase *dt_iop_lensfun_db = (lfDatabase *)gd->db;
1403   delete dt_iop_lensfun_db;
1404 
1405   dt_opencl_free_kernel(gd->kernel_lens_distort_bilinear);
1406   dt_opencl_free_kernel(gd->kernel_lens_distort_bicubic);
1407   dt_opencl_free_kernel(gd->kernel_lens_distort_lanczos2);
1408   dt_opencl_free_kernel(gd->kernel_lens_distort_lanczos3);
1409   dt_opencl_free_kernel(gd->kernel_lens_vignette);
1410   free(module->data);
1411   module->data = NULL;
1412 }
1413 
1414 /// ############################################################
1415 /// gui stuff: inspired by ufraws lensfun tab:
1416 
1417 /* simple function to compute the floating-point precision
1418    which is enough for "normal use". The criteria is to have
1419    about 3 leading digits after the initial zeros.  */
precision(double x,double adj)1420 static int precision(double x, double adj)
1421 {
1422   x *= adj;
1423 
1424   if(x == 0) return 1;
1425   if(x < 1.0)
1426     if(x < 0.1)
1427       if(x < 0.01)
1428         return 5;
1429       else
1430         return 4;
1431     else
1432       return 3;
1433   else if(x < 100.0)
1434     if(x < 10.0)
1435       return 2;
1436     else
1437       return 1;
1438   else
1439     return 0;
1440 }
1441 
1442 /* -- ufraw ptr array functions -- */
1443 
ptr_array_insert_sorted(GPtrArray * array,const void * item,GCompareFunc compare)1444 static int ptr_array_insert_sorted(GPtrArray *array, const void *item, GCompareFunc compare)
1445 {
1446   int length = array->len;
1447   g_ptr_array_set_size(array, length + 1);
1448   const void **root = (const void **)array->pdata;
1449 
1450   int m = 0, l = 0, r = length - 1;
1451 
1452   // Skip trailing NULL, if any
1453   if(l <= r && !root[r]) r--;
1454 
1455   while(l <= r)
1456   {
1457     m = (l + r) / 2;
1458     int cmp = compare(root[m], item);
1459 
1460     if(cmp == 0)
1461     {
1462       ++m;
1463       goto done;
1464     }
1465     else if(cmp < 0)
1466       l = m + 1;
1467     else
1468       r = m - 1;
1469   }
1470   if(r == m) m++;
1471 
1472 done:
1473   memmove(root + m + 1, root + m, sizeof(void *) * (length - m));
1474   root[m] = item;
1475   return m;
1476 }
1477 
ptr_array_find_sorted(const GPtrArray * array,const void * item,GCompareFunc compare)1478 static int ptr_array_find_sorted(const GPtrArray *array, const void *item, GCompareFunc compare)
1479 {
1480   int length = array->len;
1481   void **root = array->pdata;
1482 
1483   int l = 0, r = length - 1;
1484   int m = 0, cmp = 0;
1485 
1486   if(!length) return -1;
1487 
1488   // Skip trailing NULL, if any
1489   if(!root[r]) r--;
1490 
1491   while(l <= r)
1492   {
1493     m = (l + r) / 2;
1494     cmp = compare(root[m], item);
1495 
1496     if(cmp == 0)
1497       return m;
1498     else if(cmp < 0)
1499       l = m + 1;
1500     else
1501       r = m - 1;
1502   }
1503 
1504   return -1;
1505 }
1506 
ptr_array_insert_index(GPtrArray * array,const void * item,int index)1507 static void ptr_array_insert_index(GPtrArray *array, const void *item, int index)
1508 {
1509   const void **root;
1510   int length = array->len;
1511   g_ptr_array_set_size(array, length + 1);
1512   root = (const void **)array->pdata;
1513   memmove(root + index + 1, root + index, sizeof(void *) * (length - index));
1514   root[index] = item;
1515 }
1516 
1517 /* -- end ufraw ptr array functions -- */
1518 
1519 /* -- camera -- */
1520 
camera_set(dt_iop_module_t * self,const lfCamera * cam)1521 static void camera_set(dt_iop_module_t *self, const lfCamera *cam)
1522 {
1523   dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
1524   dt_iop_lensfun_params_t *p = (dt_iop_lensfun_params_t *)self->params;
1525   gchar *fm;
1526   const char *maker, *model, *variant;
1527   char _variant[100];
1528 
1529   if(!cam)
1530   {
1531     gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(g->camera_model))), "");
1532     gtk_widget_set_tooltip_text(GTK_WIDGET(g->camera_model), "");
1533     return;
1534   }
1535 
1536   g_strlcpy(p->camera, cam->Model, sizeof(p->camera));
1537   p->crop = cam->CropFactor;
1538   g->camera = cam;
1539 
1540   maker = lf_mlstr_get(cam->Maker);
1541   model = lf_mlstr_get(cam->Model);
1542   variant = lf_mlstr_get(cam->Variant);
1543 
1544   if(model)
1545   {
1546     if(maker)
1547       fm = g_strdup_printf("%s, %s", maker, model);
1548     else
1549       fm = g_strdup_printf("%s", model);
1550     gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(g->camera_model))), fm);
1551     g_free(fm);
1552   }
1553 
1554   if(variant)
1555     snprintf(_variant, sizeof(_variant), " (%s)", variant);
1556   else
1557     _variant[0] = 0;
1558 
1559   fm = g_strdup_printf(_("maker:\t\t%s\n"
1560                          "model:\t\t%s%s\n"
1561                          "mount:\t\t%s\n"
1562                          "crop factor:\t%.1f"),
1563                        maker, model, _variant, cam->Mount, cam->CropFactor);
1564   gtk_widget_set_tooltip_text(GTK_WIDGET(g->camera_model), fm);
1565   g_free(fm);
1566 }
1567 
camera_menu_select(GtkMenuItem * menuitem,gpointer user_data)1568 static void camera_menu_select(GtkMenuItem *menuitem, gpointer user_data)
1569 {
1570   dt_iop_module_t *self = (dt_iop_module_t *)user_data;
1571   camera_set(self, (lfCamera *)g_object_get_data(G_OBJECT(menuitem), "lfCamera"));
1572   if(darktable.gui->reset) return;
1573   dt_iop_lensfun_params_t *p = (dt_iop_lensfun_params_t *)self->params;
1574   p->modified = 1;
1575   dt_dev_add_history_item(darktable.develop, self, TRUE);
1576 }
1577 
camera_menu_fill(dt_iop_module_t * self,const lfCamera * const * camlist)1578 static void camera_menu_fill(dt_iop_module_t *self, const lfCamera *const *camlist)
1579 {
1580   dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
1581   unsigned i;
1582   GPtrArray *makers, *submenus;
1583 
1584   if(g->camera_menu)
1585   {
1586     gtk_widget_destroy(GTK_WIDGET(g->camera_menu));
1587     g->camera_menu = NULL;
1588   }
1589 
1590   /* Count all existing camera makers and create a sorted list */
1591   makers = g_ptr_array_new();
1592   submenus = g_ptr_array_new();
1593   for(i = 0; camlist[i]; i++)
1594   {
1595     GtkWidget *submenu, *item;
1596     const char *m = lf_mlstr_get(camlist[i]->Maker);
1597     int idx = ptr_array_find_sorted(makers, m, (GCompareFunc)g_utf8_collate);
1598     if(idx < 0)
1599     {
1600       /* No such maker yet, insert it into the array */
1601       idx = ptr_array_insert_sorted(makers, m, (GCompareFunc)g_utf8_collate);
1602       /* Create a submenu for cameras by this maker */
1603       submenu = gtk_menu_new();
1604       ptr_array_insert_index(submenus, submenu, idx);
1605     }
1606 
1607     submenu = (GtkWidget *)g_ptr_array_index(submenus, idx);
1608     /* Append current camera name to the submenu */
1609     m = lf_mlstr_get(camlist[i]->Model);
1610     if(!camlist[i]->Variant)
1611       item = gtk_menu_item_new_with_label(m);
1612     else
1613     {
1614       gchar *fm = g_strdup_printf("%s (%s)", m, camlist[i]->Variant);
1615       item = gtk_menu_item_new_with_label(fm);
1616       g_free(fm);
1617     }
1618     gtk_widget_show(item);
1619     g_object_set_data(G_OBJECT(item), "lfCamera", (void *)camlist[i]);
1620     g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(camera_menu_select), self);
1621     gtk_menu_shell_append(GTK_MENU_SHELL(submenu), item);
1622   }
1623 
1624   g->camera_menu = GTK_MENU(gtk_menu_new());
1625   for(i = 0; i < makers->len; i++)
1626   {
1627     GtkWidget *item = (GtkWidget *)gtk_menu_item_new_with_label((const gchar *)g_ptr_array_index(makers, i));
1628     gtk_widget_show(item);
1629     gtk_menu_shell_append(GTK_MENU_SHELL(g->camera_menu), item);
1630     gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), (GtkWidget *)g_ptr_array_index(submenus, i));
1631   }
1632 
1633   g_ptr_array_free(submenus, TRUE);
1634   g_ptr_array_free(makers, TRUE);
1635 }
1636 
parse_model(const char * txt,char * model,size_t sz_model)1637 static void parse_model(const char *txt, char *model, size_t sz_model)
1638 {
1639   while(txt[0] && isspace(txt[0])) txt++;
1640   size_t len = strlen(txt);
1641   if(len > sz_model - 1) len = sz_model - 1;
1642   memcpy(model, txt, len);
1643   model[len] = 0;
1644 }
1645 
camera_menusearch_clicked(GtkWidget * button,gpointer user_data)1646 static void camera_menusearch_clicked(GtkWidget *button, gpointer user_data)
1647 {
1648   dt_iop_module_t *self = (dt_iop_module_t *)user_data;
1649   dt_iop_lensfun_global_data_t *gd = (dt_iop_lensfun_global_data_t *)self->global_data;
1650   lfDatabase *dt_iop_lensfun_db = (lfDatabase *)gd->db;
1651   dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
1652 
1653   (void)button;
1654 
1655   const lfCamera *const *camlist;
1656   dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
1657   camlist = dt_iop_lensfun_db->GetCameras();
1658   dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
1659   if(!camlist) return;
1660   camera_menu_fill(self, camlist);
1661 
1662 #if GTK_CHECK_VERSION(3, 22, 0)
1663   gtk_menu_popup_at_pointer(GTK_MENU(g->camera_menu), NULL);
1664 #else
1665   gtk_menu_popup(GTK_MENU(g->camera_menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
1666 #endif
1667 }
1668 
camera_autosearch_clicked(GtkWidget * button,gpointer user_data)1669 static void camera_autosearch_clicked(GtkWidget *button, gpointer user_data)
1670 {
1671   dt_iop_module_t *self = (dt_iop_module_t *)user_data;
1672   dt_iop_lensfun_global_data_t *gd = (dt_iop_lensfun_global_data_t *)self->global_data;
1673   lfDatabase *dt_iop_lensfun_db = (lfDatabase *)gd->db;
1674   dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
1675   char make[200], model[200];
1676   const gchar *txt = (const gchar *)((dt_iop_lensfun_params_t *)self->default_params)->camera;
1677 
1678   (void)button;
1679 
1680   if(txt[0] == '\0')
1681   {
1682     const lfCamera *const *camlist;
1683     dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
1684     camlist = dt_iop_lensfun_db->GetCameras();
1685     dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
1686     if(!camlist) return;
1687     camera_menu_fill(self, camlist);
1688   }
1689   else
1690   {
1691     parse_model(txt, model, sizeof(model));
1692     dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
1693     const lfCamera **camlist = dt_iop_lensfun_db->FindCamerasExt(make, model, 0);
1694     dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
1695     if(!camlist) return;
1696     camera_menu_fill(self, camlist);
1697     lf_free(camlist);
1698   }
1699 
1700 #if GTK_CHECK_VERSION(3, 22, 0)
1701   gtk_menu_popup_at_pointer(GTK_MENU(g->camera_menu), NULL);
1702 #else
1703   gtk_menu_popup(GTK_MENU(g->camera_menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
1704 #endif
1705 }
1706 
1707 /* -- end camera -- */
1708 
lens_comboentry_focal_update(GtkWidget * widget,dt_iop_module_t * self)1709 static void lens_comboentry_focal_update(GtkWidget *widget, dt_iop_module_t *self)
1710 {
1711   dt_iop_lensfun_params_t *p = (dt_iop_lensfun_params_t *)self->params;
1712   const char *text = dt_bauhaus_combobox_get_text(widget);
1713   if(text) (void)sscanf(text, "%f", &p->focal);
1714   p->modified = 1;
1715   dt_dev_add_history_item(darktable.develop, self, TRUE);
1716 }
1717 
lens_comboentry_aperture_update(GtkWidget * widget,dt_iop_module_t * self)1718 static void lens_comboentry_aperture_update(GtkWidget *widget, dt_iop_module_t *self)
1719 {
1720   dt_iop_lensfun_params_t *p = (dt_iop_lensfun_params_t *)self->params;
1721   const char *text = dt_bauhaus_combobox_get_text(widget);
1722   if(text) (void)sscanf(text, "%f", &p->aperture);
1723   p->modified = 1;
1724   dt_dev_add_history_item(darktable.develop, self, TRUE);
1725 }
1726 
lens_comboentry_distance_update(GtkWidget * widget,dt_iop_module_t * self)1727 static void lens_comboentry_distance_update(GtkWidget *widget, dt_iop_module_t *self)
1728 {
1729   dt_iop_lensfun_params_t *p = (dt_iop_lensfun_params_t *)self->params;
1730   const char *text = dt_bauhaus_combobox_get_text(widget);
1731   if(text) (void)sscanf(text, "%f", &p->distance);
1732   p->modified = 1;
1733   dt_dev_add_history_item(darktable.develop, self, TRUE);
1734 }
1735 
delete_children(GtkWidget * widget,gpointer data)1736 static void delete_children(GtkWidget *widget, gpointer data)
1737 {
1738   (void)data;
1739   gtk_widget_destroy(widget);
1740 }
1741 
lens_set(dt_iop_module_t * self,const lfLens * lens)1742 static void lens_set(dt_iop_module_t *self, const lfLens *lens)
1743 {
1744   dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
1745   dt_iop_lensfun_params_t *p = (dt_iop_lensfun_params_t *)self->params;
1746 
1747   gchar *fm;
1748   const char *maker, *model;
1749   unsigned i;
1750   gdouble focal_values[]
1751       = { -INFINITY, 4.5, 8,   10,  12,  14,  15,  16,  17,  18,  20,  24,  28,   30,      31,  35,
1752           38,        40,  43,  45,  50,  55,  60,  70,  75,  77,  80,  85,  90,   100,     105, 110,
1753           120,       135, 150, 200, 210, 240, 250, 300, 400, 500, 600, 800, 1000, INFINITY };
1754   gdouble aperture_values[]
1755       = { -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,
1756           5.6,       6.3, 7.1, 8,   9, 10,  11,  13,  14,  16, 18,  20,  22,  25,  29,  32, 38,  INFINITY };
1757 
1758   if(!lens)
1759   {
1760     gtk_widget_set_sensitive(GTK_WIDGET(g->modflags), FALSE);
1761     gtk_widget_set_sensitive(GTK_WIDGET(g->target_geom), FALSE);
1762     gtk_widget_set_sensitive(GTK_WIDGET(g->scale), FALSE);
1763     gtk_widget_set_sensitive(GTK_WIDGET(g->reverse), FALSE);
1764     gtk_widget_set_sensitive(GTK_WIDGET(g->tca_r), FALSE);
1765     gtk_widget_set_sensitive(GTK_WIDGET(g->tca_b), FALSE);
1766     gtk_widget_set_sensitive(GTK_WIDGET(g->message), FALSE);
1767 
1768     gtk_container_foreach(GTK_CONTAINER(g->detection_warning), delete_children, NULL);
1769 
1770     GtkWidget *label;
1771 
1772     label = gtk_label_new(_("camera/lens not found - please select manually"));
1773     gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_MIDDLE);
1774 
1775     gtk_widget_set_tooltip_text(label, _("try to locate your camera/lens in the above two menus"));
1776 
1777     gtk_box_pack_start(GTK_BOX(g->detection_warning), label, FALSE, FALSE, 0);
1778 
1779     gtk_widget_hide(g->lens_param_box);
1780     gtk_widget_show_all(g->detection_warning);
1781     return;
1782   }
1783   else
1784   {
1785     gtk_widget_set_sensitive(GTK_WIDGET(g->modflags), TRUE);
1786     gtk_widget_set_sensitive(GTK_WIDGET(g->target_geom), TRUE);
1787     gtk_widget_set_sensitive(GTK_WIDGET(g->scale), TRUE);
1788     gtk_widget_set_sensitive(GTK_WIDGET(g->reverse), TRUE);
1789     gtk_widget_set_sensitive(GTK_WIDGET(g->tca_r), TRUE);
1790     gtk_widget_set_sensitive(GTK_WIDGET(g->tca_b), TRUE);
1791     gtk_widget_set_sensitive(GTK_WIDGET(g->message), TRUE);
1792   }
1793 
1794   maker = lf_mlstr_get(lens->Maker);
1795   model = lf_mlstr_get(lens->Model);
1796 
1797   g_strlcpy(p->lens, lens->Model, sizeof(p->lens));
1798 
1799   if(model)
1800   {
1801     if(maker)
1802       fm = g_strdup_printf("%s, %s", maker, model);
1803     else
1804       fm = g_strdup_printf("%s", model);
1805     gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(g->lens_model))), fm);
1806     g_free(fm);
1807   }
1808 
1809   char focal[100], aperture[100], mounts[200];
1810 
1811   if(lens->MinFocal < lens->MaxFocal)
1812     snprintf(focal, sizeof(focal), "%g-%gmm", lens->MinFocal, lens->MaxFocal);
1813   else
1814     snprintf(focal, sizeof(focal), "%gmm", lens->MinFocal);
1815   if(lens->MinAperture < lens->MaxAperture)
1816     snprintf(aperture, sizeof(aperture), "%g-%g", lens->MinAperture, lens->MaxAperture);
1817   else
1818     snprintf(aperture, sizeof(aperture), "%g", lens->MinAperture);
1819 
1820   mounts[0] = 0;
1821 #ifdef LF_0395
1822   const char* const* mount_names = lens->GetMountNames();
1823   i = 0;
1824   while (mount_names && *mount_names) {
1825     if(i > 0) g_strlcat(mounts, ", ", sizeof(mounts));
1826     g_strlcat(mounts, *mount_names, sizeof(mounts));
1827     i++;
1828     mount_names++;
1829   }
1830 #else
1831   if(lens->Mounts)
1832     for(i = 0; lens->Mounts[i]; i++)
1833     {
1834       if(i > 0) g_strlcat(mounts, ", ", sizeof(mounts));
1835       g_strlcat(mounts, lens->Mounts[i], sizeof(mounts));
1836     }
1837 #endif
1838   fm = g_strdup_printf(_("maker:\t\t%s\n"
1839                          "model:\t\t%s\n"
1840                          "focal range:\t%s\n"
1841                          "aperture:\t%s\n"
1842                          "crop factor:\t%.1f\n"
1843                          "type:\t\t%s\n"
1844                          "mounts:\t%s"),
1845                        maker ? maker : "?", model ? model : "?", focal, aperture,
1846 #ifdef LF_0395
1847                        g->camera->CropFactor,
1848 #else
1849                        lens->CropFactor,
1850 #endif
1851                        lfLens::GetLensTypeDesc(lens->Type, NULL), mounts);
1852 
1853   gtk_widget_set_tooltip_text(GTK_WIDGET(g->lens_model), fm);
1854   g_free(fm);
1855 
1856   /* Create the focal/aperture/distance combo boxes */
1857   gtk_container_foreach(GTK_CONTAINER(g->lens_param_box), delete_children, NULL);
1858 
1859   int ffi = 1, fli = -1;
1860   for(i = 1; i < sizeof(focal_values) / sizeof(gdouble) - 1; i++)
1861   {
1862     if(focal_values[i] < lens->MinFocal) ffi = i + 1;
1863     if(focal_values[i] > lens->MaxFocal && fli == -1) fli = i;
1864   }
1865   if(focal_values[ffi] > lens->MinFocal)
1866   {
1867     focal_values[ffi - 1] = lens->MinFocal;
1868     ffi--;
1869   }
1870   if(lens->MaxFocal == 0 || fli < 0) fli = sizeof(focal_values) / sizeof(gdouble) - 2;
1871   if(focal_values[fli + 1] < lens->MaxFocal)
1872   {
1873     focal_values[fli + 1] = lens->MaxFocal;
1874     ffi++;
1875   }
1876   if(fli < ffi) fli = ffi + 1;
1877 
1878   GtkWidget *w;
1879   char txt[30];
1880 
1881   // focal length
1882   w = dt_bauhaus_combobox_new(self);
1883   dt_bauhaus_widget_set_label(w, NULL, N_("mm"));
1884   gtk_widget_set_tooltip_text(w, _("focal length (mm)"));
1885   snprintf(txt, sizeof(txt), "%.*f", precision(p->focal, 10.0), p->focal);
1886   dt_bauhaus_combobox_add(w, txt);
1887   for(int k = 0; k < fli - ffi; k++)
1888   {
1889     snprintf(txt, sizeof(txt), "%.*f", precision(focal_values[ffi + k], 10.0), focal_values[ffi + k]);
1890     dt_bauhaus_combobox_add(w, txt);
1891   }
1892   g_signal_connect(G_OBJECT(w), "value-changed", G_CALLBACK(lens_comboentry_focal_update), self);
1893   gtk_box_pack_start(GTK_BOX(g->lens_param_box), w, TRUE, TRUE, 0);
1894   dt_bauhaus_combobox_set_editable(w, 1);
1895   g->cbe[0] = w;
1896 
1897   // f-stop
1898   ffi = 1, fli = sizeof(aperture_values) / sizeof(gdouble) - 1;
1899   for(i = 1; i < sizeof(aperture_values) / sizeof(gdouble) - 1; i++)
1900     if(aperture_values[i] < lens->MinAperture) ffi = i + 1;
1901   if(aperture_values[ffi] > lens->MinAperture)
1902   {
1903     aperture_values[ffi - 1] = lens->MinAperture;
1904     ffi--;
1905   }
1906 
1907   w = dt_bauhaus_combobox_new(self);
1908   dt_bauhaus_widget_set_label(w, NULL, N_("f/"));
1909   gtk_widget_set_tooltip_text(w, _("f-number (aperture)"));
1910   snprintf(txt, sizeof(txt), "%.*f", precision(p->aperture, 10.0), p->aperture);
1911   dt_bauhaus_combobox_add(w, txt);
1912   for(int k = 0; k < fli - ffi; k++)
1913   {
1914     snprintf(txt, sizeof(txt), "%.*f", precision(aperture_values[ffi + k], 10.0), aperture_values[ffi + k]);
1915     dt_bauhaus_combobox_add(w, txt);
1916   }
1917   g_signal_connect(G_OBJECT(w), "value-changed", G_CALLBACK(lens_comboentry_aperture_update), self);
1918   gtk_box_pack_start(GTK_BOX(g->lens_param_box), w, TRUE, TRUE, 0);
1919   dt_bauhaus_combobox_set_editable(w, 1);
1920   g->cbe[1] = w;
1921 
1922   w = dt_bauhaus_combobox_new(self);
1923   dt_bauhaus_widget_set_label(w, NULL, N_("d"));
1924   gtk_widget_set_tooltip_text(w, _("distance to subject"));
1925   snprintf(txt, sizeof(txt), "%.*f", precision(p->distance, 10.0), p->distance);
1926   dt_bauhaus_combobox_add(w, txt);
1927   float val = 0.25f;
1928   for(int k = 0; k < 25; k++)
1929   {
1930     if(val > 1000.0f) val = 1000.0f;
1931     snprintf(txt, sizeof(txt), "%.*f", precision(val, 10.0), val);
1932     dt_bauhaus_combobox_add(w, txt);
1933     if(val >= 1000.0f) break;
1934     val *= sqrtf(2.0f);
1935   }
1936   g_signal_connect(G_OBJECT(w), "value-changed", G_CALLBACK(lens_comboentry_distance_update), self);
1937   gtk_box_pack_start(GTK_BOX(g->lens_param_box), w, TRUE, TRUE, 0);
1938   dt_bauhaus_combobox_set_editable(w, 1);
1939   g->cbe[2] = w;
1940 
1941   gtk_widget_hide(g->detection_warning);
1942   gtk_widget_show_all(g->lens_param_box);
1943 }
1944 
lens_menu_select(GtkMenuItem * menuitem,gpointer user_data)1945 static void lens_menu_select(GtkMenuItem *menuitem, gpointer user_data)
1946 {
1947   dt_iop_module_t *self = (dt_iop_module_t *)user_data;
1948   dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
1949   dt_iop_lensfun_params_t *p = (dt_iop_lensfun_params_t *)self->params;
1950   lens_set(self, (lfLens *)g_object_get_data(G_OBJECT(menuitem), "lfLens"));
1951   if(darktable.gui->reset) return;
1952   p->modified = 1;
1953   const float scale = get_autoscale(self, p, g->camera);
1954   dt_bauhaus_slider_set(g->scale, scale);
1955   dt_dev_add_history_item(darktable.develop, self, TRUE);
1956 }
1957 
lens_menu_fill(dt_iop_module_t * self,const lfLens * const * lenslist)1958 static void lens_menu_fill(dt_iop_module_t *self, const lfLens *const *lenslist)
1959 {
1960   dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
1961   unsigned i;
1962   GPtrArray *makers, *submenus;
1963 
1964   if(g->lens_menu)
1965   {
1966     gtk_widget_destroy(GTK_WIDGET(g->lens_menu));
1967     g->lens_menu = NULL;
1968   }
1969 
1970   /* Count all existing lens makers and create a sorted list */
1971   makers = g_ptr_array_new();
1972   submenus = g_ptr_array_new();
1973   for(i = 0; lenslist[i]; i++)
1974   {
1975     GtkWidget *submenu, *item;
1976     const char *m = lf_mlstr_get(lenslist[i]->Maker);
1977     int idx = ptr_array_find_sorted(makers, m, (GCompareFunc)g_utf8_collate);
1978     if(idx < 0)
1979     {
1980       /* No such maker yet, insert it into the array */
1981       idx = ptr_array_insert_sorted(makers, m, (GCompareFunc)g_utf8_collate);
1982       /* Create a submenu for lenses by this maker */
1983       submenu = gtk_menu_new();
1984       ptr_array_insert_index(submenus, submenu, idx);
1985     }
1986 
1987     submenu = (GtkWidget *)g_ptr_array_index(submenus, idx);
1988     /* Append current lens name to the submenu */
1989     item = gtk_menu_item_new_with_label(lf_mlstr_get(lenslist[i]->Model));
1990     gtk_widget_show(item);
1991     g_object_set_data(G_OBJECT(item), "lfLens", (void *)lenslist[i]);
1992     g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(lens_menu_select), self);
1993     gtk_menu_shell_append(GTK_MENU_SHELL(submenu), item);
1994   }
1995 
1996   g->lens_menu = GTK_MENU(gtk_menu_new());
1997   for(i = 0; i < makers->len; i++)
1998   {
1999     GtkWidget *item = gtk_menu_item_new_with_label((const gchar *)g_ptr_array_index(makers, i));
2000     gtk_widget_show(item);
2001     gtk_menu_shell_append(GTK_MENU_SHELL(g->lens_menu), item);
2002     gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), (GtkWidget *)g_ptr_array_index(submenus, i));
2003   }
2004 
2005   g_ptr_array_free(submenus, TRUE);
2006   g_ptr_array_free(makers, TRUE);
2007 }
2008 
lens_menusearch_clicked(GtkWidget * button,gpointer user_data)2009 static void lens_menusearch_clicked(GtkWidget *button, gpointer user_data)
2010 {
2011   dt_iop_module_t *self = (dt_iop_module_t *)user_data;
2012   dt_iop_lensfun_global_data_t *gd = (dt_iop_lensfun_global_data_t *)self->global_data;
2013   lfDatabase *dt_iop_lensfun_db = (lfDatabase *)gd->db;
2014   dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
2015   const lfLens **lenslist;
2016 
2017   (void)button;
2018 
2019   dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
2020   lenslist = dt_iop_lensfun_db->FindLenses(g->camera, NULL, NULL, LF_SEARCH_SORT_AND_UNIQUIFY);
2021   dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
2022   if(!lenslist) return;
2023   lens_menu_fill(self, lenslist);
2024   lf_free(lenslist);
2025 
2026 #if GTK_CHECK_VERSION(3, 22, 0)
2027   gtk_menu_popup_at_pointer(GTK_MENU(g->lens_menu), NULL);
2028 #else
2029   gtk_menu_popup(GTK_MENU(g->lens_menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
2030 #endif
2031 }
2032 
lens_autosearch_clicked(GtkWidget * button,gpointer user_data)2033 static void lens_autosearch_clicked(GtkWidget *button, gpointer user_data)
2034 {
2035   dt_iop_module_t *self = (dt_iop_module_t *)user_data;
2036   dt_iop_lensfun_global_data_t *gd = (dt_iop_lensfun_global_data_t *)self->global_data;
2037   lfDatabase *dt_iop_lensfun_db = (lfDatabase *)gd->db;
2038   dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
2039   const lfLens **lenslist;
2040   char model[200];
2041   const gchar *txt = ((dt_iop_lensfun_params_t *)self->default_params)->lens;
2042 
2043   (void)button;
2044 
2045   parse_model(txt, model, sizeof(model));
2046   dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
2047   lenslist = dt_iop_lensfun_db->FindLenses(g->camera, NULL,
2048                                            model[0] ? model : NULL, LF_SEARCH_SORT_AND_UNIQUIFY);
2049   dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
2050   if(!lenslist) return;
2051   lens_menu_fill(self, lenslist);
2052   lf_free(lenslist);
2053 
2054 #if GTK_CHECK_VERSION(3, 22, 0)
2055   gtk_menu_popup_at_pointer(GTK_MENU(g->lens_menu), NULL);
2056 #else
2057   gtk_menu_popup(GTK_MENU(g->lens_menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
2058 #endif
2059 }
2060 
2061 /* -- end lens -- */
2062 
target_geometry_changed(GtkWidget * widget,gpointer user_data)2063 static void target_geometry_changed(GtkWidget *widget, gpointer user_data)
2064 {
2065   dt_iop_module_t *self = (dt_iop_module_t *)user_data;
2066   dt_iop_lensfun_params_t *p = (dt_iop_lensfun_params_t *)self->params;
2067 
2068   int pos = dt_bauhaus_combobox_get(widget);
2069   p->target_geom = (lfLensType)(pos + LF_UNKNOWN + 1);
2070   p->modified = 1;
2071   dt_dev_add_history_item(darktable.develop, self, TRUE);
2072 }
2073 
modflags_changed(GtkWidget * widget,gpointer user_data)2074 static void modflags_changed(GtkWidget *widget, gpointer user_data)
2075 {
2076   dt_iop_module_t *self = (dt_iop_module_t *)user_data;
2077   if(darktable.gui->reset) return;
2078   dt_iop_lensfun_params_t *p = (dt_iop_lensfun_params_t *)self->params;
2079   dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
2080   int pos = dt_bauhaus_combobox_get(widget);
2081   for(GList *modifiers = g->modifiers;  modifiers; modifiers = g_list_next(modifiers))
2082   {
2083     dt_iop_lensfun_modifier_t *mm = (dt_iop_lensfun_modifier_t *)modifiers->data;
2084     if(mm->pos == pos)
2085     {
2086       p->modify_flags = (p->modify_flags & ~LENSFUN_MODFLAG_MASK) | mm->modflag;
2087       p->modified = 1;
2088       dt_dev_add_history_item(darktable.develop, self, TRUE);
2089       break;
2090     }
2091   }
2092 }
2093 
gui_changed(dt_iop_module_t * self,GtkWidget * w,void * previous)2094 void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
2095 {
2096   dt_iop_lensfun_params_t *p = (dt_iop_lensfun_params_t *)self->params;
2097   dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
2098 
2099   // update gui to show/hide tca sliders if tca_override was changed
2100   if(!w || w == g->tca_override)
2101   {
2102     // show tca sliders only iff tca_overwrite is set
2103     gtk_widget_set_visible(g->tca_r, p->tca_override);
2104     gtk_widget_set_visible(g->tca_b, p->tca_override);
2105   }
2106 
2107   if(w)
2108   {
2109     // user did modify something with some widget
2110     p->modified = 1;
2111   }
2112 }
2113 
2114 
get_autoscale(dt_iop_module_t * self,dt_iop_lensfun_params_t * p,const lfCamera * camera)2115 static float get_autoscale(dt_iop_module_t *self, dt_iop_lensfun_params_t *p, const lfCamera *camera)
2116 {
2117   dt_iop_lensfun_global_data_t *gd = (dt_iop_lensfun_global_data_t *)self->global_data;
2118   lfDatabase *dt_iop_lensfun_db = (lfDatabase *)gd->db;
2119   float scale = 1.0;
2120   if(p->lens[0] != '\0')
2121   {
2122     dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
2123     const lfLens **lenslist
2124         = dt_iop_lensfun_db->FindLenses(camera, NULL, p->lens, 0);
2125     if(lenslist)
2126     {
2127       const dt_image_t *img = &(self->dev->image_storage);
2128 
2129       // FIXME: get those from rawprepare IOP somehow !!!
2130       const int iwd = img->width - img->crop_x - img->crop_width,
2131                 iht = img->height - img->crop_y - img->crop_height;
2132 
2133       // create dummy modifier
2134 #if defined(__GNUC__) && (__GNUC__ > 7)
2135       const dt_iop_lensfun_data_t d =
2136         {
2137          .lens         = (lfLens *)lenslist[0],
2138          .modify_flags = p->modify_flags,
2139          .inverse      = p->inverse,
2140          .scale        = 1.0f,
2141          .crop         = p->crop,
2142          .focal        = p->focal,
2143          .aperture     = p->aperture,
2144          .distance     = p->distance,
2145          .target_geom  = p->target_geom,
2146          .custom_tca   = { .Model = LF_TCA_MODEL_NONE }
2147         };
2148 #else
2149       // prior to GCC 8.x the / .custom_tca   = { .Model = ??? } / was not supported:
2150       //    sorry, unimplemented: non-trivial designated initializers not supported
2151       // ?? This code can be removed when GCC-7 is not used anymore.
2152 
2153       dt_iop_lensfun_data_t d;
2154       d.lens             = (lfLens *)lenslist[0];
2155       d.modify_flags     = p->modify_flags;
2156       d.inverse          = p->inverse;
2157       d.scale            = 1.0f;
2158       d.crop             = p->crop;
2159       d.focal            = p->focal;
2160       d.aperture         = p->aperture;
2161       d.distance         = p->distance;
2162       d.target_geom      = p->target_geom;
2163       d.custom_tca.Model = LF_TCA_MODEL_NONE;
2164 #endif
2165 
2166       lfModifier *modifier = get_modifier(NULL, iwd, iht, &d, LF_MODIFY_ALL, FALSE);
2167 
2168       scale = modifier->GetAutoScale(p->inverse);
2169       delete modifier;
2170     }
2171     lf_free(lenslist);
2172     dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
2173   }
2174   return scale;
2175 }
2176 
autoscale_pressed(GtkWidget * button,gpointer user_data)2177 static void autoscale_pressed(GtkWidget *button, gpointer user_data)
2178 {
2179   dt_iop_module_t *self = (dt_iop_module_t *)user_data;
2180   dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
2181   dt_iop_lensfun_params_t *p = (dt_iop_lensfun_params_t *)self->params;
2182   const float scale = get_autoscale(self, p, g->camera);
2183   p->modified = 1;
2184   dt_bauhaus_slider_set(g->scale, scale);
2185 }
2186 
corrections_done(gpointer instance,gpointer user_data)2187 static void corrections_done(gpointer instance, gpointer user_data)
2188 {
2189   dt_iop_module_t *self = (dt_iop_module_t *)user_data;
2190   dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
2191   if(darktable.gui->reset) return;
2192 
2193   dt_iop_gui_enter_critical_section(self);
2194   const int corrections_done = g->corrections_done;
2195   dt_iop_gui_leave_critical_section(self);
2196 
2197   const char empty_message[] = "";
2198   char *message = (char *)empty_message;
2199   for(GList *modifiers = g->modifiers; modifiers && self->enabled; modifiers = g_list_next(modifiers))
2200   {
2201     dt_iop_lensfun_modifier_t *mm = (dt_iop_lensfun_modifier_t *)modifiers->data;
2202     if(mm->modflag == corrections_done)
2203     {
2204       message = mm->name;
2205       break;
2206     }
2207   }
2208 
2209   ++darktable.gui->reset;
2210   gtk_label_set_text(g->message, message);
2211   gtk_widget_set_tooltip_text(GTK_WIDGET(g->message), message);
2212   --darktable.gui->reset;
2213 }
2214 
gui_init(struct dt_iop_module_t * self)2215 void gui_init(struct dt_iop_module_t *self)
2216 {
2217   dt_iop_lensfun_gui_data_t *g = IOP_GUI_ALLOC(lensfun);
2218 
2219   g->camera = NULL;
2220   g->camera_menu = NULL;
2221   g->lens_menu = NULL;
2222   g->modifiers = NULL;
2223 
2224   dt_iop_gui_enter_critical_section(self); // not actually needed, we're the only one with a ref to this instance
2225   g->corrections_done = -1;
2226   dt_iop_gui_leave_critical_section(self);
2227 
2228   // initialize modflags options
2229   int pos = -1;
2230   dt_iop_lensfun_modifier_t *modifier;
2231   modifier = (dt_iop_lensfun_modifier_t *)g_malloc0(sizeof(dt_iop_lensfun_modifier_t));
2232   dt_utf8_strlcpy(modifier->name, _("none"), sizeof(modifier->name));
2233   g->modifiers = g_list_append(g->modifiers, modifier);
2234   modifier->modflag = LENSFUN_MODFLAG_NONE;
2235   modifier->pos = ++pos;
2236 
2237   modifier = (dt_iop_lensfun_modifier_t *)g_malloc0(sizeof(dt_iop_lensfun_modifier_t));
2238   dt_utf8_strlcpy(modifier->name, _("all"), sizeof(modifier->name));
2239   g->modifiers = g_list_append(g->modifiers, modifier);
2240   modifier->modflag = LENSFUN_MODFLAG_ALL;
2241   modifier->pos = ++pos;
2242 
2243   modifier = (dt_iop_lensfun_modifier_t *)g_malloc0(sizeof(dt_iop_lensfun_modifier_t));
2244   dt_utf8_strlcpy(modifier->name, _("distortion & TCA"), sizeof(modifier->name));
2245   g->modifiers = g_list_append(g->modifiers, modifier);
2246   modifier->modflag = LENSFUN_MODFLAG_DIST_TCA;
2247   modifier->pos = ++pos;
2248 
2249   modifier = (dt_iop_lensfun_modifier_t *)g_malloc0(sizeof(dt_iop_lensfun_modifier_t));
2250   dt_utf8_strlcpy(modifier->name, _("distortion & vignetting"), sizeof(modifier->name));
2251   g->modifiers = g_list_append(g->modifiers, modifier);
2252   modifier->modflag = LENSFUN_MODFLAG_DIST_VIGN;
2253   modifier->pos = ++pos;
2254 
2255   modifier = (dt_iop_lensfun_modifier_t *)g_malloc0(sizeof(dt_iop_lensfun_modifier_t));
2256   dt_utf8_strlcpy(modifier->name, _("TCA & vignetting"), sizeof(modifier->name));
2257   g->modifiers = g_list_append(g->modifiers, modifier);
2258   modifier->modflag = LENSFUN_MODFLAG_TCA_VIGN;
2259   modifier->pos = ++pos;
2260 
2261   modifier = (dt_iop_lensfun_modifier_t *)g_malloc0(sizeof(dt_iop_lensfun_modifier_t));
2262   dt_utf8_strlcpy(modifier->name, _("only distortion"), sizeof(modifier->name));
2263   g->modifiers = g_list_append(g->modifiers, modifier);
2264   modifier->modflag = LENSFUN_MODFLAG_DIST;
2265   modifier->pos = ++pos;
2266 
2267   modifier = (dt_iop_lensfun_modifier_t *)g_malloc0(sizeof(dt_iop_lensfun_modifier_t));
2268   dt_utf8_strlcpy(modifier->name, _("only TCA"), sizeof(modifier->name));
2269   g->modifiers = g_list_append(g->modifiers, modifier);
2270   modifier->modflag = LENSFUN_MODFLAG_TCA;
2271   modifier->pos = ++pos;
2272 
2273   modifier = (dt_iop_lensfun_modifier_t *)g_malloc0(sizeof(dt_iop_lensfun_modifier_t));
2274   dt_utf8_strlcpy(modifier->name, _("only vignetting"), sizeof(modifier->name));
2275   g->modifiers = g_list_append(g->modifiers, modifier);
2276   modifier->modflag = LENSFUN_MODFLAG_VIGN;
2277   modifier->pos = ++pos;
2278 
2279   self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_BAUHAUS_SPACE);
2280 
2281   // camera selector
2282   GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2283   g->camera_model = dt_iop_button_new(self, N_("camera model"),
2284                                       G_CALLBACK(camera_menusearch_clicked), FALSE, 0, (GdkModifierType)0,
2285                                       NULL, 0, hbox);
2286   dt_gui_key_accel_block_on_focus_connect(GTK_WIDGET(g->camera_model));
2287   g->find_camera_button = dt_iop_button_new(self, N_("find camera"),
2288                                             G_CALLBACK(camera_autosearch_clicked), FALSE, 0, (GdkModifierType)0,
2289                                             dtgtk_cairo_paint_solid_triangle, CPF_DIRECTION_DOWN, NULL);
2290   gtk_box_pack_start(GTK_BOX(hbox), g->find_camera_button, FALSE, FALSE, 0);
2291   gtk_box_pack_start(GTK_BOX(self->widget), hbox, TRUE, TRUE, 0);
2292 
2293   // lens selector
2294   hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2295   g->lens_model = dt_iop_button_new(self, N_("lens model"),
2296                                     G_CALLBACK(lens_menusearch_clicked), FALSE, 0, (GdkModifierType)0,
2297                                     NULL, 0, hbox);
2298   dt_gui_key_accel_block_on_focus_connect(GTK_WIDGET(g->lens_model));
2299   g->find_lens_button = dt_iop_button_new(self, N_("find lens"),
2300                                           G_CALLBACK(lens_autosearch_clicked), FALSE, 0, (GdkModifierType)0,
2301                                           dtgtk_cairo_paint_solid_triangle, CPF_DIRECTION_DOWN, NULL);
2302   gtk_box_pack_start(GTK_BOX(hbox), g->find_lens_button, FALSE, FALSE, 0);
2303   gtk_box_pack_start(GTK_BOX(self->widget), hbox, TRUE, TRUE, 0);
2304 
2305   // lens properties
2306   g->lens_param_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2307   gtk_box_pack_start(GTK_BOX(self->widget), g->lens_param_box, TRUE, TRUE, 0);
2308 
2309   // camera/lens not detected warning box
2310   g->detection_warning = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2311   gtk_box_pack_start(GTK_BOX(self->widget), g->detection_warning, TRUE, TRUE, 0);
2312 
2313 #if 0
2314   // if unambiguous info is there, use it.
2315   if(self->dev->image_storage.exif_lens[0] != '\0')
2316   {
2317     char make [200], model [200];
2318     const gchar *txt = gtk_entry_get_text(GTK_ENTRY(g->lens_model));
2319     parse_maker_model (txt, make, sizeof (make), model, sizeof (model));
2320     dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
2321     const lfLens **lenslist = lf_db_find_lenses_hd (dt_iop_lensfun_db, g->camera,
2322                               make [0] ? make : NULL,
2323                               model [0] ? model : NULL, 0);
2324     if(lenslist) lens_set (self, lenslist[0]);
2325     lf_free (lenslist);
2326     dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
2327   }
2328 #endif
2329 
2330   // selector for correction type (modflags): one or more out of distortion, TCA, vignetting
2331   g->modflags = dt_bauhaus_combobox_new(self);
2332   dt_bauhaus_widget_set_label(g->modflags, NULL, N_("corrections"));
2333   gtk_box_pack_start(GTK_BOX(self->widget), g->modflags, TRUE, TRUE, 0);
2334   gtk_widget_set_tooltip_text(g->modflags, _("which corrections to apply"));
2335   GList *l = g->modifiers;
2336   while(l)
2337   {
2338     modifier = (dt_iop_lensfun_modifier_t *)l->data;
2339     dt_bauhaus_combobox_add(g->modflags, modifier->name);
2340     l = g_list_next(l);
2341   }
2342   dt_bauhaus_combobox_set(g->modflags, 0);
2343   g_signal_connect(G_OBJECT(g->modflags), "value-changed", G_CALLBACK(modflags_changed), (gpointer)self);
2344 
2345   // target geometry
2346   g->target_geom = dt_bauhaus_combobox_new(self);
2347   dt_bauhaus_widget_set_label(g->target_geom, NULL, N_("geometry"));
2348   gtk_box_pack_start(GTK_BOX(self->widget), g->target_geom, TRUE, TRUE, 0);
2349   gtk_widget_set_tooltip_text(g->target_geom, _("target geometry"));
2350   dt_bauhaus_combobox_add(g->target_geom, _("rectilinear"));
2351   dt_bauhaus_combobox_add(g->target_geom, _("fish-eye"));
2352   dt_bauhaus_combobox_add(g->target_geom, _("panoramic"));
2353   dt_bauhaus_combobox_add(g->target_geom, _("equirectangular"));
2354 #if LF_VERSION >= ((0 << 24) | (2 << 16) | (6 << 8) | 0)
2355   dt_bauhaus_combobox_add(g->target_geom, _("orthographic"));
2356   dt_bauhaus_combobox_add(g->target_geom, _("stereographic"));
2357   dt_bauhaus_combobox_add(g->target_geom, _("equisolid angle"));
2358   dt_bauhaus_combobox_add(g->target_geom, _("thoby fish-eye"));
2359 #endif
2360   g_signal_connect(G_OBJECT(g->target_geom), "value-changed", G_CALLBACK(target_geometry_changed),
2361                    (gpointer)self);
2362 
2363   // scale
2364   g->scale = dt_bauhaus_slider_from_params(self, N_("scale"));
2365   dt_bauhaus_slider_set_step(g->scale, 0.005);
2366   dt_bauhaus_slider_set_digits(g->scale, 3);
2367   dt_bauhaus_widget_set_quad_paint(g->scale, dtgtk_cairo_paint_refresh, 0, NULL);
2368   g_signal_connect(G_OBJECT(g->scale), "quad-pressed", G_CALLBACK(autoscale_pressed), self);
2369   gtk_widget_set_tooltip_text(g->scale, _("auto scale"));
2370 
2371   // reverse direction
2372   g->reverse = dt_bauhaus_combobox_from_params(self, "inverse");
2373   dt_bauhaus_combobox_add(g->reverse, _("correct"));
2374   dt_bauhaus_combobox_add(g->reverse, _("distort"));
2375   gtk_widget_set_tooltip_text(g->reverse, _("correct distortions or apply them"));
2376 
2377   g->tca_override = dt_bauhaus_toggle_from_params(self, "tca_override");
2378 
2379   // override linear tca (if not 1.0):
2380   g->tca_r = dt_bauhaus_slider_from_params(self, "tca_r");
2381   dt_bauhaus_slider_set_digits(g->tca_r, 5);
2382   gtk_widget_set_tooltip_text(g->tca_r, _("Transversal Chromatic Aberration red"));
2383 
2384   g->tca_b = dt_bauhaus_slider_from_params(self, "tca_b");
2385   dt_bauhaus_slider_set_digits(g->tca_b, 5);
2386   gtk_widget_set_tooltip_text(g->tca_b, _("Transversal Chromatic Aberration blue"));
2387 
2388   // message box to inform user what corrections have been done. this is useful as depending on lensfuns
2389   // profile only some of the lens flaws can be corrected
2390   GtkBox *hbox1 = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0));
2391   GtkWidget *label = gtk_label_new(_("corrections done: "));
2392   gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_MIDDLE);
2393   gtk_widget_set_tooltip_text(label, _("which corrections have actually been done"));
2394   gtk_box_pack_start(GTK_BOX(hbox1), label, FALSE, FALSE, 0);
2395   g->message = GTK_LABEL(gtk_label_new("")); // This gets filled in by process
2396   gtk_label_set_ellipsize(GTK_LABEL(g->message), PANGO_ELLIPSIZE_MIDDLE);
2397   gtk_box_pack_start(GTK_BOX(hbox1), GTK_WIDGET(g->message), FALSE, FALSE, 0);
2398   gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(hbox1), TRUE, TRUE, 0);
2399 
2400   /* add signal handler for preview pipe finish to update message on corrections done */
2401   DT_DEBUG_CONTROL_SIGNAL_CONNECT(darktable.signals, DT_SIGNAL_DEVELOP_PREVIEW_PIPE_FINISHED,
2402                             G_CALLBACK(corrections_done), self);
2403 }
2404 
gui_update(struct dt_iop_module_t * self)2405 void gui_update(struct dt_iop_module_t *self)
2406 {
2407   // let gui elements reflect params
2408   dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
2409   dt_iop_lensfun_params_t *p = (dt_iop_lensfun_params_t *)self->params;
2410 
2411   if(p->modified == 0)
2412   {
2413     /*
2414      * user did not modify anything in gui after autodetection - let's
2415      * use current default_params as params - for presets and mass-export
2416      */
2417     memcpy(self->params, self->default_params, sizeof(dt_iop_lensfun_params_t));
2418   }
2419 
2420   dt_iop_lensfun_global_data_t *gd = (dt_iop_lensfun_global_data_t *)self->global_data;
2421   lfDatabase *dt_iop_lensfun_db = (lfDatabase *)gd->db;
2422   // these are the wrong (untranslated) strings in general but that's ok, they will be overwritten further
2423   // down
2424   gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(g->camera_model))), p->camera);
2425   gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(g->lens_model))), p->lens);
2426   gtk_widget_set_tooltip_text(g->camera_model, "");
2427   gtk_widget_set_tooltip_text(g->lens_model, "");
2428 
2429   int modflag = p->modify_flags & LENSFUN_MODFLAG_MASK;
2430   for(GList *modifiers = g->modifiers; modifiers; modifiers = g_list_next(modifiers))
2431   {
2432     dt_iop_lensfun_modifier_t *mm = (dt_iop_lensfun_modifier_t *)modifiers->data;
2433     if(mm->modflag == modflag)
2434     {
2435       dt_bauhaus_combobox_set(g->modflags, mm->pos);
2436       break;
2437     }
2438   }
2439 
2440   dt_bauhaus_combobox_set(g->target_geom, p->target_geom - LF_UNKNOWN - 1);
2441   dt_bauhaus_combobox_set(g->reverse, p->inverse);
2442   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->tca_override), p->tca_override);
2443   dt_bauhaus_slider_set(g->tca_r, p->tca_r);
2444   dt_bauhaus_slider_set(g->tca_b, p->tca_b);
2445   dt_bauhaus_slider_set(g->scale, p->scale);
2446   const lfCamera **cam = NULL;
2447   g->camera = NULL;
2448   if(p->camera[0])
2449   {
2450     dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
2451     cam = dt_iop_lensfun_db->FindCamerasExt(NULL, p->camera, 0);
2452     dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
2453     if(cam)
2454       camera_set(self, cam[0]);
2455     else
2456       camera_set(self, NULL);
2457   }
2458   if(g->camera && p->lens[0])
2459   {
2460     char model[200];
2461     parse_model(p->lens, model, sizeof(model));
2462     dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
2463     const lfLens **lenslist = dt_iop_lensfun_db->FindLenses(g->camera, NULL,
2464                                                             model[0] ? model : NULL, 0);
2465     if(lenslist)
2466       lens_set(self, lenslist[0]);
2467     else
2468       lens_set(self, NULL);
2469     lf_free(lenslist);
2470     dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
2471   }
2472   else
2473   {
2474     dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
2475     lens_set(self, NULL);
2476     dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
2477   }
2478 
2479   gui_changed(self, NULL, NULL);
2480 }
2481 
gui_cleanup(struct dt_iop_module_t * self)2482 void gui_cleanup(struct dt_iop_module_t *self)
2483 {
2484   dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
2485 
2486   DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(darktable.signals, G_CALLBACK(corrections_done), self);
2487 
2488   dt_gui_key_accel_block_on_focus_disconnect(GTK_WIDGET(g->lens_model));
2489   dt_gui_key_accel_block_on_focus_disconnect(GTK_WIDGET(g->camera_model));
2490   while(g->modifiers)
2491   {
2492     g_free(g->modifiers->data);
2493     g->modifiers = g_list_delete_link(g->modifiers, g->modifiers);
2494   }
2495 
2496   IOP_GUI_FREE;
2497 }
2498 
2499 }
2500 
2501 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
2502 // vim: shiftwidth=2 expandtab tabstop=2 cindent
2503 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
2504