78 #define THRESHOLD 2.3283064365386963e-10f
153 return _(
"ne_gadoctor");
158 return _(
"film|invert|negative|scan");
164 _(
"corrective and creative"),
165 _(
"linear, RGB, display-referred"),
166 _(
"non-linear, RGB"),
167 _(
"non-linear, RGB, display-referred"));
188 void *new_params,
const int new_version)
190 if(old_version == 1 && new_version == 2)
192 typedef struct dt_iop_negadoctor_params_v1_t
204 } dt_iop_negadoctor_params_v1_t;
206 dt_iop_negadoctor_params_v1_t *o = (dt_iop_negadoctor_params_v1_t *)old_params;
215 n->film_stock = o->film_stock;
216 n->Dmin[0] = o->Dmin[0];
217 n->Dmin[1] = o->Dmin[1];
218 n->Dmin[2] = o->Dmin[2];
219 n->Dmin[3] = o->Dmin[3];
220 n->wb_high[0] = o->wb_high[0];
221 n->wb_high[1] = o->wb_high[1];
222 n->wb_high[2] = o->wb_high[2];
223 n->wb_high[3] = o->wb_high[3];
224 n->wb_low[0] = o->wb_low[0];
225 n->wb_low[1] = o->wb_low[1];
226 n->wb_low[2] = o->wb_low[2];
227 n->wb_low[3] = o->wb_low[3];
229 n->offset = o->offset;
232 n->soft_clip = o->soft_clip;
233 n->exposure = o->exposure;
248 for(
size_t c = 0; c < 4; c++)
d->wb_high[c] =
p->wb_high[c] /
p->D_max;
250 for(
size_t c = 0; c < 4; c++)
d->offset[c] =
p->wb_high[c] *
p->offset *
p->wb_low[c];
254 for(
size_t c = 0; c < 4; c++)
d->Dmin[c] =
p->Dmin[c];
256 for(
size_t c = 0; c < 4; c++)
d->Dmin[c] =
p->Dmin[0];
259 d->black = -
p->exposure * (1.0f +
p->black);
262 d->soft_clip =
p->soft_clip;
263 d->soft_clip_comp = 1.0f -
p->soft_clip;
266 d->exposure =
p->exposure;
274 void *
const restrict
ovoid)
278 const float *
const restrict in = (
float *)ivoid;
279 float *
const restrict
out = (
float *)
ovoid;
281 for(
size_t k = 0;
k < (size_t)roi_out->height * roi_out->width * 4;
k += 4)
283 for(
size_t c = 0; c < 4; c++)
286 const float *
const restrict pix_in = in +
k;
287 float *
const restrict pix_out =
out +
k;
288 const float *
const restrict Dmin = __builtin_assume_aligned(
d->Dmin, 16);
289 const float *
const restrict wb_high = __builtin_assume_aligned(
d->wb_high, 16);
290 const float *
const restrict offset = __builtin_assume_aligned(
d->offset, 16);
293 const float density = - log10f(Dmin[c] / fmaxf(pix_in[c],
THRESHOLD));
296 const float corrected_de = wb_high[c] * density + offset[c];
299 const float print_linear = -(
d->exposure *
fast_exp10f(corrected_de) +
d->black);
300 const float print_gamma = powf(fmaxf(print_linear, 0.0f),
d->gamma);
303 pix_out[c] = (print_gamma >
d->soft_clip) ?
d->soft_clip + (1.0f -
fast_expf(-(print_gamma -
d->soft_clip) /
d->soft_clip_comp)) *
d->soft_clip_comp
309 dt_iop_alpha_copy(ivoid,
ovoid, roi_out->width, roi_out->height);
324 const int devid = pipe->
devid;
325 const int width = roi_in->width;
326 const int height = roi_in->height;
344 if(err != CL_SUCCESS)
goto error;
368 .Dmin = { 1.13f, 0.49f, 0.27f, 0.0f},
369 .wb_high = { 1.0f, 1.0f, 1.0f, 0.0f },
370 .wb_low = { 1.0f, 1.0f, 1.0f, 0.0f },
380 self->version(), &tmp,
sizeof(tmp), 1);
383 .Dmin = { 1.0f, 1.0f, 1.0f, 0.0f},
384 .wb_high = { 1.0f, 1.0f, 1.0f, 0.0f },
385 .wb_low = { 1.0f, 1.0f, 1.0f, 0.0f },
395 self->version(), &tmq,
sizeof(tmq), 1);
405 const int program = 30;
433 gtk_widget_set_visible(
g->Dmin_G,
state);
434 gtk_widget_set_visible(
g->Dmin_B,
state);
458 fprintf(stderr,
"negadoctor film stock: undefined behaviour\n");
482 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(
g->Dmin_picker), &
color);
494 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget), &c);
496 p->Dmin[1] = c.green;
519 for(
size_t c = 0; c < 3; ++c) WB_low_invert[c] = 2.0f -
p->wb_low[c];
520 const float WB_low_max =
v_maxf(WB_low_invert);
521 for(
size_t c = 0; c < 3; ++c) WB_low_invert[c] /= WB_low_max;
523 color.red = WB_low_invert[0];
524 color.green = WB_low_invert[1];
525 color.blue = WB_low_invert[2];
527 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(
g->WB_low_picker), &
color);
539 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget), &c);
544 for(
size_t k = 0;
k < 3;
k++)
p->wb_low[
k] =
RGB[
k] / RGB_min;
567 for(
size_t c = 0; c < 3; ++c) WB_high_invert[c] = 2.0f -
p->wb_high[c];
568 const float WB_high_max =
v_maxf(WB_high_invert);
569 for(
size_t c = 0; c < 3; ++c) WB_high_invert[c] /= WB_high_max;
571 color.red = WB_high_invert[0];
572 color.green = WB_high_invert[1];
573 color.blue = WB_high_invert[2];
575 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(
g->WB_high_picker), &
color);
587 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget), &c);
591 for(
size_t k = 0;
k < 3;
k++)
p->wb_high[
k] =
RGB[
k] / RGB_min;
612 const float WB_low_max =
v_maxf(
p->wb_low);
613 for(
size_t c = 0; c < 3; ++c)
614 p->wb_low[c] /= WB_low_max;
635 const float WB_high_min =
v_minf(
p->wb_high);
636 for(
size_t c = 0; c < 3; ++c)
637 p->wb_high[c] /= WB_high_min;
681 for(
int c = 0; c < 3; c++)
705 for(
int c = 0; c < 3; c++)
728 for(
int c = 0; c < 3; c++)
731 const float RGB_v_min =
v_minf(RGB_min);
732 for(
int c = 0; c < 3; c++)
p->wb_low[c] = RGB_v_min / RGB_min[c];
754 for(
int c = 0; c < 3; c++)
755 RGB_min[c] = fabsf(-1.0f / (
p->offset *
p->wb_low[c] - log10f(
p->Dmin[c] / fmaxf(self->
picked_color[c],
THRESHOLD)) /
p->D_max));
757 const float RGB_v_min =
v_minf(RGB_min);
758 for(
int c = 0; c < 3; c++)
p->wb_high[c] = RGB_min[c] / RGB_v_min;
780 for(
int c = 0; c < 3; c++)
783 RGB[c] *=
p->wb_high[c] /
p->D_max;
784 RGB[c] +=
p->wb_low[c] *
p->offset *
p->wb_high[c];
806 for(
int c = 0; c < 3; c++)
809 RGB[c] *=
p->wb_high[c] /
p->D_max;
810 RGB[c] +=
p->wb_low[c] *
p->offset;
831 if (picker ==
g->Dmin_sampler)
833 else if(picker ==
g->WB_high_sampler)
835 else if(picker ==
g->offset)
837 else if(picker ==
g->D_max)
839 else if(picker ==
g->WB_low_sampler)
841 else if(picker ==
g->exposure)
843 else if(picker ==
g->black)
846 fprintf(stderr,
"[negadoctor] unknown color picker\n");
868 g->Dmin_picker = gtk_color_button_new();
869 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(
g->Dmin_picker),
FALSE);
870 gtk_color_button_set_title(GTK_COLOR_BUTTON(
g->Dmin_picker), _(
"select color of film material from a swatch"));
871 gtk_box_pack_start(GTK_BOX(
row1), GTK_WIDGET(
g->Dmin_picker),
TRUE,
TRUE, 0);
875 gtk_widget_set_tooltip_text(
g->Dmin_sampler , _(
"pick color of film material from image"));
877 gtk_box_pack_start(GTK_BOX(page1), GTK_WIDGET(
row1),
FALSE,
FALSE, 0);
884 gtk_widget_set_tooltip_text(
g->Dmin_R, _(
"adjust the color and shade of the film transparent base.\n"
885 "this value depends on the film material, \n"
886 "the chemical fog produced while developing the film,\n"
887 "and the scanner white balance."));
894 gtk_widget_set_tooltip_text(
g->Dmin_G, _(
"adjust the color and shade of the film transparent base.\n"
895 "this value depends on the film material, \n"
896 "the chemical fog produced while developing the film,\n"
897 "and the scanner white balance."));
904 gtk_widget_set_tooltip_text(
g->Dmin_B, _(
"adjust the color and shade of the film transparent base.\n"
905 "this value depends on the film material, \n"
906 "the chemical fog produced while developing the film,\n"
907 "and the scanner white balance."));
915 gtk_widget_set_tooltip_text(
g->D_max, _(
"maximum density of the film, corresponding to white after inversion.\n"
916 "this value depends on the film specifications, the developing process,\n"
917 "the dynamic range of the scene and the scanner exposure settings."));
923 gtk_widget_set_tooltip_text(
g->offset, _(
"correct the exposure of the scanner, for all RGB channels,\n"
924 "before the inversion, so blacks are neither clipped or too pale."));
934 g->WB_low_picker = gtk_color_button_new();
935 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(
g->WB_low_picker),
FALSE);
936 gtk_color_button_set_title(GTK_COLOR_BUTTON(
g->WB_low_picker), _(
"select color of shadows from a swatch"));
937 gtk_box_pack_start(GTK_BOX(row3), GTK_WIDGET(
g->WB_low_picker),
TRUE,
TRUE, 0);
941 gtk_box_pack_start(GTK_BOX(row3), GTK_WIDGET(
g->WB_low_norm),
FALSE,
FALSE, 0);
944 gtk_widget_set_tooltip_text(
g->WB_low_sampler, _(
"pick shadows color from image"));
946 gtk_box_pack_start(GTK_BOX(page2), GTK_WIDGET(row3),
FALSE,
FALSE, 0);
950 gtk_widget_set_tooltip_text(
g->wb_low_R, _(
"correct the color cast in shadows so blacks are\n"
951 "truly achromatic. Setting this value before\n"
952 "the highlights illuminant white balance will help\n"
953 "recovering the global white balance in difficult cases."));
957 gtk_widget_set_tooltip_text(
g->wb_low_G, _(
"correct the color cast in shadows so blacks are\n"
958 "truly achromatic. Setting this value before\n"
959 "the highlights illuminant white balance will help\n"
960 "recovering the global white balance in difficult cases."));
964 gtk_widget_set_tooltip_text(
g->wb_low_B, _(
"correct the color cast in shadows so blacks are\n"
965 "truly achromatic. Setting this value before\n"
966 "the highlights illuminant white balance will help\n"
967 "recovering the global white balance in difficult cases."));
974 g->WB_high_picker = gtk_color_button_new();
975 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(
g->WB_high_picker),
FALSE);
976 gtk_color_button_set_title(GTK_COLOR_BUTTON(
g->WB_high_picker), _(
"select color of illuminant from a swatch"));
977 gtk_box_pack_start(GTK_BOX(
row2), GTK_WIDGET(
g->WB_high_picker),
TRUE,
TRUE, 0);
981 gtk_box_pack_start(GTK_BOX(
row2), GTK_WIDGET(
g->WB_high_norm),
FALSE,
FALSE, 0);
984 gtk_widget_set_tooltip_text(
g->WB_high_sampler , _(
"pick illuminant color from image"));
986 gtk_box_pack_start(GTK_BOX(page2), GTK_WIDGET(
row2),
FALSE,
FALSE, 0);
990 gtk_widget_set_tooltip_text(
g->wb_high_R, _(
"correct the color of the illuminant so whites are\n"
991 "truly achromatic. Setting this value after\n"
992 "the shadows color cast will help\n"
993 "recovering the global white balance in difficult cases."));
997 gtk_widget_set_tooltip_text(
g->wb_high_G, _(
"correct the color of the illuminant so whites are\n"
998 "truly achromatic. Setting this value after\n"
999 "the shadows color cast will help\n"
1000 "recovering the global white balance in difficult cases."));
1004 gtk_widget_set_tooltip_text(
g->wb_high_B, _(
"correct the color of the illuminant so whites are\n"
1005 "truly achromatic. Setting this value after\n"
1006 "the shadows color cast will help\n"
1007 "recovering the global white balance in difficult cases."));
1019 gtk_widget_set_tooltip_text(
g->black, _(
"correct the density of black after the inversion,\n"
1020 "to adjust the global contrast while avoiding clipping shadows."));
1024 gtk_widget_set_tooltip_text(
g->gamma, _(
"select the grade of the virtual paper, which is actually\n"
1025 "equivalent to applying a gamma. it compensates the film D max\n"
1026 "and recovers the contrast. use a high grade for high D max."));
1032 gtk_widget_set_tooltip_text(
g->soft_clip, _(
"gradually compress specular highlights past this value\n"
1033 "to avoid clipping while pushing the exposure for mid-tones.\n"
1034 "this somewhat reproduces the behaviour of matte paper."));
1043 gtk_widget_set_tooltip_text(
g->exposure, _(
"correct the printing exposure after inversion to adjust\n"
1044 "the global contrast and avoid clipping highlights."));
1051 gtk_widget_set_tooltip_text(
g->film_stock, _(
"toggle on or off the color controls"));
1054 const int active_page =
dt_conf_get_int(
"plugins/darkroom/negadoctor/gui_page");
1055 gtk_widget_show(gtk_notebook_get_nth_page(
g->notebook, active_page));
1056 gtk_notebook_set_current_page(
g->notebook, active_page);
1064 dt_conf_set_int(
"plugins/darkroom/negadoctor/gui_page", gtk_notebook_get_current_page(
g->notebook));
1082 else if(w ==
g->Dmin_R || w ==
g->Dmin_G || w ==
g->Dmin_B)
1086 else if(w ==
g->exposure)
1088 p->exposure = powf(2.0f,
p->exposure);
1091 if(
IS_NULL_PTR(w) || w ==
g->wb_high_R || w ==
g->wb_high_G || w ==
g->wb_high_B)
1096 if(
IS_NULL_PTR(w) || w ==
g->wb_low_R || w ==
g->wb_low_G || w ==
g->wb_low_B)
static void error(char *msg)
void dt_bauhaus_slider_set_digits(GtkWidget *widget, int val)
void dt_bauhaus_slider_set_default(GtkWidget *widget, float def)
void dt_bauhaus_slider_set_soft_max(GtkWidget *widget, float val)
void dt_bauhaus_slider_set_soft_min(GtkWidget *widget, float val)
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
void dt_bauhaus_slider_set_factor(GtkWidget *widget, float factor)
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
void dt_iop_color_picker_reset(dt_iop_module_t *module, gboolean keep)
GtkWidget * dt_color_picker_new(dt_iop_module_t *module, dt_iop_color_picker_kind_t kind, GtkWidget *w)
static float soft_clip(const float x, const float soft_threshold, const float hard_threshold)
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
const dt_colormatrix_t dt_aligned_pixel_t out
static dt_aligned_pixel_t RGB
void dt_conf_set_int(const char *name, int val)
int dt_conf_get_int(const char *name)
Integer for name, clamped to the bounds declared in the XML.
void dt_control_queue_redraw_widget(GtkWidget *widget)
threadsafe request of redraw of specific widget. Use this function if you need to redraw a specific w...
#define dt_dev_add_history_item(dev, module, enable, redraw)
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
void dt_gui_presets_add_generic(const char *name, dt_dev_operation_t op, const int32_t version, const void *params, const int32_t params_size, const int32_t enabled)
void dt_iop_default_init(dt_iop_module_t *module)
const char ** dt_iop_set_description(dt_iop_module_t *module, const char *main_text, const char *purpose, const char *input, const char *process, const char *output)
@ IOP_FLAGS_INCLUDE_IN_STYLES
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
GtkWidget * dt_bauhaus_combobox_from_params(dt_iop_module_t *self, const char *param)
static dt_iop_gui_data_t * dt_iop_gui_data(const struct dt_iop_module_t *m)
The module's GUI data blob, NULL-safe for headless callers: IOP process() implementations read it for...
#define IOP_GUI_ALLOC(module)
GtkWidget * dt_ui_section_label_new(const gchar *str)
GtkWidget * dt_action_button_new(dt_lib_module_t *self, const gchar *label, gpointer callback, gpointer data, const gchar *tooltip, guint accel_key, GdkModifierType mods)
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
float *const restrict const size_t k
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
static void * dt_calloc_align(size_t size)
dt_alloc_align() followed by a zero fill.
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
DT_MODULE() for a module whose params struct is introspected.
void init(dt_iop_module_t *module)
const char ** description(struct dt_iop_module_t *self)
static void WB_high_picker_update(dt_iop_module_t *self)
void gui_update(dt_iop_module_t *const self)
Refresh GUI controls from current params and configuration.
void gui_reset(dt_iop_module_t *self)
static void WB_low_picker_callback(GtkColorButton *widget, dt_iop_module_t *self)
static void Wb_high_norm_callback(GtkColorButton *widget, dt_iop_module_t *self)
static void toggle_stock_controls(dt_iop_module_t *const self)
static void WB_high_picker_callback(GtkColorButton *widget, dt_iop_module_t *self)
__DT_CLONE_TARGETS__ int process(struct dt_iop_module_t *const self, const dt_dev_pixelpipe_t *const pipe, const dt_dev_pixelpipe_iop_t *const piece, const void *const restrict ivoid, void *const restrict ovoid)
static void apply_auto_WB_high(dt_iop_module_t *self)
dt_iop_negadoctor_filmstock_t
static void apply_auto_WB_low(dt_iop_module_t *self)
int process_cl(struct dt_iop_module_t *const self, const dt_dev_pixelpipe_t *const pipe, const dt_dev_pixelpipe_iop_t *const piece, cl_mem dev_in, cl_mem dev_out)
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
void gui_init(dt_iop_module_t *self)
static void setup_color_variables(dt_iop_negadoctor_gui_data_t *const g, const gint state)
void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
void commit_params(dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
void cleanup_global(dt_iop_module_so_t *module)
static void apply_auto_exposure(dt_iop_module_t *self)
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
static void apply_auto_Dmin(dt_iop_module_t *self)
void gui_cleanup(struct dt_iop_module_t *self)
static void Dmin_picker_update(dt_iop_module_t *self)
void init_presets(dt_iop_module_so_t *self)
static void WB_low_picker_update(dt_iop_module_t *self)
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static void Wb_low_norm_callback(GtkColorButton *widget, dt_iop_module_t *self)
static void apply_auto_offset(dt_iop_module_t *self)
static void apply_auto_black(dt_iop_module_t *self)
void init_global(dt_iop_module_so_t *module)
static void apply_auto_Dmax(dt_iop_module_t *self)
void color_picker_apply(dt_iop_module_t *self, GtkWidget *picker, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params, const int new_version)
static void Dmin_picker_callback(GtkColorButton *widget, dt_iop_module_t *self)
GtkWidget * dt_ui_notebook_page(GtkNotebook *notebook, const char *text, const char *tooltip)
GtkNotebook * dt_ui_notebook_new()
void dt_ui_notebook_set_picker_owner(GtkNotebook *notebook, gpointer owner)
Register an opaque owner for a GtkNotebook's page switches, and report every "switch_page" to the hos...
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
int dt_opencl_create_kernel(const int prog, const char *name)
void dt_opencl_free_kernel(const int kernel)
int dt_opencl_set_kernel_arg(const int dev, const int kernel, const int num, const size_t size, const void *arg)
#define __OMP_FOR_SIMD__(...)
static float v_maxf(const float vector[3])
static float fast_exp10f(const float x)
static float fast_expf(const float x)
static float v_minf(const float vector[3])
static const dt_aligned_pixel_simd_t const dt_aligned_pixel_simd_t row1
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
static const dt_aligned_pixel_simd_t const dt_aligned_pixel_simd_t const dt_aligned_pixel_simd_t row2
const float uint32_t state[4]
struct dt_iop_module_t *void * data
dt_iop_global_data_t * data
dt_iop_params_t * default_params
struct dt_iop_module_gui_t * gui
struct dt_develop_t * dev
dt_iop_global_data_t * global_data
dt_aligned_pixel_t picked_color_min
dt_aligned_pixel_t picked_color_max
dt_aligned_pixel_t picked_color
dt_aligned_pixel_t wb_high
dt_aligned_pixel_t offset
GtkWidget * WB_high_sampler
GtkWidget * WB_low_picker
GtkWidget * WB_high_picker
GtkWidget * WB_low_sampler
dt_iop_negadoctor_filmstock_t film_stock
Region of interest passed through the pixelpipe.
#define __DT_CLONE_TARGETS__