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