Revision 74f2632206090a0dfd2cc7ddb91a875f22a9347b authored by houz on 19 December 2015, 21:41:39 UTC, committed by houz on 19 December 2015, 21:41:39 UTC
Possible fix for the import dialog crash.
lens.c
/*
This file is part of darktable,
copyright (c) 2009--2012 johannes hanika.
copyright (c) 2014-2015 LebedevRI.
darktable is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
darktable is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with darktable. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include <string.h>
#include <gtk/gtk.h>
#include <inttypes.h>
#include <ctype.h>
#include <lensfun.h>
#include "develop/develop.h"
#include "develop/imageop.h"
#include "develop/tiling.h"
#include "common/opencl.h"
#include "common/interpolation.h"
#include "control/control.h"
#include "dtgtk/button.h"
#include "dtgtk/resetlabel.h"
#include "bauhaus/bauhaus.h"
#include "gui/accelerators.h"
#include "gui/gtk.h"
#include "gui/draw.h"
#if LF_VERSION < ((0 << 24) | (2 << 16) | (9 << 8) | 0)
#define LF_SEARCH_SORT_AND_UNIQUIFY 2
#endif
#ifndef __GNUC_PREREQ
// on OSX, gcc-4.6 and clang chokes if this is not here.
#if defined __GNUC__ && defined __GNUC_MINOR__
#define __GNUC_PREREQ(maj, min) ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
#else
#define __GNUC_PREREQ(maj, min) 0
#endif
#endif
DT_MODULE_INTROSPECTION(5, dt_iop_lensfun_params_t)
typedef enum dt_iop_lensfun_modflag_t
{
LENSFUN_MODFLAG_NONE = 0,
LENSFUN_MODFLAG_ALL = LF_MODIFY_DISTORTION | LF_MODIFY_TCA | LF_MODIFY_VIGNETTING,
LENSFUN_MODFLAG_DIST_TCA = LF_MODIFY_DISTORTION | LF_MODIFY_TCA,
LENSFUN_MODFLAG_DIST_VIGN = LF_MODIFY_DISTORTION | LF_MODIFY_VIGNETTING,
LENSFUN_MODFLAG_TCA_VIGN = LF_MODIFY_TCA | LF_MODIFY_VIGNETTING,
LENSFUN_MODFLAG_DIST = LF_MODIFY_DISTORTION,
LENSFUN_MODFLAG_TCA = LF_MODIFY_TCA,
LENSFUN_MODFLAG_VIGN = LF_MODIFY_VIGNETTING,
LENSFUN_MODFLAG_MASK = LF_MODIFY_DISTORTION | LF_MODIFY_TCA | LF_MODIFY_VIGNETTING
} dt_iop_lensfun_modflag_t;
typedef struct dt_iop_lensfun_modifier_t
{
char name[40];
int pos; // position in combo box
int modflag;
} dt_iop_lensfun_modifier_t;
typedef struct dt_iop_lensfun_params_t
{
int modify_flags;
int inverse;
float scale;
float crop;
float focal;
float aperture;
float distance;
lfLensType target_geom;
char camera[128];
char lens[128];
int tca_override;
float tca_r, tca_b;
int modified; // did user changed anything from automatically detected?
} dt_iop_lensfun_params_t;
typedef struct dt_iop_lensfun_gui_data_t
{
const lfCamera *camera;
GtkWidget *lens_param_box;
GtkWidget *detection_warning;
GtkWidget *cbe[3];
GtkButton *camera_model;
GtkMenu *camera_menu;
GtkButton *lens_model;
GtkMenu *lens_menu;
GtkWidget *modflags, *target_geom, *reverse, *tca_r, *tca_b, *scale;
GtkWidget *find_lens_button;
GtkWidget *find_camera_button;
GList *modifiers;
GtkLabel *message;
int corrections_done;
dt_pthread_mutex_t lock;
} dt_iop_lensfun_gui_data_t;
typedef struct dt_iop_lensfun_global_data_t
{
lfDatabase *db;
int kernel_lens_distort_bilinear;
int kernel_lens_distort_bicubic;
int kernel_lens_distort_lanczos2;
int kernel_lens_distort_lanczos3;
int kernel_lens_vignette;
} dt_iop_lensfun_global_data_t;
typedef struct dt_iop_lensfun_data_t
{
lfLens *lens;
int modify_flags;
int inverse;
float scale;
float crop;
float focal;
float aperture;
float distance;
lfLensType target_geom;
gboolean do_nan_checks;
} dt_iop_lensfun_data_t;
const char *name()
{
return _("lens correction");
}
int groups()
{
return IOP_GROUP_CORRECT;
}
int operation_tags()
{
return IOP_TAG_DISTORT;
}
int flags()
{
return IOP_FLAGS_ALLOW_TILING | IOP_FLAGS_TILING_FULL_ROI | IOP_FLAGS_ONE_INSTANCE;
}
void init_key_accels(dt_iop_module_so_t *self)
{
dt_accel_register_slider_iop(self, FALSE, NC_("accel", "scale"));
dt_accel_register_slider_iop(self, FALSE, NC_("accel", "TCA R"));
dt_accel_register_slider_iop(self, FALSE, NC_("accel", "TCA B"));
dt_accel_register_iop(self, FALSE, NC_("accel", "find camera"), 0, 0);
dt_accel_register_iop(self, FALSE, NC_("accel", "find lens"), 0, 0);
dt_accel_register_iop(self, FALSE, NC_("accel", "auto scale"), 0, 0);
dt_accel_register_iop(self, FALSE, NC_("accel", "camera model"), 0, 0);
dt_accel_register_iop(self, FALSE, NC_("accel", "lens model"), 0, 0);
dt_accel_register_iop(self, FALSE, NC_("accel", "select corrections"), 0, 0);
}
void connect_key_accels(dt_iop_module_t *self)
{
dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
dt_accel_connect_button_iop(self, "find lens", GTK_WIDGET(g->find_lens_button));
dt_accel_connect_button_iop(self, "lens model", GTK_WIDGET(g->lens_model));
dt_accel_connect_button_iop(self, "camera model", GTK_WIDGET(g->camera_model));
dt_accel_connect_button_iop(self, "find camera", GTK_WIDGET(g->find_camera_button));
dt_accel_connect_button_iop(self, "select corrections", GTK_WIDGET(g->modflags));
dt_accel_connect_slider_iop(self, "scale", GTK_WIDGET(g->scale));
dt_accel_connect_slider_iop(self, "tca R", GTK_WIDGET(g->tca_r));
dt_accel_connect_slider_iop(self, "tca B", GTK_WIDGET(g->tca_b));
}
int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version,
void *new_params, const int new_version)
{
if(old_version == 2 && new_version == 5)
{
// legacy params of version 2; version 1 comes from ancient times and seems to be forgotten by now
typedef struct dt_iop_lensfun_params_v2_t
{
int modify_flags;
int inverse;
float scale;
float crop;
float focal;
float aperture;
float distance;
lfLensType target_geom;
char camera[52];
char lens[52];
int tca_override;
float tca_r, tca_b;
} dt_iop_lensfun_params_v2_t;
const dt_iop_lensfun_params_v2_t *o = (dt_iop_lensfun_params_v2_t *)old_params;
dt_iop_lensfun_params_t *n = (dt_iop_lensfun_params_t *)new_params;
dt_iop_lensfun_params_t *d = (dt_iop_lensfun_params_t *)self->default_params;
*n = *d; // start with a fresh copy of default parameters
n->modify_flags = o->modify_flags;
n->inverse = o->inverse;
n->scale = o->scale;
n->crop = o->crop;
n->focal = o->focal;
n->aperture = o->aperture;
n->distance = o->distance;
n->target_geom = o->target_geom;
n->tca_override = o->tca_override;
strncpy(n->camera, o->camera, sizeof(n->camera));
strncpy(n->lens, o->lens, sizeof(n->lens));
n->modified = 1;
// old versions had R and B swapped
n->tca_r = o->tca_b;
n->tca_b = o->tca_r;
return 0;
}
if(old_version == 3 && new_version == 5)
{
typedef struct dt_iop_lensfun_params_v3_t
{
int modify_flags;
int inverse;
float scale;
float crop;
float focal;
float aperture;
float distance;
lfLensType target_geom;
char camera[128];
char lens[128];
int tca_override;
float tca_r, tca_b;
} dt_iop_lensfun_params_v3_t;
const dt_iop_lensfun_params_v3_t *o = (dt_iop_lensfun_params_v3_t *)old_params;
dt_iop_lensfun_params_t *n = (dt_iop_lensfun_params_t *)new_params;
dt_iop_lensfun_params_t *d = (dt_iop_lensfun_params_t *)self->default_params;
*n = *d; // start with a fresh copy of default parameters
memcpy(n, o, sizeof(dt_iop_lensfun_params_t) - sizeof(int));
// one more parameter and changed parameters in case we autodetect
n->modified = 1;
// old versions had R and B swapped
n->tca_r = o->tca_b;
n->tca_b = o->tca_r;
return 0;
}
if(old_version == 4 && new_version == 5)
{
typedef struct dt_iop_lensfun_params_v4_t
{
int modify_flags;
int inverse;
float scale;
float crop;
float focal;
float aperture;
float distance;
lfLensType target_geom;
char camera[128];
char lens[128];
int tca_override;
float tca_r, tca_b;
int modified;
} dt_iop_lensfun_params_v4_t;
const dt_iop_lensfun_params_v4_t *o = (dt_iop_lensfun_params_v4_t *)old_params;
dt_iop_lensfun_params_t *n = (dt_iop_lensfun_params_t *)new_params;
dt_iop_lensfun_params_t *d = (dt_iop_lensfun_params_t *)self->default_params;
*n = *d; // start with a fresh copy of default parameters
memcpy(n, o, sizeof(dt_iop_lensfun_params_t));
// old versions had R and B swapped
n->tca_r = o->tca_b;
n->tca_b = o->tca_r;
return 0;
}
return 1;
}
static char *_lens_sanitize(const char *orig_lens)
{
char *found_or = strstr(orig_lens, " or ");
char *found_parenthesis = strstr(orig_lens, " (");
if(found_or || found_parenthesis)
{
size_t pos_or = (size_t)(found_or - orig_lens);
size_t pos_parenthesis = (size_t)(found_parenthesis - orig_lens);
size_t pos = pos_or < pos_parenthesis ? pos_or : pos_parenthesis;
if(pos > 0)
{
char *new_lens = (char *)malloc(pos + 1);
strncpy(new_lens, orig_lens, pos);
new_lens[pos] = '\0';
return new_lens;
}
else
{
char *new_lens = strdup(orig_lens);
return new_lens;
}
}
else
{
char *new_lens = strdup(orig_lens);
return new_lens;
}
}
void process(dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *ovoid,
const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
{
const dt_iop_lensfun_data_t *const d = (dt_iop_lensfun_data_t *)piece->data;
dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
const int ch = piece->colors;
const int ch_width = ch * roi_in->width;
const int mask_display = piece->pipe->mask_display;
const unsigned int pixelformat = ch == 3 ? LF_CR_3(RED, GREEN, BLUE) : LF_CR_4(RED, GREEN, BLUE, UNKNOWN);
if(!d->lens || !d->lens->Maker || d->crop <= 0.0f)
{
memcpy(ovoid, ivoid, (size_t)ch * sizeof(float) * roi_out->width * roi_out->height);
return;
}
const float orig_w = roi_in->scale * piece->buf_in.width, orig_h = roi_in->scale * piece->buf_in.height;
dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
lfModifier *modifier = lf_modifier_new(d->lens, d->crop, orig_w, orig_h);
const int modflags
= lf_modifier_initialize(modifier, d->lens, LF_PF_F32, d->focal, d->aperture, d->distance, d->scale,
d->target_geom, d->modify_flags, d->inverse);
dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
const struct dt_interpolation *const interpolation = dt_interpolation_new(DT_INTERPOLATION_USERPREF);
if(d->inverse)
{
// reverse direction (useful for renderings)
if(modflags & (LF_MODIFY_TCA | LF_MODIFY_DISTORTION | LF_MODIFY_GEOMETRY | LF_MODIFY_SCALE))
{
// acquire temp memory for distorted pixel coords
const size_t bufsize = (size_t)roi_out->width * 2 * 3;
void *buf = dt_alloc_align(16, bufsize * dt_get_num_threads() * sizeof(float));
#ifdef _OPENMP
#pragma omp parallel for default(none) shared(buf, modifier, ovoid) schedule(static)
#endif
for(int y = 0; y < roi_out->height; y++)
{
float *bufptr = ((float *)buf) + (size_t)bufsize * dt_get_thread_num();
lf_modifier_apply_subpixel_geometry_distortion(modifier, roi_out->x, roi_out->y + y, roi_out->width,
1, bufptr);
// reverse transform the global coords from lf to our buffer
float *out = ((float *)ovoid) + (size_t)y * roi_out->width * ch;
for(int x = 0; x < roi_out->width; x++, bufptr += 6, out += ch)
{
for(int c = 0; c < 3; c++)
{
if(d->do_nan_checks && (!isfinite(bufptr[c * 2]) || !isfinite(bufptr[c * 2 + 1])))
{
out[c] = 0.0f;
continue;
}
const float *const inptr = (const float *const)ivoid + (size_t)c;
const float pi0 = bufptr[c * 2] - roi_in->x;
const float pi1 = bufptr[c * 2 + 1] - roi_in->y;
out[c] = dt_interpolation_compute_sample(interpolation, inptr, pi0, pi1, roi_in->width,
roi_in->height, ch, ch_width);
}
if(mask_display)
{
if(d->do_nan_checks && (!isfinite(bufptr[2]) || !isfinite(bufptr[3])))
{
out[3] = 0.0f;
continue;
}
// take green channel distortion also for alpha channel
const float *const inptr = (const float *const)ivoid + (size_t)3;
const float pi0 = bufptr[2] - roi_in->x;
const float pi1 = bufptr[3] - roi_in->y;
out[3] = dt_interpolation_compute_sample(interpolation, inptr, pi0, pi1, roi_in->width,
roi_in->height, ch, ch_width);
}
}
}
dt_free_align(buf);
}
else
{
memcpy(ovoid, ivoid, (size_t)ch * sizeof(float) * roi_out->width * roi_out->height);
}
if(modflags & LF_MODIFY_VIGNETTING)
{
#ifdef _OPENMP
#pragma omp parallel for default(none) shared(ovoid, modifier) schedule(static)
#endif
for(int y = 0; y < roi_out->height; y++)
{
/* Colour correction: vignetting */
// actually this way row stride does not matter.
float *out = ((float *)ovoid) + (size_t)y * roi_out->width * ch;
lf_modifier_apply_color_modification(modifier, out, roi_out->x, roi_out->y + y, roi_out->width, 1,
pixelformat, ch * roi_out->width);
}
}
}
else // correct distortions:
{
// acquire temp memory for image buffer
const size_t bufsize = (size_t)roi_in->width * roi_in->height * ch * sizeof(float);
void *buf = dt_alloc_align(16, bufsize);
memcpy(buf, ivoid, bufsize);
if(modflags & LF_MODIFY_VIGNETTING)
{
#ifdef _OPENMP
#pragma omp parallel for default(none) shared(buf, modifier) schedule(static)
#endif
for(int y = 0; y < roi_in->height; y++)
{
/* Colour correction: vignetting */
// actually this way row stride does not matter.
float *bufptr = ((float *)buf) + (size_t)ch * roi_in->width * y;
lf_modifier_apply_color_modification(modifier, bufptr, roi_in->x, roi_in->y + y, roi_in->width, 1,
pixelformat, ch * roi_in->width);
}
}
if(modflags & (LF_MODIFY_TCA | LF_MODIFY_DISTORTION | LF_MODIFY_GEOMETRY | LF_MODIFY_SCALE))
{
// acquire temp memory for distorted pixel coords
const size_t buf2size = (size_t)roi_out->width * 2 * 3;
void *buf2 = dt_alloc_align(16, buf2size * sizeof(float) * dt_get_num_threads());
#ifdef _OPENMP
#pragma omp parallel for default(none) shared(buf2, buf, modifier, ovoid) schedule(static)
#endif
for(int y = 0; y < roi_out->height; y++)
{
float *buf2ptr = ((float *)buf2) + (size_t)buf2size * dt_get_thread_num();
lf_modifier_apply_subpixel_geometry_distortion(modifier, roi_out->x, roi_out->y + y, roi_out->width,
1, buf2ptr);
// reverse transform the global coords from lf to our buffer
float *out = ((float *)ovoid) + (size_t)y * roi_out->width * ch;
for(int x = 0; x < roi_out->width; x++, buf2ptr += 6, out += ch)
{
for(int c = 0; c < 3; c++)
{
if(d->do_nan_checks && (!isfinite(buf2ptr[c * 2]) || !isfinite(buf2ptr[c * 2 + 1])))
{
out[c] = 0.0f;
continue;
}
float *bufptr = ((float *)buf) + c;
const float pi0 = buf2ptr[c * 2] - roi_in->x;
const float pi1 = buf2ptr[c * 2 + 1] - roi_in->y;
out[c] = dt_interpolation_compute_sample(interpolation, bufptr, pi0, pi1, roi_in->width,
roi_in->height, ch, ch_width);
}
if(mask_display)
{
if(d->do_nan_checks && (!isfinite(buf2ptr[2]) || !isfinite(buf2ptr[3])))
{
out[3] = 0.0f;
continue;
}
// take green channel distortion also for alpha channel
float *bufptr = ((float *)buf) + 3;
const float pi0 = buf2ptr[2] - roi_in->x;
const float pi1 = buf2ptr[3] - roi_in->y;
out[3] = dt_interpolation_compute_sample(interpolation, bufptr, pi0, pi1, roi_in->width,
roi_in->height, ch, ch_width);
}
}
}
dt_free_align(buf2);
}
else
{
memcpy(ovoid, buf, bufsize);
}
dt_free_align(buf);
}
lf_modifier_destroy(modifier);
if(self->dev->gui_attached && g)
{
dt_pthread_mutex_lock(&g->lock);
g->corrections_done = (modflags & LENSFUN_MODFLAG_MASK);
dt_pthread_mutex_unlock(&g->lock);
}
}
#ifdef HAVE_OPENCL
int 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 *roi_in, const dt_iop_roi_t *roi_out)
{
dt_iop_lensfun_data_t *d = (dt_iop_lensfun_data_t *)piece->data;
dt_iop_lensfun_global_data_t *gd = (dt_iop_lensfun_global_data_t *)self->data;
dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
cl_mem dev_tmpbuf = NULL;
cl_mem dev_tmp = NULL;
cl_int err = -999;
float *tmpbuf = NULL;
lfModifier *modifier = NULL;
const int devid = piece->pipe->devid;
const int iwidth = roi_in->width;
const int iheight = roi_in->height;
const int owidth = roi_out->width;
const int oheight = roi_out->height;
const int roi_in_x = roi_in->x;
const int roi_in_y = roi_in->y;
const int width = MAX(iwidth, owidth);
const int height = MAX(iheight, oheight);
const int ch = piece->colors;
const int tmpbufwidth = owidth * 2 * 3;
const size_t tmpbuflen = d->inverse ? (size_t)oheight * owidth * 2 * 3 * sizeof(float)
: MAX((size_t)oheight * owidth * 2 * 3, (size_t)iheight * iwidth * ch)
* sizeof(float);
const unsigned int pixelformat = ch == 3 ? LF_CR_3(RED, GREEN, BLUE) : LF_CR_4(RED, GREEN, BLUE, UNKNOWN);
const float orig_w = roi_in->scale * piece->buf_in.width, orig_h = roi_in->scale * piece->buf_in.height;
size_t origin[] = { 0, 0, 0 };
size_t iregion[] = { iwidth, iheight, 1 };
size_t oregion[] = { owidth, oheight, 1 };
size_t isizes[] = { ROUNDUPWD(iwidth), ROUNDUPHT(iheight), 1 };
size_t osizes[] = { ROUNDUPWD(owidth), ROUNDUPHT(oheight), 1 };
if(!d->lens || !d->lens->Maker || d->crop <= 0.0f)
{
err = dt_opencl_enqueue_copy_image(devid, dev_in, dev_out, origin, origin, oregion);
if(err != CL_SUCCESS) goto error;
return TRUE;
}
const struct dt_interpolation *interpolation = dt_interpolation_new(DT_INTERPOLATION_USERPREF);
int ldkernel = -1;
switch(interpolation->id)
{
case DT_INTERPOLATION_BILINEAR:
ldkernel = gd->kernel_lens_distort_bilinear;
break;
case DT_INTERPOLATION_BICUBIC:
ldkernel = gd->kernel_lens_distort_bicubic;
break;
case DT_INTERPOLATION_LANCZOS2:
ldkernel = gd->kernel_lens_distort_lanczos2;
break;
case DT_INTERPOLATION_LANCZOS3:
ldkernel = gd->kernel_lens_distort_lanczos3;
break;
default:
return FALSE;
}
tmpbuf = (float *)dt_alloc_align(16, tmpbuflen);
if(tmpbuf == NULL) goto error;
dev_tmp = dt_opencl_alloc_device(devid, width, height, 4 * sizeof(float));
if(dev_tmp == NULL) goto error;
dev_tmpbuf = dt_opencl_alloc_device_buffer(devid, tmpbuflen);
if(dev_tmpbuf == NULL) goto error;
dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
modifier = lf_modifier_new(d->lens, d->crop, orig_w, orig_h);
int modflags = lf_modifier_initialize(modifier, d->lens, LF_PF_F32, d->focal, d->aperture, d->distance,
d->scale, d->target_geom, d->modify_flags, d->inverse);
dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
if(d->inverse)
{
// reverse direction (useful for renderings)
if(modflags & (LF_MODIFY_TCA | LF_MODIFY_DISTORTION | LF_MODIFY_GEOMETRY | LF_MODIFY_SCALE))
{
#ifdef _OPENMP
#pragma omp parallel for default(none) shared(roi_out, roi_in, tmpbuf, d, modifier) schedule(static)
#endif
for(int y = 0; y < roi_out->height; y++)
{
float *pi = tmpbuf + (size_t)y * tmpbufwidth;
lf_modifier_apply_subpixel_geometry_distortion(modifier, roi_out->x, roi_out->y + y, roi_out->width,
1, pi);
}
/* _blocking_ memory transfer: host tmpbuf buffer -> opencl dev_tmpbuf */
err = dt_opencl_write_buffer_to_device(devid, tmpbuf, dev_tmpbuf, 0,
(size_t)owidth * oheight * 2 * 3 * sizeof(float), CL_TRUE);
if(err != CL_SUCCESS) goto error;
dt_opencl_set_kernel_arg(devid, ldkernel, 0, sizeof(cl_mem), (void *)&dev_in);
dt_opencl_set_kernel_arg(devid, ldkernel, 1, sizeof(cl_mem), (void *)&dev_tmp);
dt_opencl_set_kernel_arg(devid, ldkernel, 2, sizeof(int), (void *)&owidth);
dt_opencl_set_kernel_arg(devid, ldkernel, 3, sizeof(int), (void *)&oheight);
dt_opencl_set_kernel_arg(devid, ldkernel, 4, sizeof(int), (void *)&iwidth);
dt_opencl_set_kernel_arg(devid, ldkernel, 5, sizeof(int), (void *)&iheight);
dt_opencl_set_kernel_arg(devid, ldkernel, 6, sizeof(int), (void *)&roi_in_x);
dt_opencl_set_kernel_arg(devid, ldkernel, 7, sizeof(int), (void *)&roi_in_y);
dt_opencl_set_kernel_arg(devid, ldkernel, 8, sizeof(cl_mem), (void *)&dev_tmpbuf);
dt_opencl_set_kernel_arg(devid, ldkernel, 9, sizeof(int), (void *)&(d->do_nan_checks));
err = dt_opencl_enqueue_kernel_2d(devid, ldkernel, osizes);
if(err != CL_SUCCESS) goto error;
}
else
{
err = dt_opencl_enqueue_copy_image(devid, dev_in, dev_tmp, origin, origin, oregion);
if(err != CL_SUCCESS) goto error;
}
if(modflags & LF_MODIFY_VIGNETTING)
{
#ifdef _OPENMP
#pragma omp parallel for default(none) shared(roi_out, roi_in, tmpbuf, modifier, d) schedule(static)
#endif
for(int y = 0; y < roi_out->height; y++)
{
/* Colour correction: vignetting */
// actually this way row stride does not matter.
float *buf = tmpbuf + (size_t)y * ch * roi_out->width;
for(int k = 0; k < ch * roi_out->width; k++) buf[k] = 0.5f;
lf_modifier_apply_color_modification(modifier, buf, roi_out->x, roi_out->y + y, roi_out->width, 1,
pixelformat, ch * roi_out->width);
}
/* _blocking_ memory transfer: host tmpbuf buffer -> opencl dev_tmpbuf */
err = dt_opencl_write_buffer_to_device(devid, tmpbuf, dev_tmpbuf, 0,
(size_t)ch * roi_out->width * roi_out->height * sizeof(float),
CL_TRUE);
if(err != CL_SUCCESS) goto error;
dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 0, sizeof(cl_mem), (void *)&dev_tmp);
dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 1, sizeof(cl_mem), (void *)&dev_out);
dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 2, sizeof(int), (void *)&owidth);
dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 3, sizeof(int), (void *)&oheight);
dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 4, sizeof(cl_mem), (void *)&dev_tmpbuf);
err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_lens_vignette, osizes);
if(err != CL_SUCCESS) goto error;
}
else
{
err = dt_opencl_enqueue_copy_image(devid, dev_tmp, dev_out, origin, origin, oregion);
if(err != CL_SUCCESS) goto error;
}
}
else // correct distortions:
{
if(modflags & LF_MODIFY_VIGNETTING)
{
#ifdef _OPENMP
#pragma omp parallel for default(none) shared(roi_out, roi_in, tmpbuf, modifier, d) schedule(static)
#endif
for(int y = 0; y < roi_in->height; y++)
{
/* Colour correction: vignetting */
// actually this way row stride does not matter.
float *buf = tmpbuf + (size_t)y * ch * roi_in->width;
for(int k = 0; k < ch * roi_in->width; k++) buf[k] = 0.5f;
lf_modifier_apply_color_modification(modifier, buf, roi_in->x, roi_in->y + y, roi_in->width, 1,
pixelformat, ch * roi_in->width);
}
/* _blocking_ memory transfer: host tmpbuf buffer -> opencl dev_tmpbuf */
err = dt_opencl_write_buffer_to_device(
devid, tmpbuf, dev_tmpbuf, 0, (size_t)ch * roi_in->width * roi_in->height * sizeof(float), CL_TRUE);
if(err != CL_SUCCESS) goto error;
dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 0, sizeof(cl_mem), (void *)&dev_in);
dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 1, sizeof(cl_mem), (void *)&dev_tmp);
dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 2, sizeof(int), (void *)&iwidth);
dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 3, sizeof(int), (void *)&iheight);
dt_opencl_set_kernel_arg(devid, gd->kernel_lens_vignette, 4, sizeof(cl_mem), (void *)&dev_tmpbuf);
err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_lens_vignette, isizes);
if(err != CL_SUCCESS) goto error;
}
else
{
err = dt_opencl_enqueue_copy_image(devid, dev_in, dev_tmp, origin, origin, iregion);
if(err != CL_SUCCESS) goto error;
}
if(modflags & (LF_MODIFY_TCA | LF_MODIFY_DISTORTION | LF_MODIFY_GEOMETRY | LF_MODIFY_SCALE))
{
#ifdef _OPENMP
#pragma omp parallel for default(none) shared(roi_out, roi_in, tmpbuf, d, modifier) schedule(static)
#endif
for(int y = 0; y < roi_out->height; y++)
{
float *pi = tmpbuf + (size_t)y * tmpbufwidth;
lf_modifier_apply_subpixel_geometry_distortion(modifier, roi_out->x, roi_out->y + y, roi_out->width,
1, pi);
}
/* _blocking_ memory transfer: host tmpbuf buffer -> opencl dev_tmpbuf */
err = dt_opencl_write_buffer_to_device(devid, tmpbuf, dev_tmpbuf, 0,
(size_t)owidth * oheight * 2 * 3 * sizeof(float), CL_TRUE);
if(err != CL_SUCCESS) goto error;
dt_opencl_set_kernel_arg(devid, ldkernel, 0, sizeof(cl_mem), (void *)&dev_tmp);
dt_opencl_set_kernel_arg(devid, ldkernel, 1, sizeof(cl_mem), (void *)&dev_out);
dt_opencl_set_kernel_arg(devid, ldkernel, 2, sizeof(int), (void *)&owidth);
dt_opencl_set_kernel_arg(devid, ldkernel, 3, sizeof(int), (void *)&oheight);
dt_opencl_set_kernel_arg(devid, ldkernel, 4, sizeof(int), (void *)&iwidth);
dt_opencl_set_kernel_arg(devid, ldkernel, 5, sizeof(int), (void *)&iheight);
dt_opencl_set_kernel_arg(devid, ldkernel, 6, sizeof(int), (void *)&roi_in_x);
dt_opencl_set_kernel_arg(devid, ldkernel, 7, sizeof(int), (void *)&roi_in_y);
dt_opencl_set_kernel_arg(devid, ldkernel, 8, sizeof(cl_mem), (void *)&dev_tmpbuf);
dt_opencl_set_kernel_arg(devid, ldkernel, 9, sizeof(int), (void *)&(d->do_nan_checks));
err = dt_opencl_enqueue_kernel_2d(devid, ldkernel, osizes);
if(err != CL_SUCCESS) goto error;
}
else
{
err = dt_opencl_enqueue_copy_image(devid, dev_tmp, dev_out, origin, origin, oregion);
if(err != CL_SUCCESS) goto error;
}
}
if(self->dev->gui_attached && g)
{
dt_pthread_mutex_lock(&g->lock);
g->corrections_done = (modflags & LENSFUN_MODFLAG_MASK);
dt_pthread_mutex_unlock(&g->lock);
}
dt_opencl_release_mem_object(dev_tmpbuf);
dt_opencl_release_mem_object(dev_tmp);
if(tmpbuf != NULL) dt_free_align(tmpbuf);
if(modifier != NULL) lf_modifier_destroy(modifier);
return TRUE;
error:
if(dev_tmp != NULL) dt_opencl_release_mem_object(dev_tmp);
if(dev_tmpbuf != NULL) dt_opencl_release_mem_object(dev_tmpbuf);
if(tmpbuf != NULL) dt_free_align(tmpbuf);
if(modifier != NULL) lf_modifier_destroy(modifier);
dt_print(DT_DEBUG_OPENCL, "[opencl_lens] couldn't enqueue kernel! %d\n", err);
return FALSE;
}
#endif
void 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)
{
tiling->factor = 4.5f; // in + out + tmp + tmpbuf
tiling->maxbuf = 1.5f;
tiling->overhead = 0;
tiling->overlap = 4;
tiling->xalign = 1;
tiling->yalign = 1;
return;
}
int distort_transform(dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, float *points, size_t points_count)
{
dt_iop_lensfun_data_t *d = (dt_iop_lensfun_data_t *)piece->data;
if(!d->lens || !d->lens->Maker || d->crop <= 0.0f) return 0;
const float orig_w = piece->buf_in.width, orig_h = piece->buf_in.height;
lfModifier *modifier = lf_modifier_new(d->lens, d->crop, orig_w, orig_h);
int modflags = lf_modifier_initialize(modifier, d->lens, LF_PF_F32, d->focal, d->aperture, d->distance,
d->scale, d->target_geom, d->modify_flags, !d->inverse);
float *buf = malloc(2 * 3 * sizeof(float));
for(size_t i = 0; i < points_count * 2; i += 2)
{
if(modflags & (LF_MODIFY_TCA | LF_MODIFY_DISTORTION | LF_MODIFY_GEOMETRY | LF_MODIFY_SCALE))
{
lf_modifier_apply_subpixel_geometry_distortion(modifier, points[i], points[i + 1], 1, 1, buf);
points[i] = buf[0];
points[i + 1] = buf[3];
}
}
free(buf);
lf_modifier_destroy(modifier);
return 1;
}
int distort_backtransform(dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, float *points,
size_t points_count)
{
dt_iop_lensfun_data_t *d = (dt_iop_lensfun_data_t *)piece->data;
if(!d->lens || !d->lens->Maker || d->crop <= 0.0f) return 0;
const float orig_w = piece->buf_in.width, orig_h = piece->buf_in.height;
lfModifier *modifier = lf_modifier_new(d->lens, d->crop, orig_w, orig_h);
int modflags = lf_modifier_initialize(modifier, d->lens, LF_PF_F32, d->focal, d->aperture, d->distance,
d->scale, d->target_geom, d->modify_flags, d->inverse);
float *buf = malloc(2 * 3 * sizeof(float));
for(size_t i = 0; i < points_count * 2; i += 2)
{
if(modflags & (LF_MODIFY_TCA | LF_MODIFY_DISTORTION | LF_MODIFY_GEOMETRY | LF_MODIFY_SCALE))
{
lf_modifier_apply_subpixel_geometry_distortion(modifier, points[i], points[i + 1], 1, 1, buf);
points[i] = buf[0];
points[i + 1] = buf[3];
}
}
free(buf);
lf_modifier_destroy(modifier);
return 1;
}
void 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)
{
*roi_out = *roi_in;
}
void 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)
{
dt_iop_lensfun_data_t *d = (dt_iop_lensfun_data_t *)piece->data;
*roi_in = *roi_out;
// inverse transform with given params
if(!d->lens || !d->lens->Maker || d->crop <= 0.0f) return;
const float orig_w = roi_in->scale * piece->buf_in.width, orig_h = roi_in->scale * piece->buf_in.height;
lfModifier *modifier = lf_modifier_new(d->lens, d->crop, orig_w, orig_h);
float xm = FLT_MAX, xM = -FLT_MAX, ym = FLT_MAX, yM = -FLT_MAX;
int modflags = lf_modifier_initialize(modifier, d->lens, LF_PF_F32, d->focal, d->aperture, d->distance,
d->scale, d->target_geom, d->modify_flags, d->inverse);
if(modflags & (LF_MODIFY_TCA | LF_MODIFY_DISTORTION | LF_MODIFY_GEOMETRY | LF_MODIFY_SCALE))
{
// acquire temp memory for distorted pixel coords
const size_t bufsize = (size_t)roi_in->width * 2 * 3;
#if defined(_OPENMP) && __GNUC_PREREQ(4, 7)
void *buf = dt_alloc_align(16, bufsize * dt_get_num_threads() * sizeof(float));
#pragma omp parallel for default(none) shared(buf, modifier) reduction(min : xm, ym) reduction(max : xM, yM) \
schedule(static)
#else
void *buf = dt_alloc_align(16, bufsize * sizeof(float));
#endif
for(int y = 0; y < roi_out->height; y++)
{
float *bufptr = ((float *)buf) + (size_t)bufsize * dt_get_thread_num();
lf_modifier_apply_subpixel_geometry_distortion(modifier, roi_out->x, roi_out->y + y, roi_out->width, 1,
bufptr);
// reverse transform the global coords from lf to our buffer
for(int x = 0; x < roi_out->width; x++)
{
for(int c = 0; c < 3; c++, bufptr += 2)
{
xm = MIN(xm, bufptr[0]);
xM = MAX(xM, bufptr[0]);
ym = MIN(ym, bufptr[1]);
yM = MAX(yM, bufptr[1]);
}
}
}
dt_free_align(buf);
// LensFun can return NAN coords, so we need to handle them carefully.
if(!isfinite(xm) || !(0 <= xm && xm < orig_w)) xm = 0;
if(!isfinite(xM) || !(1 <= xM && xM < orig_w)) xM = orig_w;
if(!isfinite(ym) || !(0 <= ym && ym < orig_h)) ym = 0;
if(!isfinite(yM) || !(1 <= yM && yM < orig_h)) yM = orig_h;
const struct dt_interpolation *interpolation = dt_interpolation_new(DT_INTERPOLATION_USERPREF);
roi_in->x = fmaxf(0.0f, xm - interpolation->width);
roi_in->y = fmaxf(0.0f, ym - interpolation->width);
roi_in->width = fminf(orig_w - roi_in->x, xM - roi_in->x + interpolation->width);
roi_in->height = fminf(orig_h - roi_in->y, yM - roi_in->y + interpolation->width);
// sanity check.
roi_in->x = CLAMP(roi_in->x, 0, (int)floorf(orig_w));
roi_in->y = CLAMP(roi_in->y, 0, (int)floorf(orig_h));
roi_in->width = CLAMP(roi_in->width, 1, (int)ceilf(orig_w) - roi_in->x);
roi_in->height = CLAMP(roi_in->height, 1, (int)ceilf(orig_h) - roi_in->y);
}
lf_modifier_destroy(modifier);
}
void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe,
dt_dev_pixelpipe_iop_t *piece)
{
dt_iop_lensfun_params_t *p = (dt_iop_lensfun_params_t *)p1;
if(p->modified == 0)
{
/*
* user did not modify anything in gui after autodetection - let's
* use current default_params as params - for presets and mass-export
*/
p = (dt_iop_lensfun_params_t *)self->default_params;
}
#ifdef HAVE_GEGL
// pull in new params to gegl
#error "lensfun needs to be ported to GEGL!"
#else
dt_iop_lensfun_data_t *d = (dt_iop_lensfun_data_t *)piece->data;
dt_iop_lensfun_global_data_t *gd = (dt_iop_lensfun_global_data_t *)self->data;
lfDatabase *dt_iop_lensfun_db = (lfDatabase *)gd->db;
const lfCamera *camera = NULL;
const lfCamera **cam = NULL;
if(d->lens)
{
lf_lens_destroy(d->lens);
d->lens = NULL;
}
d->lens = lf_lens_new();
if(p->camera[0])
{
dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
cam = lf_db_find_cameras_ext(dt_iop_lensfun_db, NULL, p->camera, 0);
if(cam)
{
camera = cam[0];
d->crop = cam[0]->CropFactor;
}
dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
}
if(p->lens[0])
{
dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
const lfLens **lens
= lf_db_find_lenses_hd(dt_iop_lensfun_db, camera, NULL, p->lens, LF_SEARCH_SORT_AND_UNIQUIFY);
dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
if(lens)
{
lf_lens_copy(d->lens, lens[0]);
if(p->tca_override)
{
// add manual d->lens stuff:
lfLensCalibTCA tca = { 0 };
tca.Focal = 0;
tca.Model = LF_TCA_MODEL_LINEAR;
tca.Terms[0] = p->tca_r;
tca.Terms[1] = p->tca_b;
if(d->lens->CalibTCA)
while(d->lens->CalibTCA[0]) lf_lens_remove_calib_tca(d->lens, 0);
lf_lens_add_calib_tca(d->lens, &tca);
}
lf_free(lens);
}
}
lf_free(cam);
d->modify_flags = p->modify_flags;
d->inverse = p->inverse;
d->scale = p->scale;
d->focal = p->focal;
d->aperture = p->aperture;
d->distance = p->distance;
d->target_geom = p->target_geom;
d->do_nan_checks = TRUE;
/*
* there are certain situations when LensFun can return NAN coordinated.
* most common case would be when the FOV is increased.
*/
if(d->target_geom == LF_RECTILINEAR)
{
d->do_nan_checks = FALSE;
}
else if(d->target_geom == d->lens->Type)
{
d->do_nan_checks = FALSE;
}
#endif
}
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
{
#ifdef HAVE_GEGL
#error "lensfun needs to be ported to GEGL!"
#else
piece->data = calloc(1, sizeof(dt_iop_lensfun_data_t));
self->commit_params(self, self->default_params, pipe, piece);
#endif
}
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
{
#ifdef HAVE_GEGL
#error "lensfun needs to be ported to GEGL!"
#else
dt_iop_lensfun_data_t *d = (dt_iop_lensfun_data_t *)piece->data;
if(d->lens)
{
lf_lens_destroy(d->lens);
d->lens = NULL;
}
free(piece->data);
piece->data = NULL;
#endif
}
void init_global(dt_iop_module_so_t *module)
{
const int program = 2; // basic.cl, from programs.conf
dt_iop_lensfun_global_data_t *gd
= (dt_iop_lensfun_global_data_t *)calloc(1, sizeof(dt_iop_lensfun_global_data_t));
module->data = gd;
gd->kernel_lens_distort_bilinear = dt_opencl_create_kernel(program, "lens_distort_bilinear");
gd->kernel_lens_distort_bicubic = dt_opencl_create_kernel(program, "lens_distort_bicubic");
gd->kernel_lens_distort_lanczos2 = dt_opencl_create_kernel(program, "lens_distort_lanczos2");
gd->kernel_lens_distort_lanczos3 = dt_opencl_create_kernel(program, "lens_distort_lanczos3");
gd->kernel_lens_vignette = dt_opencl_create_kernel(program, "lens_vignette");
lfDatabase *dt_iop_lensfun_db = lf_db_new();
gd->db = (void *)dt_iop_lensfun_db;
#if defined(__MACH__) || defined(__APPLE__)
#else
if(lf_db_load(dt_iop_lensfun_db) != LF_NO_ERROR)
#endif
{
char path[PATH_MAX] = { 0 };
dt_loc_get_datadir(path, sizeof(path));
char *c = path + strlen(path);
for(; c > path && *c != '/'; c--)
;
#ifdef LF_MAX_DATABASE_VERSION
snprintf(c, PATH_MAX - (c - path), "/lensfun/version_%d", LF_MAX_DATABASE_VERSION);
g_free(dt_iop_lensfun_db->HomeDataDir);
dt_iop_lensfun_db->HomeDataDir = g_strdup(path);
if(lf_db_load(dt_iop_lensfun_db) != LF_NO_ERROR)
{
fprintf(stderr, "[iop_lens]: could not load lensfun database in `%s'!\n", path);
#endif
snprintf(c, PATH_MAX - (c - path), "/lensfun");
g_free(dt_iop_lensfun_db->HomeDataDir);
dt_iop_lensfun_db->HomeDataDir = g_strdup(path);
if(lf_db_load(dt_iop_lensfun_db) != LF_NO_ERROR)
fprintf(stderr, "[iop_lens]: could not load lensfun database in `%s'!\n", path);
#ifdef LF_MAX_DATABASE_VERSION
}
#endif
}
}
static float get_autoscale(dt_iop_module_t *self, dt_iop_lensfun_params_t *p, const lfCamera *camera);
void reload_defaults(dt_iop_module_t *module)
{
// reload image specific stuff
// get all we can from exif:
dt_iop_lensfun_params_t tmp = { 0 };
// we might be called from presets update infrastructure => there is no image
if(!module || !module->dev) goto end;
const dt_image_t *img = &module->dev->image_storage;
char *new_lens = _lens_sanitize(img->exif_lens);
g_strlcpy(tmp.lens, new_lens, sizeof(tmp.lens));
free(new_lens);
g_strlcpy(tmp.camera, img->exif_model, sizeof(tmp.camera));
tmp.crop = img->exif_crop;
tmp.aperture = img->exif_aperture;
tmp.focal = img->exif_focal_length;
tmp.scale = 1.0;
tmp.inverse = 0;
tmp.modify_flags = LF_MODIFY_TCA | LF_MODIFY_VIGNETTING | LF_MODIFY_DISTORTION | LF_MODIFY_GEOMETRY
| LF_MODIFY_SCALE;
// if we did not find focus_distance in EXIF, lets default to 1000
tmp.distance = img->exif_focus_distance == 0.0f ? 1000.0f : img->exif_focus_distance;
tmp.target_geom = LF_RECTILINEAR;
tmp.tca_override = 0;
tmp.tca_r = 1.0;
tmp.tca_b = 1.0;
tmp.modified = 0;
// init crop from db:
char model[100]; // truncate often complex descriptions.
g_strlcpy(model, img->exif_model, sizeof(model));
for(char cnt = 0, *c = model; c < model + 100 && *c != '\0'; c++)
if(*c == ' ')
if(++cnt == 2) *c = '\0';
if(img->exif_maker[0] || model[0])
{
dt_iop_lensfun_global_data_t *gd = (dt_iop_lensfun_global_data_t *)module->data;
// just to be sure
if(!gd || !gd->db) goto end;
dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
const lfCamera **cam = lf_db_find_cameras_ext(gd->db, img->exif_maker, img->exif_model, 0);
dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
if(cam)
{
dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
const lfLens **lens = lf_db_find_lenses_hd(gd->db, cam[0], NULL, tmp.lens, LF_SEARCH_SORT_AND_UNIQUIFY);
dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
if(!lens && islower(cam[0]->Mount[0]))
{
/*
* This is a fixed-lens camera, and LF returned no lens.
* (reasons: lens is "(65535)" or lens is correct lens name,
* but LF have it as "fixed lens")
*
* Let's unset lens name and re-run lens query
*/
g_strlcpy(tmp.lens, "", sizeof(tmp.lens));
dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
lens = lf_db_find_lenses_hd(gd->db, cam[0], NULL, tmp.lens, LF_SEARCH_SORT_AND_UNIQUIFY);
dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
}
if(lens)
{
int lens_i = 0;
/*
* Current SVN lensfun lets you test for a fixed-lens camera by looking
* at the zeroth character in the mount's name:
* If it is a lower case letter, it is a fixed-lens camera.
*/
if(!tmp.lens[0] && islower(cam[0]->Mount[0]))
{
/*
* no lens info in EXIF, and this is fixed-lens camera,
* let's find shortest lens model in the list of possible lenses
*/
size_t min_model_len = SIZE_MAX;
for(int i = 0; lens[i]; i++)
{
if(strlen(lens[i]->Model) < min_model_len)
{
min_model_len = strlen(lens[i]->Model);
lens_i = i;
}
}
// and set lens to it
g_strlcpy(tmp.lens, lens[lens_i]->Model, sizeof(tmp.lens));
}
tmp.target_geom = lens[lens_i]->Type;
lf_free(lens);
}
tmp.crop = cam[0]->CropFactor;
tmp.scale = get_autoscale(module, &tmp, cam[0]);
lf_free(cam);
}
}
end:
memcpy(module->params, &tmp, sizeof(dt_iop_lensfun_params_t));
memcpy(module->default_params, &tmp, sizeof(dt_iop_lensfun_params_t));
module->default_enabled = 0;
}
void init(dt_iop_module_t *module)
{
module->params = calloc(1, sizeof(dt_iop_lensfun_params_t));
module->default_params = calloc(1, sizeof(dt_iop_lensfun_params_t));
module->default_enabled = 0;
module->params_size = sizeof(dt_iop_lensfun_params_t);
module->gui_data = NULL;
module->priority = 250; // module order created by iop_dependencies.py, do not edit!
}
void cleanup(dt_iop_module_t *module)
{
free(module->params);
module->params = NULL;
}
void cleanup_global(dt_iop_module_so_t *module)
{
dt_iop_lensfun_global_data_t *gd = (dt_iop_lensfun_global_data_t *)module->data;
lfDatabase *dt_iop_lensfun_db = (lfDatabase *)gd->db;
lf_db_destroy(dt_iop_lensfun_db);
dt_opencl_free_kernel(gd->kernel_lens_distort_bilinear);
dt_opencl_free_kernel(gd->kernel_lens_distort_bicubic);
dt_opencl_free_kernel(gd->kernel_lens_distort_lanczos2);
dt_opencl_free_kernel(gd->kernel_lens_distort_lanczos3);
dt_opencl_free_kernel(gd->kernel_lens_vignette);
free(module->data);
module->data = NULL;
}
/// ############################################################
/// gui stuff: inspired by ufraws lensfun tab:
/* simple function to compute the floating-point precision
which is enough for "normal use". The criteria is to have
about 3 leading digits after the initial zeros. */
static int precision(double x, double adj)
{
x *= adj;
if(x == 0) return 1;
if(x < 1.0)
if(x < 0.1)
if(x < 0.01)
return 5;
else
return 4;
else
return 3;
else if(x < 100.0)
if(x < 10.0)
return 2;
else
return 1;
else
return 0;
}
/* -- ufraw ptr array functions -- */
static int ptr_array_insert_sorted(GPtrArray *array, const void *item, GCompareFunc compare)
{
int length = array->len;
g_ptr_array_set_size(array, length + 1);
const void **root = (const void **)array->pdata;
int m = 0, l = 0, r = length - 1;
// Skip trailing NULL, if any
if(l <= r && !root[r]) r--;
while(l <= r)
{
m = (l + r) / 2;
int cmp = compare(root[m], item);
if(cmp == 0)
{
++m;
goto done;
}
else if(cmp < 0)
l = m + 1;
else
r = m - 1;
}
if(r == m) m++;
done:
memmove(root + m + 1, root + m, (length - m) * sizeof(void *));
root[m] = item;
return m;
}
static int ptr_array_find_sorted(const GPtrArray *array, const void *item, GCompareFunc compare)
{
int length = array->len;
void **root = array->pdata;
int l = 0, r = length - 1;
int m = 0, cmp = 0;
if(!length) return -1;
// Skip trailing NULL, if any
if(!root[r]) r--;
while(l <= r)
{
m = (l + r) / 2;
cmp = compare(root[m], item);
if(cmp == 0)
return m;
else if(cmp < 0)
l = m + 1;
else
r = m - 1;
}
return -1;
}
static void ptr_array_insert_index(GPtrArray *array, const void *item, int index)
{
const void **root;
int length = array->len;
g_ptr_array_set_size(array, length + 1);
root = (const void **)array->pdata;
memmove(root + index + 1, root + index, (length - index) * sizeof(void *));
root[index] = item;
}
/* -- end ufraw ptr array functions -- */
/* -- camera -- */
static void camera_set(dt_iop_module_t *self, const lfCamera *cam)
{
dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
dt_iop_lensfun_params_t *p = (dt_iop_lensfun_params_t *)self->params;
gchar *fm;
const char *maker, *model, *variant;
char _variant[100];
if(!cam)
{
gtk_button_set_label(GTK_BUTTON(g->camera_model), "");
gtk_label_set_ellipsize(GTK_LABEL(gtk_bin_get_child(GTK_BIN(g->camera_model))), PANGO_ELLIPSIZE_END);
g_object_set(G_OBJECT(g->camera_model), "tooltip-text", "", (char *)NULL);
return;
}
g_strlcpy(p->camera, cam->Model, sizeof(p->camera));
p->crop = cam->CropFactor;
g->camera = cam;
maker = lf_mlstr_get(cam->Maker);
model = lf_mlstr_get(cam->Model);
variant = lf_mlstr_get(cam->Variant);
if(model)
{
if(maker)
fm = g_strdup_printf("%s, %s", maker, model);
else
fm = g_strdup_printf("%s", model);
gtk_button_set_label(GTK_BUTTON(g->camera_model), fm);
gtk_label_set_ellipsize(GTK_LABEL(gtk_bin_get_child(GTK_BIN(g->camera_model))), PANGO_ELLIPSIZE_END);
g_free(fm);
}
if(variant)
snprintf(_variant, sizeof(_variant), " (%s)", variant);
else
_variant[0] = 0;
fm = g_strdup_printf(_("maker:\t\t%s\n"
"model:\t\t%s%s\n"
"mount:\t\t%s\n"
"crop factor:\t%.1f"),
maker, model, _variant, cam->Mount, cam->CropFactor);
g_object_set(G_OBJECT(g->camera_model), "tooltip-text", fm, (char *)NULL);
g_free(fm);
}
static void camera_menu_select(GtkMenuItem *menuitem, gpointer user_data)
{
dt_iop_module_t *self = (dt_iop_module_t *)user_data;
camera_set(self, (lfCamera *)g_object_get_data(G_OBJECT(menuitem), "lfCamera"));
if(darktable.gui->reset) return;
dt_iop_lensfun_params_t *p = (dt_iop_lensfun_params_t *)self->params;
p->modified = 1;
dt_dev_add_history_item(darktable.develop, self, TRUE);
}
static void camera_menu_fill(dt_iop_module_t *self, const lfCamera *const *camlist)
{
dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
unsigned i;
GPtrArray *makers, *submenus;
if(g->camera_menu)
{
gtk_widget_destroy(GTK_WIDGET(g->camera_menu));
g->camera_menu = NULL;
}
/* Count all existing camera makers and create a sorted list */
makers = g_ptr_array_new();
submenus = g_ptr_array_new();
for(i = 0; camlist[i]; i++)
{
GtkWidget *submenu, *item;
const char *m = lf_mlstr_get(camlist[i]->Maker);
int idx = ptr_array_find_sorted(makers, m, (GCompareFunc)g_utf8_collate);
if(idx < 0)
{
/* No such maker yet, insert it into the array */
idx = ptr_array_insert_sorted(makers, m, (GCompareFunc)g_utf8_collate);
/* Create a submenu for cameras by this maker */
submenu = gtk_menu_new();
ptr_array_insert_index(submenus, submenu, idx);
}
submenu = g_ptr_array_index(submenus, idx);
/* Append current camera name to the submenu */
m = lf_mlstr_get(camlist[i]->Model);
if(!camlist[i]->Variant)
item = gtk_menu_item_new_with_label(m);
else
{
gchar *fm = g_strdup_printf("%s (%s)", m, camlist[i]->Variant);
item = gtk_menu_item_new_with_label(fm);
g_free(fm);
}
gtk_widget_show(item);
g_object_set_data(G_OBJECT(item), "lfCamera", (void *)camlist[i]);
g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(camera_menu_select), self);
gtk_menu_shell_append(GTK_MENU_SHELL(submenu), item);
}
g->camera_menu = GTK_MENU(gtk_menu_new());
for(i = 0; i < makers->len; i++)
{
GtkWidget *item = gtk_menu_item_new_with_label(g_ptr_array_index(makers, i));
gtk_widget_show(item);
gtk_menu_shell_append(GTK_MENU_SHELL(g->camera_menu), item);
gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), (GtkWidget *)g_ptr_array_index(submenus, i));
}
g_ptr_array_free(submenus, TRUE);
g_ptr_array_free(makers, TRUE);
}
static void parse_maker_model(const char *txt, char *make, size_t sz_make, char *model, size_t sz_model)
{
const gchar *sep;
while(txt[0] && isspace(txt[0])) txt++;
sep = strchr(txt, ',');
if(sep)
{
size_t len = sep - txt;
if(len > sz_make - 1) len = sz_make - 1;
memcpy(make, txt, len);
make[len] = 0;
while(*++sep && isspace(sep[0]))
;
len = strlen(sep);
if(len > sz_model - 1) len = sz_model - 1;
memcpy(model, sep, len);
model[len] = 0;
}
else
{
size_t len = strlen(txt);
if(len > sz_model - 1) len = sz_model - 1;
memcpy(model, txt, len);
model[len] = 0;
make[0] = 0;
}
}
static void camera_menusearch_clicked(GtkWidget *button, gpointer user_data)
{
dt_iop_module_t *self = (dt_iop_module_t *)user_data;
dt_iop_lensfun_global_data_t *gd = (dt_iop_lensfun_global_data_t *)self->data;
lfDatabase *dt_iop_lensfun_db = (lfDatabase *)gd->db;
dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
(void)button;
const lfCamera *const *camlist;
dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
camlist = lf_db_get_cameras(dt_iop_lensfun_db);
dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
if(!camlist) return;
camera_menu_fill(self, camlist);
gtk_menu_popup(GTK_MENU(g->camera_menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
}
static void camera_autosearch_clicked(GtkWidget *button, gpointer user_data)
{
dt_iop_module_t *self = (dt_iop_module_t *)user_data;
dt_iop_lensfun_global_data_t *gd = (dt_iop_lensfun_global_data_t *)self->data;
lfDatabase *dt_iop_lensfun_db = (lfDatabase *)gd->db;
dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
char make[200], model[200];
const gchar *txt = ((dt_iop_lensfun_params_t *)self->default_params)->camera;
(void)button;
if(txt[0] == '\0')
{
const lfCamera *const *camlist;
dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
camlist = lf_db_get_cameras(dt_iop_lensfun_db);
dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
if(!camlist) return;
camera_menu_fill(self, camlist);
}
else
{
parse_maker_model(txt, make, sizeof(make), model, sizeof(model));
dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
const lfCamera **camlist = lf_db_find_cameras_ext(dt_iop_lensfun_db, make, model, 0);
dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
if(!camlist) return;
camera_menu_fill(self, camlist);
lf_free(camlist);
}
gtk_menu_popup(GTK_MENU(g->camera_menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
}
/* -- end camera -- */
static void lens_comboentry_focal_update(GtkWidget *widget, dt_iop_module_t *self)
{
dt_iop_lensfun_params_t *p = (dt_iop_lensfun_params_t *)self->params;
const char *text = dt_bauhaus_combobox_get_text(widget);
if(text) (void)sscanf(text, "%f", &p->focal);
p->modified = 1;
dt_dev_add_history_item(darktable.develop, self, TRUE);
}
static void lens_comboentry_aperture_update(GtkWidget *widget, dt_iop_module_t *self)
{
dt_iop_lensfun_params_t *p = (dt_iop_lensfun_params_t *)self->params;
const char *text = dt_bauhaus_combobox_get_text(widget);
if(text) (void)sscanf(text, "%f", &p->aperture);
p->modified = 1;
dt_dev_add_history_item(darktable.develop, self, TRUE);
}
static void lens_comboentry_distance_update(GtkWidget *widget, dt_iop_module_t *self)
{
dt_iop_lensfun_params_t *p = (dt_iop_lensfun_params_t *)self->params;
const char *text = dt_bauhaus_combobox_get_text(widget);
if(text) (void)sscanf(text, "%f", &p->distance);
p->modified = 1;
dt_dev_add_history_item(darktable.develop, self, TRUE);
}
static void delete_children(GtkWidget *widget, gpointer data)
{
(void)data;
gtk_widget_destroy(widget);
}
static void lens_set(dt_iop_module_t *self, const lfLens *lens)
{
dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
dt_iop_lensfun_params_t *p = (dt_iop_lensfun_params_t *)self->params;
gchar *fm;
const char *maker, *model;
unsigned i;
gdouble focal_values[]
= { -INFINITY, 4.5, 8, 10, 12, 14, 15, 16, 17, 18, 20, 24, 28, 30, 31, 35,
38, 40, 43, 45, 50, 55, 60, 70, 75, 77, 80, 85, 90, 100, 105, 110,
120, 135, 150, 200, 210, 240, 250, 300, 400, 500, 600, 800, 1000, INFINITY };
gdouble aperture_values[]
= { -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,
5.6, 6.3, 7.1, 8, 9, 10, 11, 13, 14, 16, 18, 20, 22, 25, 29, 32, 38, INFINITY };
if(!lens)
{
gtk_widget_set_sensitive(GTK_WIDGET(g->modflags), FALSE);
gtk_widget_set_sensitive(GTK_WIDGET(g->target_geom), FALSE);
gtk_widget_set_sensitive(GTK_WIDGET(g->scale), FALSE);
gtk_widget_set_sensitive(GTK_WIDGET(g->reverse), FALSE);
gtk_widget_set_sensitive(GTK_WIDGET(g->tca_r), FALSE);
gtk_widget_set_sensitive(GTK_WIDGET(g->tca_b), FALSE);
gtk_widget_set_sensitive(GTK_WIDGET(g->message), FALSE);
gtk_container_foreach(GTK_CONTAINER(g->detection_warning), delete_children, NULL);
GtkLabel *label;
label = GTK_LABEL(gtk_label_new(_("camera/lens not found - please select manually")));
g_object_set(G_OBJECT(label), "tooltip-text", _("try to locate your camera/lens in the above two menus"),
(char *)NULL);
gtk_box_pack_start(GTK_BOX(g->detection_warning), GTK_WIDGET(label), FALSE, FALSE, 0);
gtk_widget_hide(g->lens_param_box);
gtk_widget_show_all(g->detection_warning);
return;
}
else
{
gtk_widget_set_sensitive(GTK_WIDGET(g->modflags), TRUE);
gtk_widget_set_sensitive(GTK_WIDGET(g->target_geom), TRUE);
gtk_widget_set_sensitive(GTK_WIDGET(g->scale), TRUE);
gtk_widget_set_sensitive(GTK_WIDGET(g->reverse), TRUE);
gtk_widget_set_sensitive(GTK_WIDGET(g->tca_r), TRUE);
gtk_widget_set_sensitive(GTK_WIDGET(g->tca_b), TRUE);
gtk_widget_set_sensitive(GTK_WIDGET(g->message), TRUE);
}
maker = lf_mlstr_get(lens->Maker);
model = lf_mlstr_get(lens->Model);
g_strlcpy(p->lens, lens->Model, sizeof(p->lens));
if(model)
{
if(maker)
fm = g_strdup_printf("%s, %s", maker, model);
else
fm = g_strdup_printf("%s", model);
gtk_button_set_label(GTK_BUTTON(g->lens_model), fm);
gtk_label_set_ellipsize(GTK_LABEL(gtk_bin_get_child(GTK_BIN(g->lens_model))), PANGO_ELLIPSIZE_END);
g_free(fm);
}
char focal[100], aperture[100], mounts[200];
if(lens->MinFocal < lens->MaxFocal)
snprintf(focal, sizeof(focal), "%g-%gmm", lens->MinFocal, lens->MaxFocal);
else
snprintf(focal, sizeof(focal), "%gmm", lens->MinFocal);
if(lens->MinAperture < lens->MaxAperture)
snprintf(aperture, sizeof(aperture), "%g-%g", lens->MinAperture, lens->MaxAperture);
else
snprintf(aperture, sizeof(aperture), "%g", lens->MinAperture);
mounts[0] = 0;
if(lens->Mounts)
for(i = 0; lens->Mounts[i]; i++)
{
if(i > 0) g_strlcat(mounts, ", ", sizeof(mounts));
g_strlcat(mounts, lens->Mounts[i], sizeof(mounts));
}
fm = g_strdup_printf(_("maker:\t\t%s\n"
"model:\t\t%s\n"
"focal range:\t%s\n"
"aperture:\t\t%s\n"
"crop factor:\t%.1f\n"
"type:\t\t\t%s\n"
"mounts:\t\t%s"),
maker ? maker : "?", model ? model : "?", focal, aperture, lens->CropFactor,
lf_get_lens_type_desc(lens->Type, NULL), mounts);
g_object_set(G_OBJECT(g->lens_model), "tooltip-text", fm, (char *)NULL);
g_free(fm);
/* Create the focal/aperture/distance combo boxes */
gtk_container_foreach(GTK_CONTAINER(g->lens_param_box), delete_children, NULL);
int ffi = 1, fli = -1;
for(i = 1; i < sizeof(focal_values) / sizeof(gdouble) - 1; i++)
{
if(focal_values[i] < lens->MinFocal) ffi = i + 1;
if(focal_values[i] > lens->MaxFocal && fli == -1) fli = i;
}
if(focal_values[ffi] > lens->MinFocal)
{
focal_values[ffi - 1] = lens->MinFocal;
ffi--;
}
if(lens->MaxFocal == 0 || fli < 0) fli = sizeof(focal_values) / sizeof(gdouble) - 2;
if(focal_values[fli + 1] < lens->MaxFocal)
{
focal_values[fli + 1] = lens->MaxFocal;
ffi++;
}
if(fli < ffi) fli = ffi + 1;
GtkWidget *w;
char txt[30];
// focal length
w = dt_bauhaus_combobox_new(self);
dt_bauhaus_widget_set_label(w, NULL, _("mm"));
g_object_set(G_OBJECT(w), "tooltip-text", _("focal length (mm)"), (char *)NULL);
snprintf(txt, sizeof(txt), "%.*f", precision(p->focal, 10.0), p->focal);
dt_bauhaus_combobox_add(w, txt);
for(int k = 0; k < fli - ffi; k++)
{
snprintf(txt, sizeof(txt), "%.*f", precision(focal_values[ffi + k], 10.0), focal_values[ffi + k]);
dt_bauhaus_combobox_add(w, txt);
}
g_signal_connect(G_OBJECT(w), "value-changed", G_CALLBACK(lens_comboentry_focal_update), self);
gtk_box_pack_start(GTK_BOX(g->lens_param_box), w, TRUE, TRUE, 0);
dt_bauhaus_combobox_set_editable(w, 1);
g->cbe[0] = w;
// f-stop
ffi = 1, fli = sizeof(aperture_values) / sizeof(gdouble) - 1;
for(i = 1; i < sizeof(aperture_values) / sizeof(gdouble) - 1; i++)
if(aperture_values[i] < lens->MinAperture) ffi = i + 1;
if(aperture_values[ffi] > lens->MinAperture)
{
aperture_values[ffi - 1] = lens->MinAperture;
ffi--;
}
w = dt_bauhaus_combobox_new(self);
dt_bauhaus_widget_set_label(w, NULL, _("f/"));
g_object_set(G_OBJECT(w), "tooltip-text", _("f-number (aperture)"), (char *)NULL);
snprintf(txt, sizeof(txt), "%.*f", precision(p->aperture, 10.0), p->aperture);
dt_bauhaus_combobox_add(w, txt);
for(size_t k = 0; k < fli - ffi; k++)
{
snprintf(txt, sizeof(txt), "%.*f", precision(aperture_values[ffi + k], 10.0), aperture_values[ffi + k]);
dt_bauhaus_combobox_add(w, txt);
}
g_signal_connect(G_OBJECT(w), "value-changed", G_CALLBACK(lens_comboentry_aperture_update), self);
gtk_box_pack_start(GTK_BOX(g->lens_param_box), w, TRUE, TRUE, 0);
dt_bauhaus_combobox_set_editable(w, 1);
g->cbe[1] = w;
w = dt_bauhaus_combobox_new(self);
dt_bauhaus_widget_set_label(w, NULL, _("d"));
g_object_set(G_OBJECT(w), "tooltip-text", _("distance to subject"), (char *)NULL);
snprintf(txt, sizeof(txt), "%.*f", precision(p->distance, 10.0), p->distance);
dt_bauhaus_combobox_add(w, txt);
float val = 0.25f;
for(int k = 0; k < 25; k++)
{
if(val > 1000.0f) val = 1000.0f;
snprintf(txt, sizeof(txt), "%.*f", precision(val, 10.0), val);
dt_bauhaus_combobox_add(w, txt);
if(val >= 1000.0f) break;
val *= sqrtf(2.0f);
}
g_signal_connect(G_OBJECT(w), "value-changed", G_CALLBACK(lens_comboentry_distance_update), self);
gtk_box_pack_start(GTK_BOX(g->lens_param_box), w, TRUE, TRUE, 0);
dt_bauhaus_combobox_set_editable(w, 1);
g->cbe[2] = w;
gtk_widget_hide(g->detection_warning);
gtk_widget_show_all(g->lens_param_box);
}
static void lens_menu_select(GtkMenuItem *menuitem, gpointer user_data)
{
dt_iop_module_t *self = (dt_iop_module_t *)user_data;
dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
dt_iop_lensfun_params_t *p = (dt_iop_lensfun_params_t *)self->params;
lens_set(self, (lfLens *)g_object_get_data(G_OBJECT(menuitem), "lfLens"));
if(darktable.gui->reset) return;
p->modified = 1;
const float scale = get_autoscale(self, p, g->camera);
dt_bauhaus_slider_set(g->scale, scale);
dt_dev_add_history_item(darktable.develop, self, TRUE);
}
static void lens_menu_fill(dt_iop_module_t *self, const lfLens *const *lenslist)
{
dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
unsigned i;
GPtrArray *makers, *submenus;
if(g->lens_menu)
{
gtk_widget_destroy(GTK_WIDGET(g->lens_menu));
g->lens_menu = NULL;
}
/* Count all existing lens makers and create a sorted list */
makers = g_ptr_array_new();
submenus = g_ptr_array_new();
for(i = 0; lenslist[i]; i++)
{
GtkWidget *submenu, *item;
const char *m = lf_mlstr_get(lenslist[i]->Maker);
int idx = ptr_array_find_sorted(makers, m, (GCompareFunc)g_utf8_collate);
if(idx < 0)
{
/* No such maker yet, insert it into the array */
idx = ptr_array_insert_sorted(makers, m, (GCompareFunc)g_utf8_collate);
/* Create a submenu for lenses by this maker */
submenu = gtk_menu_new();
ptr_array_insert_index(submenus, submenu, idx);
}
submenu = g_ptr_array_index(submenus, idx);
/* Append current lens name to the submenu */
item = gtk_menu_item_new_with_label(lf_mlstr_get(lenslist[i]->Model));
gtk_widget_show(item);
g_object_set_data(G_OBJECT(item), "lfLens", (void *)lenslist[i]);
g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(lens_menu_select), self);
gtk_menu_shell_append(GTK_MENU_SHELL(submenu), item);
}
g->lens_menu = GTK_MENU(gtk_menu_new());
for(i = 0; i < makers->len; i++)
{
GtkWidget *item = gtk_menu_item_new_with_label(g_ptr_array_index(makers, i));
gtk_widget_show(item);
gtk_menu_shell_append(GTK_MENU_SHELL(g->lens_menu), item);
gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), (GtkWidget *)g_ptr_array_index(submenus, i));
}
g_ptr_array_free(submenus, TRUE);
g_ptr_array_free(makers, TRUE);
}
static void lens_menusearch_clicked(GtkWidget *button, gpointer user_data)
{
dt_iop_module_t *self = (dt_iop_module_t *)user_data;
dt_iop_lensfun_global_data_t *gd = (dt_iop_lensfun_global_data_t *)self->data;
lfDatabase *dt_iop_lensfun_db = (lfDatabase *)gd->db;
dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
const lfLens **lenslist;
(void)button;
dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
lenslist = lf_db_find_lenses_hd(dt_iop_lensfun_db, g->camera, NULL, NULL, LF_SEARCH_SORT_AND_UNIQUIFY);
dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
if(!lenslist) return;
lens_menu_fill(self, lenslist);
lf_free(lenslist);
gtk_menu_popup(GTK_MENU(g->lens_menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
}
static void lens_autosearch_clicked(GtkWidget *button, gpointer user_data)
{
dt_iop_module_t *self = (dt_iop_module_t *)user_data;
dt_iop_lensfun_global_data_t *gd = (dt_iop_lensfun_global_data_t *)self->data;
lfDatabase *dt_iop_lensfun_db = (lfDatabase *)gd->db;
dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
const lfLens **lenslist;
char make[200], model[200];
const gchar *txt = ((dt_iop_lensfun_params_t *)self->default_params)->lens;
(void)button;
parse_maker_model(txt, make, sizeof(make), model, sizeof(model));
dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
lenslist = lf_db_find_lenses_hd(dt_iop_lensfun_db, g->camera, make[0] ? make : NULL,
model[0] ? model : NULL, LF_SEARCH_SORT_AND_UNIQUIFY);
dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
if(!lenslist) return;
lens_menu_fill(self, lenslist);
lf_free(lenslist);
gtk_menu_popup(GTK_MENU(g->lens_menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
}
/* -- end lens -- */
static void target_geometry_changed(GtkWidget *widget, gpointer user_data)
{
dt_iop_module_t *self = (dt_iop_module_t *)user_data;
dt_iop_lensfun_params_t *p = (dt_iop_lensfun_params_t *)self->params;
int pos = dt_bauhaus_combobox_get(widget);
p->target_geom = pos + LF_UNKNOWN + 1;
p->modified = 1;
dt_dev_add_history_item(darktable.develop, self, TRUE);
}
static void modflags_changed(GtkWidget *widget, gpointer user_data)
{
dt_iop_module_t *self = (dt_iop_module_t *)user_data;
if(self->dt->gui->reset) return;
dt_iop_lensfun_params_t *p = (dt_iop_lensfun_params_t *)self->params;
dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
int pos = dt_bauhaus_combobox_get(widget);
GList *modifiers = g->modifiers;
while(modifiers)
{
// could use g_list_nth. this seems safer?
dt_iop_lensfun_modifier_t *mm = (dt_iop_lensfun_modifier_t *)modifiers->data;
if(mm->pos == pos)
{
p->modify_flags = (p->modify_flags & ~LENSFUN_MODFLAG_MASK) | mm->modflag;
p->modified = 1;
dt_dev_add_history_item(darktable.develop, self, TRUE);
break;
}
modifiers = g_list_next(modifiers);
}
}
static void reverse_toggled(GtkWidget *widget, gpointer user_data)
{
dt_iop_module_t *self = (dt_iop_module_t *)user_data;
dt_iop_lensfun_params_t *p = (dt_iop_lensfun_params_t *)self->params;
p->inverse = dt_bauhaus_combobox_get(widget);
p->modified = 1;
dt_dev_add_history_item(darktable.develop, self, TRUE);
}
static void tca_changed(GtkWidget *slider, dt_iop_module_t *self)
{
dt_iop_lensfun_params_t *p = (dt_iop_lensfun_params_t *)self->params;
dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
const float val = dt_bauhaus_slider_get(slider);
if(slider == g->tca_r)
p->tca_r = val;
else
p->tca_b = val;
if(p->tca_r != 1.0 || p->tca_b != 1.0) p->tca_override = 1;
p->modified = 1;
dt_dev_add_history_item(darktable.develop, self, TRUE);
}
static void scale_changed(GtkWidget *slider, gpointer user_data)
{
dt_iop_module_t *self = (dt_iop_module_t *)user_data;
dt_iop_lensfun_params_t *p = (dt_iop_lensfun_params_t *)self->params;
p->scale = dt_bauhaus_slider_get(slider);
p->modified = 1;
dt_dev_add_history_item(darktable.develop, self, TRUE);
}
static float get_autoscale(dt_iop_module_t *self, dt_iop_lensfun_params_t *p, const lfCamera *camera)
{
dt_iop_lensfun_global_data_t *gd = (dt_iop_lensfun_global_data_t *)self->data;
lfDatabase *dt_iop_lensfun_db = (lfDatabase *)gd->db;
float scale = 1.0;
if(p->lens[0] != '\0')
{
dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
const lfLens **lenslist
= lf_db_find_lenses_hd(dt_iop_lensfun_db, camera, NULL, p->lens, LF_SEARCH_SORT_AND_UNIQUIFY);
if(lenslist)
{
const dt_image_t *img = &(self->dev->image_storage);
// FIXME: get those from rawprepare IOP somehow !!!
const int iwd = img->width - img->crop_x - img->crop_width,
iht = img->height - img->crop_y - img->crop_height;
// create dummy modifier
lfModifier *modifier = lf_modifier_new(lenslist[0], p->crop, iwd, iht);
(void)lf_modifier_initialize(modifier, lenslist[0], LF_PF_F32, p->focal, p->aperture, p->distance, 1.0f,
p->target_geom, p->modify_flags, p->inverse);
scale = lf_modifier_get_auto_scale(modifier, p->inverse);
lf_modifier_destroy(modifier);
}
lf_free(lenslist);
dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
}
return scale;
}
static void autoscale_pressed(GtkWidget *button, gpointer user_data)
{
dt_iop_module_t *self = (dt_iop_module_t *)user_data;
dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
dt_iop_lensfun_params_t *p = (dt_iop_lensfun_params_t *)self->params;
const float scale = get_autoscale(self, p, g->camera);
p->modified = 1;
dt_bauhaus_slider_set(g->scale, scale);
}
static gboolean draw(GtkWidget *widget, cairo_t *cr, dt_iop_module_t *self)
{
dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
if(darktable.gui->reset) return FALSE;
dt_pthread_mutex_lock(&g->lock);
const int corrections_done = g->corrections_done;
g->corrections_done = -1;
dt_pthread_mutex_unlock(&g->lock);
if(corrections_done == -1) return FALSE;
char *message = "";
GList *modifiers = g->modifiers;
while(modifiers)
{
// could use g_list_nth. this seems safer?
dt_iop_lensfun_modifier_t *mm = (dt_iop_lensfun_modifier_t *)modifiers->data;
if(mm->modflag == corrections_done)
{
message = mm->name;
break;
}
modifiers = g_list_next(modifiers);
}
darktable.gui->reset = 1;
gtk_label_set_text(g->message, message);
darktable.gui->reset = 0;
return FALSE;
}
void gui_init(struct dt_iop_module_t *self)
{
self->gui_data = malloc(sizeof(dt_iop_lensfun_gui_data_t));
// dt_iop_lensfun_global_data_t *gd = (dt_iop_lensfun_global_data_t *)self->data;
// lfDatabase *dt_iop_lensfun_db = (lfDatabase *)gd->db;
dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
dt_iop_lensfun_params_t *p = (dt_iop_lensfun_params_t *)self->params;
dt_pthread_mutex_init(&g->lock, NULL);
g->camera = NULL;
g->camera_menu = NULL;
g->lens_menu = NULL;
g->modifiers = NULL;
dt_pthread_mutex_lock(&g->lock);
g->corrections_done = -1;
dt_pthread_mutex_unlock(&g->lock);
// initialize modflags options
int pos = -1;
dt_iop_lensfun_modifier_t *modifier;
modifier = (dt_iop_lensfun_modifier_t *)g_malloc0(sizeof(dt_iop_lensfun_modifier_t));
dt_utf8_strlcpy(modifier->name, _("none"), sizeof(modifier->name));
g->modifiers = g_list_append(g->modifiers, modifier);
modifier->modflag = LENSFUN_MODFLAG_NONE;
modifier->pos = ++pos;
modifier = (dt_iop_lensfun_modifier_t *)g_malloc0(sizeof(dt_iop_lensfun_modifier_t));
dt_utf8_strlcpy(modifier->name, _("all"), sizeof(modifier->name));
g->modifiers = g_list_append(g->modifiers, modifier);
modifier->modflag = LENSFUN_MODFLAG_ALL;
modifier->pos = ++pos;
modifier = (dt_iop_lensfun_modifier_t *)g_malloc0(sizeof(dt_iop_lensfun_modifier_t));
dt_utf8_strlcpy(modifier->name, _("distortion & TCA"), sizeof(modifier->name));
g->modifiers = g_list_append(g->modifiers, modifier);
modifier->modflag = LENSFUN_MODFLAG_DIST_TCA;
modifier->pos = ++pos;
modifier = (dt_iop_lensfun_modifier_t *)g_malloc0(sizeof(dt_iop_lensfun_modifier_t));
dt_utf8_strlcpy(modifier->name, _("distortion & vignetting"), sizeof(modifier->name));
g->modifiers = g_list_append(g->modifiers, modifier);
modifier->modflag = LENSFUN_MODFLAG_DIST_VIGN;
modifier->pos = ++pos;
modifier = (dt_iop_lensfun_modifier_t *)g_malloc0(sizeof(dt_iop_lensfun_modifier_t));
dt_utf8_strlcpy(modifier->name, _("TCA & vignetting"), sizeof(modifier->name));
g->modifiers = g_list_append(g->modifiers, modifier);
modifier->modflag = LENSFUN_MODFLAG_TCA_VIGN;
modifier->pos = ++pos;
modifier = (dt_iop_lensfun_modifier_t *)g_malloc0(sizeof(dt_iop_lensfun_modifier_t));
dt_utf8_strlcpy(modifier->name, _("only distortion"), sizeof(modifier->name));
g->modifiers = g_list_append(g->modifiers, modifier);
modifier->modflag = LENSFUN_MODFLAG_DIST;
modifier->pos = ++pos;
modifier = (dt_iop_lensfun_modifier_t *)g_malloc0(sizeof(dt_iop_lensfun_modifier_t));
dt_utf8_strlcpy(modifier->name, _("only TCA"), sizeof(modifier->name));
g->modifiers = g_list_append(g->modifiers, modifier);
modifier->modflag = LENSFUN_MODFLAG_TCA;
modifier->pos = ++pos;
modifier = (dt_iop_lensfun_modifier_t *)g_malloc0(sizeof(dt_iop_lensfun_modifier_t));
dt_utf8_strlcpy(modifier->name, _("only vignetting"), sizeof(modifier->name));
g->modifiers = g_list_append(g->modifiers, modifier);
modifier->modflag = LENSFUN_MODFLAG_VIGN;
modifier->pos = ++pos;
GtkWidget *button;
self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_BAUHAUS_SPACE);
g_signal_connect(G_OBJECT(self->widget), "draw", G_CALLBACK(draw), self);
// camera selector
GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
g->camera_model = GTK_BUTTON(gtk_button_new_with_label(self->dev->image_storage.exif_model));
dt_gui_key_accel_block_on_focus_connect(GTK_WIDGET(g->camera_model));
gtk_label_set_ellipsize(GTK_LABEL(gtk_bin_get_child(GTK_BIN(g->camera_model))), PANGO_ELLIPSIZE_END);
g_signal_connect(G_OBJECT(g->camera_model), "clicked", G_CALLBACK(camera_menusearch_clicked), self);
gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(g->camera_model), TRUE, TRUE, 0);
button = dtgtk_button_new(dtgtk_cairo_paint_solid_triangle, CPF_STYLE_FLAT | CPF_DIRECTION_DOWN);
g->find_camera_button = button;
gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
g_object_set(G_OBJECT(button), "tooltip-text", _("find camera"), (char *)NULL);
g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(camera_autosearch_clicked), self);
gtk_box_pack_start(GTK_BOX(self->widget), hbox, TRUE, TRUE, 0);
// lens selector
hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
g->lens_model = GTK_BUTTON(gtk_button_new_with_label(self->dev->image_storage.exif_lens));
dt_gui_key_accel_block_on_focus_connect(GTK_WIDGET(g->lens_model));
gtk_label_set_ellipsize(GTK_LABEL(gtk_bin_get_child(GTK_BIN(g->lens_model))), PANGO_ELLIPSIZE_END);
g_signal_connect(G_OBJECT(g->lens_model), "clicked", G_CALLBACK(lens_menusearch_clicked), self);
gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(g->lens_model), TRUE, TRUE, 0);
button = dtgtk_button_new(dtgtk_cairo_paint_solid_triangle, CPF_STYLE_FLAT | CPF_DIRECTION_DOWN);
g->find_lens_button = GTK_WIDGET(button);
gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
g_object_set(G_OBJECT(button), "tooltip-text", _("find lens"), (char *)NULL);
g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(lens_autosearch_clicked), self);
gtk_box_pack_start(GTK_BOX(self->widget), hbox, TRUE, TRUE, 0);
// lens properties
g->lens_param_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_pack_start(GTK_BOX(self->widget), g->lens_param_box, TRUE, TRUE, 0);
// camera/lens not detected warning box
g->detection_warning = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_pack_start(GTK_BOX(self->widget), g->detection_warning, TRUE, TRUE, 0);
#if 0
// if unambiguous info is there, use it.
if(self->dev->image_storage.exif_lens[0] != '\0')
{
char make [200], model [200];
const gchar *txt = gtk_entry_get_text(GTK_ENTRY(g->lens_model));
parse_maker_model (txt, make, sizeof (make), model, sizeof (model));
dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
const lfLens **lenslist = lf_db_find_lenses_hd (dt_iop_lensfun_db, g->camera,
make [0] ? make : NULL,
model [0] ? model : NULL, LF_SEARCH_SORT_AND_UNIQUIFY);
if(lenslist) lens_set (self, lenslist[0]);
lf_free (lenslist);
dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
}
#endif
// selector for correction type (modflags): one or more out of distortion, TCA, vignetting
g->modflags = dt_bauhaus_combobox_new(self);
dt_bauhaus_widget_set_label(g->modflags, NULL, _("corrections"));
gtk_box_pack_start(GTK_BOX(self->widget), g->modflags, TRUE, TRUE, 0);
g_object_set(G_OBJECT(g->modflags), "tooltip-text", _("which corrections to apply"), (char *)NULL);
GList *l = g->modifiers;
while(l)
{
dt_iop_lensfun_modifier_t *modifier = (dt_iop_lensfun_modifier_t *)l->data;
dt_bauhaus_combobox_add(g->modflags, modifier->name);
l = g_list_next(l);
}
dt_bauhaus_combobox_set(g->modflags, 0);
g_signal_connect(G_OBJECT(g->modflags), "value-changed", G_CALLBACK(modflags_changed), (gpointer)self);
// target geometry
g->target_geom = dt_bauhaus_combobox_new(self);
dt_bauhaus_widget_set_label(g->target_geom, NULL, _("geometry"));
gtk_box_pack_start(GTK_BOX(self->widget), g->target_geom, TRUE, TRUE, 0);
g_object_set(G_OBJECT(g->target_geom), "tooltip-text", _("target geometry"), (char *)NULL);
dt_bauhaus_combobox_add(g->target_geom, _("rectilinear"));
dt_bauhaus_combobox_add(g->target_geom, _("fish-eye"));
dt_bauhaus_combobox_add(g->target_geom, _("panoramic"));
dt_bauhaus_combobox_add(g->target_geom, _("equirectangular"));
#if LF_VERSION >= ((0 << 24) | (2 << 16) | (6 << 8) | 0)
dt_bauhaus_combobox_add(g->target_geom, _("orthographic"));
dt_bauhaus_combobox_add(g->target_geom, _("stereographic"));
dt_bauhaus_combobox_add(g->target_geom, _("equisolid angle"));
dt_bauhaus_combobox_add(g->target_geom, _("thoby fish-eye"));
#endif
g_signal_connect(G_OBJECT(g->target_geom), "value-changed", G_CALLBACK(target_geometry_changed),
(gpointer)self);
// scale
g->scale = dt_bauhaus_slider_new_with_range(self, 0.1, 2.0, 0.005, p->scale, 3);
g_object_set(G_OBJECT(g->scale), "tooltip-text", _("auto scale"), (char *)NULL);
dt_bauhaus_widget_set_label(g->scale, NULL, _("scale"));
g_signal_connect(G_OBJECT(g->scale), "value-changed", G_CALLBACK(scale_changed), self);
g_signal_connect(G_OBJECT(g->scale), "quad-pressed", G_CALLBACK(autoscale_pressed), self);
gtk_box_pack_start(GTK_BOX(self->widget), g->scale, TRUE, TRUE, 0);
dt_bauhaus_widget_set_quad_paint(g->scale, dtgtk_cairo_paint_refresh, 0);
// reverse direction
g->reverse = dt_bauhaus_combobox_new(self);
dt_bauhaus_widget_set_label(g->reverse, NULL, _("mode"));
gtk_box_pack_start(GTK_BOX(self->widget), g->reverse, TRUE, TRUE, 0);
g_object_set(G_OBJECT(g->reverse), "tooltip-text", _("correct distortions or apply them"), (char *)NULL);
dt_bauhaus_combobox_add(g->reverse, _("correct"));
dt_bauhaus_combobox_add(g->reverse, _("distort"));
g_signal_connect(G_OBJECT(g->reverse), "value-changed", G_CALLBACK(reverse_toggled), (gpointer)self);
// override linear tca (if not 1.0):
g->tca_r = dt_bauhaus_slider_new_with_range(self, 0.99, 1.01, 0.0001, p->tca_r, 5);
g_object_set(G_OBJECT(g->tca_r), "tooltip-text", _("Transversal Chromatic Aberration red"), (char *)NULL);
dt_bauhaus_widget_set_label(g->tca_r, NULL, _("TCA red"));
g_signal_connect(G_OBJECT(g->tca_r), "value-changed", G_CALLBACK(tca_changed), self);
gtk_box_pack_start(GTK_BOX(self->widget), g->tca_r, TRUE, TRUE, 0);
g->tca_b = dt_bauhaus_slider_new_with_range(self, 0.99, 1.01, 0.0001, p->tca_b, 5);
g_object_set(G_OBJECT(g->tca_b), "tooltip-text", _("Transversal Chromatic Aberration blue"), (char *)NULL);
dt_bauhaus_widget_set_label(g->tca_b, NULL, _("TCA blue"));
g_signal_connect(G_OBJECT(g->tca_b), "value-changed", G_CALLBACK(tca_changed), self);
gtk_box_pack_start(GTK_BOX(self->widget), g->tca_b, TRUE, TRUE, 0);
// message box to inform user what corrections have been done. this is useful as depending on lensfuns
// profile only some of the lens flaws can be corrected
GtkBox *hbox1 = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0));
GtkLabel *label = GTK_LABEL(gtk_label_new(_("corrections done: ")));
g_object_set(G_OBJECT(label), "tooltip-text", _("which corrections have actually been done"), (char *)NULL);
gtk_box_pack_start(GTK_BOX(hbox1), GTK_WIDGET(label), FALSE, FALSE, 0);
g->message = GTK_LABEL(gtk_label_new("")); // This gets filled in by process
gtk_box_pack_start(GTK_BOX(hbox1), GTK_WIDGET(g->message), FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(hbox1), TRUE, TRUE, 0);
}
void gui_update(struct dt_iop_module_t *self)
{
// let gui elements reflect params
dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
dt_iop_lensfun_params_t *p = (dt_iop_lensfun_params_t *)self->params;
if(p->modified == 0)
{
/*
* user did not modify anything in gui after autodetection - let's
* use current default_params as params - for presets and mass-export
*/
memcpy(self->params, self->default_params, sizeof(dt_iop_lensfun_params_t));
}
dt_iop_lensfun_global_data_t *gd = (dt_iop_lensfun_global_data_t *)self->data;
lfDatabase *dt_iop_lensfun_db = (lfDatabase *)gd->db;
// these are the wrong (untranslated) strings in general but that's ok, they will be overwritten further
// down
gtk_button_set_label(g->camera_model, p->camera);
gtk_button_set_label(g->lens_model, p->lens);
gtk_label_set_ellipsize(GTK_LABEL(gtk_bin_get_child(GTK_BIN(g->camera_model))), PANGO_ELLIPSIZE_END);
gtk_label_set_ellipsize(GTK_LABEL(gtk_bin_get_child(GTK_BIN(g->lens_model))), PANGO_ELLIPSIZE_END);
g_object_set(G_OBJECT(g->camera_model), "tooltip-text", "", (char *)NULL);
g_object_set(G_OBJECT(g->lens_model), "tooltip-text", "", (char *)NULL);
dt_pthread_mutex_lock(&g->lock);
g->corrections_done = -1;
dt_pthread_mutex_unlock(&g->lock);
gtk_label_set_text(g->message, "");
int modflag = p->modify_flags & LENSFUN_MODFLAG_MASK;
GList *modifiers = g->modifiers;
while(modifiers)
{
// could use g_list_nth. this seems safer?
dt_iop_lensfun_modifier_t *mm = (dt_iop_lensfun_modifier_t *)modifiers->data;
if(mm->modflag == modflag)
{
dt_bauhaus_combobox_set(g->modflags, mm->pos);
break;
}
modifiers = g_list_next(modifiers);
}
dt_bauhaus_combobox_set(g->target_geom, p->target_geom - LF_UNKNOWN - 1);
dt_bauhaus_combobox_set(g->reverse, p->inverse);
dt_bauhaus_slider_set(g->tca_r, p->tca_r);
dt_bauhaus_slider_set(g->tca_b, p->tca_b);
dt_bauhaus_slider_set(g->scale, p->scale);
const lfCamera **cam = NULL;
g->camera = NULL;
if(p->camera[0])
{
dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
cam = lf_db_find_cameras_ext(dt_iop_lensfun_db, NULL, p->camera, 0);
dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
if(cam)
camera_set(self, cam[0]);
else
camera_set(self, NULL);
}
if(g->camera && p->lens[0])
{
char make[200], model[200];
parse_maker_model(p->lens, make, sizeof(make), model, sizeof(model));
dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
const lfLens **lenslist = lf_db_find_lenses_hd(dt_iop_lensfun_db, g->camera, make[0] ? make : NULL,
model[0] ? model : NULL, LF_SEARCH_SORT_AND_UNIQUIFY);
if(lenslist)
lens_set(self, lenslist[0]);
else
lens_set(self, NULL);
lf_free(lenslist);
dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
}
else
{
dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
lens_set(self, NULL);
dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
}
}
void gui_cleanup(struct dt_iop_module_t *self)
{
dt_iop_lensfun_gui_data_t *g = (dt_iop_lensfun_gui_data_t *)self->gui_data;
dt_gui_key_accel_block_on_focus_disconnect(GTK_WIDGET(g->lens_model));
dt_gui_key_accel_block_on_focus_disconnect(GTK_WIDGET(g->camera_model));
while(g->modifiers)
{
g_free(g->modifiers->data);
g->modifiers = g_list_delete_link(g->modifiers, g->modifiers);
}
dt_pthread_mutex_destroy(&g->lock);
free(self->gui_data);
self->gui_data = NULL;
}
// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
// vim: shiftwidth=2 expandtab tabstop=2 cindent
// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-space on;
![swh spinner](/static/img/swh-spinner.gif)
Computing file changes ...