72 #define THRESHOLD 2.3283064365386963e-10f
147 return _(
"ne_gadoctor");
152 return _(
"film|invert|negative|scan");
158 _(
"corrective and creative"),
159 _(
"linear, RGB, display-referred"),
160 _(
"non-linear, RGB"),
161 _(
"non-linear, RGB, display-referred"));
182 void *new_params,
const int new_version)
184 if(old_version == 1 && new_version == 2)
186 typedef struct dt_iop_negadoctor_params_v1_t
198 } dt_iop_negadoctor_params_v1_t;
200 dt_iop_negadoctor_params_v1_t *o = (dt_iop_negadoctor_params_v1_t *)old_params;
209 n->film_stock = o->film_stock;
210 n->Dmin[0] = o->Dmin[0];
211 n->Dmin[1] = o->Dmin[1];
212 n->Dmin[2] = o->Dmin[2];
213 n->Dmin[3] = o->Dmin[3];
214 n->wb_high[0] = o->wb_high[0];
215 n->wb_high[1] = o->wb_high[1];
216 n->wb_high[2] = o->wb_high[2];
217 n->wb_high[3] = o->wb_high[3];
218 n->wb_low[0] = o->wb_low[0];
219 n->wb_low[1] = o->wb_low[1];
220 n->wb_low[2] = o->wb_low[2];
221 n->wb_low[3] = o->wb_low[3];
223 n->offset = o->offset;
226 n->soft_clip = o->soft_clip;
227 n->exposure = o->exposure;
242 for(
size_t c = 0; c < 4; c++)
d->wb_high[c] =
p->wb_high[c] /
p->D_max;
244 for(
size_t c = 0; c < 4; c++)
d->offset[c] =
p->wb_high[c] *
p->offset *
p->wb_low[c];
248 for(
size_t c = 0; c < 4; c++)
d->Dmin[c] =
p->Dmin[c];
250 for(
size_t c = 0; c < 4; c++)
d->Dmin[c] =
p->Dmin[0];
253 d->black = -
p->exposure * (1.0f +
p->black);
256 d->soft_clip =
p->soft_clip;
257 d->soft_clip_comp = 1.0f -
p->soft_clip;
260 d->exposure =
p->exposure;
268 void *
const restrict
ovoid)
272 const float *
const restrict in = (
float *)ivoid;
273 float *
const restrict
out = (
float *)
ovoid;
275 for(
size_t k = 0;
k < (size_t)roi_out->height * roi_out->width * 4;
k += 4)
277 for(
size_t c = 0; c < 4; c++)
280 const float *
const restrict pix_in = in +
k;
281 float *
const restrict pix_out =
out +
k;
282 const float *
const restrict Dmin = __builtin_assume_aligned(
d->Dmin, 16);
283 const float *
const restrict wb_high = __builtin_assume_aligned(
d->wb_high, 16);
284 const float *
const restrict offset = __builtin_assume_aligned(
d->offset, 16);
287 const float density = - log10f(Dmin[c] / fmaxf(pix_in[c],
THRESHOLD));
290 const float corrected_de = wb_high[c] * density + offset[c];
293 const float print_linear = -(
d->exposure *
fast_exp10f(corrected_de) +
d->black);
294 const float print_gamma = powf(fmaxf(print_linear, 0.0f),
d->gamma);
297 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
303 dt_iop_alpha_copy(ivoid,
ovoid, roi_out->width, roi_out->height);
318 const int devid = pipe->
devid;
319 const int width = roi_in->width;
320 const int height = roi_in->height;
338 if(err != CL_SUCCESS)
goto error;
362 .Dmin = { 1.13f, 0.49f, 0.27f, 0.0f},
363 .wb_high = { 1.0f, 1.0f, 1.0f, 0.0f },
364 .wb_low = { 1.0f, 1.0f, 1.0f, 0.0f },
377 .Dmin = { 1.0f, 1.0f, 1.0f, 0.0f},
378 .wb_high = { 1.0f, 1.0f, 1.0f, 0.0f },
379 .wb_low = { 1.0f, 1.0f, 1.0f, 0.0f },
399 const int program = 30;
427 gtk_widget_set_visible(
g->Dmin_G,
state);
428 gtk_widget_set_visible(
g->Dmin_B,
state);
452 fprintf(stderr,
"negadoctor film stock: undefined behaviour\n");
467 color.red =
p->Dmin[0];
468 color.green =
p->Dmin[1];
469 color.blue =
p->Dmin[2];
473 color.red = color.green = color.blue =
p->Dmin[0];
476 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(
g->Dmin_picker), &color);
488 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget), &c);
490 p->Dmin[1] = c.green;
513 for(
size_t c = 0; c < 3; ++c) WB_low_invert[c] = 2.0f -
p->wb_low[c];
514 const float WB_low_max =
v_maxf(WB_low_invert);
515 for(
size_t c = 0; c < 3; ++c) WB_low_invert[c] /= WB_low_max;
517 color.red = WB_low_invert[0];
518 color.green = WB_low_invert[1];
519 color.blue = WB_low_invert[2];
521 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(
g->WB_low_picker), &color);
533 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget), &c);
538 for(
size_t k = 0;
k < 3;
k++)
p->wb_low[
k] =
RGB[
k] / RGB_min;
561 for(
size_t c = 0; c < 3; ++c) WB_high_invert[c] = 2.0f -
p->wb_high[c];
562 const float WB_high_max =
v_maxf(WB_high_invert);
563 for(
size_t c = 0; c < 3; ++c) WB_high_invert[c] /= WB_high_max;
565 color.red = WB_high_invert[0];
566 color.green = WB_high_invert[1];
567 color.blue = WB_high_invert[2];
569 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(
g->WB_high_picker), &color);
581 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget), &c);
585 for(
size_t k = 0;
k < 3;
k++)
p->wb_high[
k] =
RGB[
k] / RGB_min;
606 const float WB_low_max =
v_maxf(
p->wb_low);
607 for(
size_t c = 0; c < 3; ++c)
608 p->wb_low[c] /= WB_low_max;
629 const float WB_high_min =
v_minf(
p->wb_high);
630 for(
size_t c = 0; c < 3; ++c)
631 p->wb_high[c] /= WB_high_min;
675 for(
int c = 0; c < 3; c++)
699 for(
int c = 0; c < 3; c++)
722 for(
int c = 0; c < 3; c++)
725 const float RGB_v_min =
v_minf(RGB_min);
726 for(
int c = 0; c < 3; c++)
p->wb_low[c] = RGB_v_min / RGB_min[c];
748 for(
int c = 0; c < 3; c++)
749 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));
751 const float RGB_v_min =
v_minf(RGB_min);
752 for(
int c = 0; c < 3; c++)
p->wb_high[c] = RGB_min[c] / RGB_v_min;
774 for(
int c = 0; c < 3; c++)
777 RGB[c] *=
p->wb_high[c] /
p->D_max;
778 RGB[c] +=
p->wb_low[c] *
p->offset *
p->wb_high[c];
800 for(
int c = 0; c < 3; c++)
803 RGB[c] *=
p->wb_high[c] /
p->D_max;
804 RGB[c] +=
p->wb_low[c] *
p->offset;
825 if (picker ==
g->Dmin_sampler)
827 else if(picker ==
g->WB_high_sampler)
829 else if(picker ==
g->offset)
831 else if(picker ==
g->D_max)
833 else if(picker ==
g->WB_low_sampler)
835 else if(picker ==
g->exposure)
837 else if(picker ==
g->black)
840 fprintf(stderr,
"[negadoctor] unknown color picker\n");
858 g->Dmin_picker = gtk_color_button_new();
859 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(
g->Dmin_picker),
FALSE);
860 gtk_color_button_set_title(GTK_COLOR_BUTTON(
g->Dmin_picker), _(
"select color of film material from a swatch"));
861 gtk_box_pack_start(GTK_BOX(
row1), GTK_WIDGET(
g->Dmin_picker),
TRUE,
TRUE, 0);
865 gtk_widget_set_tooltip_text(
g->Dmin_sampler , _(
"pick color of film material from image"));
867 gtk_box_pack_start(GTK_BOX(page1), GTK_WIDGET(
row1),
FALSE,
FALSE, 0);
874 gtk_widget_set_tooltip_text(
g->Dmin_R, _(
"adjust the color and shade of the film transparent base.\n"
875 "this value depends on the film material, \n"
876 "the chemical fog produced while developing the film,\n"
877 "and the scanner white balance."));
884 gtk_widget_set_tooltip_text(
g->Dmin_G, _(
"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_B, _(
"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."));
905 gtk_widget_set_tooltip_text(
g->D_max, _(
"maximum density of the film, corresponding to white after inversion.\n"
906 "this value depends on the film specifications, the developing process,\n"
907 "the dynamic range of the scene and the scanner exposure settings."));
913 gtk_widget_set_tooltip_text(
g->offset, _(
"correct the exposure of the scanner, for all RGB channels,\n"
914 "before the inversion, so blacks are neither clipped or too pale."));
924 g->WB_low_picker = gtk_color_button_new();
925 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(
g->WB_low_picker),
FALSE);
926 gtk_color_button_set_title(GTK_COLOR_BUTTON(
g->WB_low_picker), _(
"select color of shadows from a swatch"));
927 gtk_box_pack_start(GTK_BOX(row3), GTK_WIDGET(
g->WB_low_picker),
TRUE,
TRUE, 0);
931 gtk_box_pack_start(GTK_BOX(row3), GTK_WIDGET(
g->WB_low_norm),
FALSE,
FALSE, 0);
934 gtk_widget_set_tooltip_text(
g->WB_low_sampler, _(
"pick shadows color from image"));
936 gtk_box_pack_start(GTK_BOX(page2), GTK_WIDGET(row3),
FALSE,
FALSE, 0);
940 gtk_widget_set_tooltip_text(
g->wb_low_R, _(
"correct the color cast in shadows so blacks are\n"
941 "truly achromatic. Setting this value before\n"
942 "the highlights illuminant white balance will help\n"
943 "recovering the global white balance in difficult cases."));
947 gtk_widget_set_tooltip_text(
g->wb_low_G, _(
"correct the color cast in shadows so blacks are\n"
948 "truly achromatic. Setting this value before\n"
949 "the highlights illuminant white balance will help\n"
950 "recovering the global white balance in difficult cases."));
954 gtk_widget_set_tooltip_text(
g->wb_low_B, _(
"correct the color cast in shadows so blacks are\n"
955 "truly achromatic. Setting this value before\n"
956 "the highlights illuminant white balance will help\n"
957 "recovering the global white balance in difficult cases."));
964 g->WB_high_picker = gtk_color_button_new();
965 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(
g->WB_high_picker),
FALSE);
966 gtk_color_button_set_title(GTK_COLOR_BUTTON(
g->WB_high_picker), _(
"select color of illuminant from a swatch"));
967 gtk_box_pack_start(GTK_BOX(
row2), GTK_WIDGET(
g->WB_high_picker),
TRUE,
TRUE, 0);
971 gtk_box_pack_start(GTK_BOX(
row2), GTK_WIDGET(
g->WB_high_norm),
FALSE,
FALSE, 0);
974 gtk_widget_set_tooltip_text(
g->WB_high_sampler , _(
"pick illuminant color from image"));
976 gtk_box_pack_start(GTK_BOX(page2), GTK_WIDGET(
row2),
FALSE,
FALSE, 0);
980 gtk_widget_set_tooltip_text(
g->wb_high_R, _(
"correct the color of the illuminant so whites are\n"
981 "truly achromatic. Setting this value after\n"
982 "the shadows color cast will help\n"
983 "recovering the global white balance in difficult cases."));
987 gtk_widget_set_tooltip_text(
g->wb_high_G, _(
"correct the color of the illuminant so whites are\n"
988 "truly achromatic. Setting this value after\n"
989 "the shadows color cast will help\n"
990 "recovering the global white balance in difficult cases."));
994 gtk_widget_set_tooltip_text(
g->wb_high_B, _(
"correct the color of the illuminant so whites are\n"
995 "truly achromatic. Setting this value after\n"
996 "the shadows color cast will help\n"
997 "recovering the global white balance in difficult cases."));
1009 gtk_widget_set_tooltip_text(
g->black, _(
"correct the density of black after the inversion,\n"
1010 "to adjust the global contrast while avoiding clipping shadows."));
1014 gtk_widget_set_tooltip_text(
g->gamma, _(
"select the grade of the virtual paper, which is actually\n"
1015 "equivalent to applying a gamma. it compensates the film D max\n"
1016 "and recovers the contrast. use a high grade for high D max."));
1022 gtk_widget_set_tooltip_text(
g->soft_clip, _(
"gradually compress specular highlights past this value\n"
1023 "to avoid clipping while pushing the exposure for mid-tones.\n"
1024 "this somewhat reproduces the behaviour of matte paper."));
1033 gtk_widget_set_tooltip_text(
g->exposure, _(
"correct the printing exposure after inversion to adjust\n"
1034 "the global contrast and avoid clipping highlights."));
1041 gtk_widget_set_tooltip_text(
g->film_stock, _(
"toggle on or off the color controls"));
1043 gtk_box_pack_start(GTK_BOX(self->
widget), GTK_WIDGET(
g->notebook),
FALSE,
FALSE, 0);
1044 const int active_page =
dt_conf_get_int(
"plugins/darkroom/negadoctor/gui_page");
1045 gtk_widget_show(gtk_notebook_get_nth_page(
g->notebook, active_page));
1046 gtk_notebook_set_current_page(
g->notebook, active_page);
1054 dt_conf_set_int(
"plugins/darkroom/negadoctor/gui_page", gtk_notebook_get_current_page(
g->notebook));
1072 else if(w ==
g->Dmin_R || w ==
g->Dmin_G || w ==
g->Dmin_B)
1076 else if(w ==
g->exposure)
1078 p->exposure = powf(2.0f,
p->exposure);
1081 if(
IS_NULL_PTR(w) || w ==
g->wb_high_R || w ==
g->wb_high_G || w ==
g->wb_high_B)
1086 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)
@ DEVELOP_BLEND_CS_RGB_DISPLAY
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
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)
const dt_colormatrix_t dt_aligned_pixel_t out
static dt_aligned_pixel_t RGB
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
void dt_conf_set_int(const char *name, int val)
int dt_conf_get_int(const char *name)
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...
void dt_print(dt_debug_thread_t thread, const char *msg,...)
static const dt_aligned_pixel_simd_t const dt_aligned_pixel_simd_t row1
#define dt_free_align(ptr)
static void * dt_calloc_align(size_t size)
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
#define __DT_CLONE_TARGETS__
static const dt_aligned_pixel_simd_t const dt_aligned_pixel_simd_t const dt_aligned_pixel_simd_t row2
#define __OMP_FOR_SIMD__(...)
#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_dev_add_history_item(dev, module, enable, redraw)
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
GtkWidget * dt_ui_notebook_page(GtkNotebook *notebook, const char *text, const char *tooltip)
GtkNotebook * dt_ui_notebook_new()
static GtkWidget * dt_ui_section_label_new(const gchar *str)
#define DT_GUI_BOX_SPACING
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, const dt_develop_blend_colorspace_t blend_cst)
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
#define IOP_GUI_ALLOC(module)
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)
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)
float *const restrict const size_t k
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)
float dt_aligned_pixel_t[4]
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)
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])
struct _GtkWidget GtkWidget
const float uint32_t state[4]
struct dt_gui_gtk_t * gui
struct dt_develop_t * develop
struct dt_iop_module_t *void * data
GModule *dt_dev_operation_t op
dt_iop_global_data_t * data
dt_iop_params_t * default_params
dt_iop_gui_data_t * gui_data
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.