Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
negadoctor.c
Go to the documentation of this file.
1/*
2 This file is part of the Ansel project.
3 Copyright (C) 2020 Aldric Renaudin.
4 Copyright (C) 2020, 2022-2023, 2025-2026 Aurélien PIERRE.
5 Copyright (C) 2020-2022 Diederik Ter Rahe.
6 Copyright (C) 2020, 2022 Pascal Obry.
7 Copyright (C) 2020-2021 Ralf Brown.
8 Copyright (C) 2020 Tobias Ellinghaus.
9 Copyright (C) 2021 Chris Elston.
10 Copyright (C) 2021 Dan Torop.
11 Copyright (C) 2021 Hubert Kowalski.
12 Copyright (C) 2022 Hanno Schwalm.
13 Copyright (C) 2022 Martin Bařinka.
14 Copyright (C) 2022 Philipp Lutz.
15 Copyright (C) 2023, 2025 Guillaume Stutin.
16 Copyright (C) 2023 Luca Zulberti.
17
18 Ansel is free software: you can redistribute it and/or modify
19 it under the terms of the GNU General Public License as published by
20 the Free Software Foundation, either version 3 of the License, or
21 (at your option) any later version.
22
23 Ansel is distributed in the hope that it will be useful,
24 but WITHOUT ANY WARRANTY; without even the implied warranty of
25 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 GNU General Public License for more details.
27
28 You should have received a copy of the GNU General Public License
29 along with Ansel. If not, see <http://www.gnu.org/licenses/>.
30*/
31#ifdef HAVE_CONFIG_H
32#include "config.h"
33#endif
34
35#include "bauhaus/bauhaus.h"
36#include "common/darktable.h"
37#include "common/opencl.h"
38#include "control/control.h"
39#include "develop/develop.h"
40#include "develop/imageop.h"
42#include "develop/imageop_gui.h"
44#include "dtgtk/button.h"
45#include "dtgtk/resetlabel.h"
46
47#include "gui/gtk.h"
48#include "gui/presets.h"
50#include "iop/iop_api.h"
51
52#include <glib.h>
53#include <math.h>
54#include <stdlib.h>
55
72 #define THRESHOLD 2.3283064365386963e-10f // -32 EV
73
74
76
77
79{
80 // What kind of emulsion are we working on ?
81 DT_FILMSTOCK_NB = 0, // $DESCRIPTION: "black and white film"
82 DT_FILMSTOCK_COLOR = 1 // $DESCRIPTION: "color film"
84
85
87{
88 dt_iop_negadoctor_filmstock_t film_stock; /* $DEFAULT: DT_FILMSTOCK_COLOR $DESCRIPTION: "film stock" */
89 float Dmin[4]; /* color of film substrate
90 $MIN: 0.00001 $MAX: 1.5 $DEFAULT: 1.0 */
91 float wb_high[4]; /* white balance RGB coeffs (illuminant)
92 $MIN: 0.25 $MAX: 2 $DEFAULT: 1.0 */
93 float wb_low[4]; /* white balance RGB offsets (base light)
94 $MIN: 0.25 $MAX: 2 $DEFAULT: 1.0 */
95 float D_max; /* max density of film
96 $MIN: 0.1 $MAX: 6 $DEFAULT: 2.046 */
97 float offset; /* inversion offset
98 $MIN: -1.0 $MAX: 1.0 $DEFAULT: -0.05 $DESCRIPTION: "scan exposure bias" */
99 float black; /* display black level
100 $MIN: -0.5 $MAX: 0.5 $DEFAULT: 0.0755 $DESCRIPTION: "paper black (density correction)" */
101 float gamma; /* display gamma
102 $MIN: 1.0 $MAX: 8.0 $DEFAULT: 4.0 $DESCRIPTION: "paper grade (gamma)" */
103 float soft_clip; /* highlights roll-off
104 $MIN: 0.0001 $MAX: 1.0 $DEFAULT: 0.75 $DESCRIPTION: "paper gloss (specular highlights)" */
105 float exposure; /* extra exposure
106 $MIN: -2.0 $MAX: 2.0 $DEFAULT: 0.9245 $DESCRIPTION: "print exposure adjustment" */
108
109
111{
112 dt_aligned_pixel_t Dmin; // color of film substrate
113 dt_aligned_pixel_t wb_high; // white balance RGB coeffs / Dmax
114 dt_aligned_pixel_t offset; // inversion offset
115 float black; // display black level
116 float gamma; // display gamma
117 float soft_clip; // highlights roll-off
118 float soft_clip_comp; // 1 - softclip, complement to 1
119 float exposure; // extra exposure
121
122
137
138
143
144
145const char *name()
146{
147 return _("ne_gadoctor");
148}
149
150const char *aliases()
151{
152 return _("film|invert|negative|scan");
153}
154
155const char **description(struct dt_iop_module_t *self)
156{
157 return dt_iop_set_description(self, _("invert film negative scans and simulate printing on paper"),
158 _("corrective and creative"),
159 _("linear, RGB, display-referred"),
160 _("non-linear, RGB"),
161 _("non-linear, RGB, display-referred"));
162}
163
168
169
171{
172 return IOP_GROUP_FILM;
173}
174
175
177{
178 return IOP_CS_RGB;
179}
180
181int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version,
182 void *new_params, const int new_version)
183{
184 if(old_version == 1 && new_version == 2)
185 {
186 typedef struct dt_iop_negadoctor_params_v1_t
187 {
189 dt_aligned_pixel_t Dmin; // color of film substrate
190 dt_aligned_pixel_t wb_high; // white balance RGB coeffs (illuminant)
191 dt_aligned_pixel_t wb_low; // white balance RGB offsets (base light)
192 float D_max; // max density of film
193 float offset; // inversion offset
194 float black; // display black level
195 float gamma; // display gamma
196 float soft_clip; // highlights roll-off
197 float exposure; // extra exposure
198 } dt_iop_negadoctor_params_v1_t;
199
200 dt_iop_negadoctor_params_v1_t *o = (dt_iop_negadoctor_params_v1_t *)old_params;
203
204 *n = *d; // start with a fresh copy of default parameters
205
206 // WARNING: when copying the arrays in a for loop, gcc wrongly assumed
207 // that n and o were aligned and used AVX instructions for me,
208 // which segfaulted. let's hope this doesn't get optimized too much.
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];
222 n->D_max = o->D_max;
223 n->offset = o->offset;
224 n->black = o->black;
225 n->gamma = o->gamma;
226 n->soft_clip = o->soft_clip;
227 n->exposure = o->exposure;
228
229 return 0;
230 }
231 return 1;
232}
233
236{
239
240 // keep WB_high even in B&W mode to apply sepia or warm tone look
241 // but premultiply it aheard with Dmax to spare one div per pixel
242 for(size_t c = 0; c < 4; c++) d->wb_high[c] = p->wb_high[c] / p->D_max;
243
244 for(size_t c = 0; c < 4; c++) d->offset[c] = p->wb_high[c] * p->offset * p->wb_low[c];
245
246 // ensure we use a monochrome Dmin for B&W film
247 if(p->film_stock == DT_FILMSTOCK_COLOR)
248 for(size_t c = 0; c < 4; c++) d->Dmin[c] = p->Dmin[c];
249 else if(p->film_stock == DT_FILMSTOCK_NB)
250 for(size_t c = 0; c < 4; c++) d->Dmin[c] = p->Dmin[0];
251
252 // arithmetic trick allowing to rewrite the pixel inversion as FMA
253 d->black = -p->exposure * (1.0f + p->black);
254
255 // highlights soft clip
256 d->soft_clip = p->soft_clip;
257 d->soft_clip_comp = 1.0f - p->soft_clip;
258
259 // copy
260 d->exposure = p->exposure;
261 d->gamma = p->gamma;
262}
263
264
266int process(struct dt_iop_module_t *const self, const dt_dev_pixelpipe_t *const pipe,
267 const dt_dev_pixelpipe_iop_t *const piece, const void *const restrict ivoid,
268 void *const restrict ovoid)
269{
270 const dt_iop_roi_t *const restrict roi_out = &piece->roi_out;
271 const dt_iop_negadoctor_data_t *const d = piece->data;
272 const float *const restrict in = (float *)ivoid;
273 float *const restrict out = (float *)ovoid;
274 __OMP_FOR_SIMD__(aligned(in, out:64) collapse(2))
275 for(size_t k = 0; k < (size_t)roi_out->height * roi_out->width * 4; k += 4)
276 {
277 for(size_t c = 0; c < 4; c++)
278 {
279 // Unpack vectors one by one with extra pragmas to be sure the compiler understands they can be vectorized
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);
285
286 // Convert transmission to density using Dmin as a fulcrum
287 const float density = - log10f(Dmin[c] / fmaxf(pix_in[c], THRESHOLD)); // threshold to -32 EV
288
289 // Correct density in log space
290 const float corrected_de = wb_high[c] * density + offset[c];
291
292 // Print density on paper : ((1 - 10^corrected_de + black) * exposure)^gamma rewritten for FMA
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); // note : this is always > 0
295
296 // Compress highlights. from https://lists.gnu.org/archive/html/openexr-devel/2005-03/msg00009.html
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
298 : print_gamma;
299 }
300 }
301
303 dt_iop_alpha_copy(ivoid, ovoid, roi_out->width, roi_out->height);
304 return 0;
305}
306
307
308#ifdef HAVE_OPENCL
309int process_cl(struct dt_iop_module_t *const self, const dt_dev_pixelpipe_t *const pipe,
310 const dt_dev_pixelpipe_iop_t *const piece, cl_mem dev_in, cl_mem dev_out)
311{
312 const dt_iop_roi_t *const restrict roi_in = &piece->roi_in;
315
316 cl_int err = -999;
317
318 const int devid = pipe->devid;
319 const int width = roi_in->width;
320 const int height = roi_in->height;
321
322 size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
323
324 dt_opencl_set_kernel_arg(devid, gd->kernel_negadoctor, 0, sizeof(cl_mem), (void *)&dev_in);
325 dt_opencl_set_kernel_arg(devid, gd->kernel_negadoctor, 1, sizeof(cl_mem), (void *)&dev_out);
326 dt_opencl_set_kernel_arg(devid, gd->kernel_negadoctor, 2, sizeof(int), (void *)&width);
327 dt_opencl_set_kernel_arg(devid, gd->kernel_negadoctor, 3, sizeof(int), (void *)&height);
328 dt_opencl_set_kernel_arg(devid, gd->kernel_negadoctor, 4, 4 * sizeof(float), (void *)&d->Dmin);
329 dt_opencl_set_kernel_arg(devid, gd->kernel_negadoctor, 5, 4 * sizeof(float), (void *)&d->wb_high);
330 dt_opencl_set_kernel_arg(devid, gd->kernel_negadoctor, 6, 4 * sizeof(float), (void *)&d->offset);
331 dt_opencl_set_kernel_arg(devid, gd->kernel_negadoctor, 7, sizeof(float), (void *)&d->exposure);
332 dt_opencl_set_kernel_arg(devid, gd->kernel_negadoctor, 8, sizeof(float), (void *)&d->black);
333 dt_opencl_set_kernel_arg(devid, gd->kernel_negadoctor, 9, sizeof(float), (void *)&d->gamma);
334 dt_opencl_set_kernel_arg(devid, gd->kernel_negadoctor, 10, sizeof(float), (void *)&d->soft_clip);
335 dt_opencl_set_kernel_arg(devid, gd->kernel_negadoctor, 11, sizeof(float), (void *)&d->soft_clip_comp);
336
337 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_negadoctor, sizes);
338 if(err != CL_SUCCESS) goto error;
339 return TRUE;
340
341error:
342 dt_print(DT_DEBUG_OPENCL, "[opencl_negadoctor] couldn't enqueue kernel! %d\n", err);
343 return FALSE;
344}
345#endif
346
347
349{
350 dt_iop_default_init(module);
351
352 dt_iop_negadoctor_params_t *d = module->default_params;
353
354 d->Dmin[0] = 1.00f;
355 d->Dmin[1] = 0.45f;
356 d->Dmin[2] = 0.25f;
357}
358
360{
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 },
365 .D_max = 1.6f,
366 .offset = -0.05f,
367 .gamma = 4.0f,
368 .soft_clip = 0.75f,
369 .exposure = 0.9245f,
370 .black = 0.0755f };
371
372
373 dt_gui_presets_add_generic(_("color film"), self->op,
374 self->version(), &tmp, sizeof(tmp), 1, DEVELOP_BLEND_CS_RGB_DISPLAY);
375
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 },
380 .D_max = 2.2f,
381 .offset = -0.05f,
382 .gamma = 5.0f,
383 .soft_clip = 0.75f,
384 .exposure = 1.f,
385 .black = 0.0755f };
386
387
388 dt_gui_presets_add_generic(_("black and white film"), self->op,
389 self->version(), &tmq, sizeof(tmq), 1, DEVELOP_BLEND_CS_RGB_DISPLAY);
390}
391
393{
396 if(IS_NULL_PTR(gd)) return;
397
398 module->data = gd;
399 const int program = 30; // negadoctor.cl, from programs.conf
400 gd->kernel_negadoctor = dt_opencl_create_kernel(program, "negadoctor");
401}
402
404{
405 dt_iop_negadoctor_global_data_t *gd = module->data;
407 dt_free(module->data);
408}
409
411{
413 piece->data_size = sizeof(dt_iop_negadoctor_data_t);
414}
415
417{
418 dt_free_align(piece->data);
419 piece->data = NULL;
420}
421
422
423/* Global GUI stuff */
424
426{
427 gtk_widget_set_visible(g->Dmin_G, state);
428 gtk_widget_set_visible(g->Dmin_B, state);
429}
430
431
433{
436
437 if(p->film_stock == DT_FILMSTOCK_NB)
438 {
439 // Hide color controls
441 dt_bauhaus_widget_set_label(g->Dmin_R, N_("D min"));
442 }
443 else if(p->film_stock == DT_FILMSTOCK_COLOR)
444 {
445 // Show color controls
447 dt_bauhaus_widget_set_label(g->Dmin_R, N_("D min red component"));
448 }
449 else
450 {
451 // We shouldn't be there
452 fprintf(stderr, "negadoctor film stock: undefined behaviour\n");
453 }
454}
455
456
458{
461
462 GdkRGBA color;
463 color.alpha = 1.0f;
464
465 if(p->film_stock == DT_FILMSTOCK_COLOR)
466 {
467 color.red = p->Dmin[0];
468 color.green = p->Dmin[1];
469 color.blue = p->Dmin[2];
470 }
471 else if(p->film_stock == DT_FILMSTOCK_NB)
472 {
473 color.red = color.green = color.blue = p->Dmin[0];
474 }
475
476 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(g->Dmin_picker), &color);
477}
478
479static void Dmin_picker_callback(GtkColorButton *widget, dt_iop_module_t *self)
480{
481 if(dt_gui_widgets_suppressed()) return;
484
486
487 GdkRGBA c;
488 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget), &c);
489 p->Dmin[0] = c.red;
490 p->Dmin[1] = c.green;
491 p->Dmin[2] = c.blue;
492
494 dt_bauhaus_slider_set(g->Dmin_R, p->Dmin[0]);
495 dt_bauhaus_slider_set(g->Dmin_G, p->Dmin[1]);
496 dt_bauhaus_slider_set(g->Dmin_B, p->Dmin[2]);
498
499 Dmin_picker_update(self);
502}
503
505{
508
509 GdkRGBA color;
510 color.alpha = 1.0f;
511
512 dt_aligned_pixel_t WB_low_invert;
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;
516
517 color.red = WB_low_invert[0];
518 color.green = WB_low_invert[1];
519 color.blue = WB_low_invert[2];
520
521 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(g->WB_low_picker), &color);
522}
523
524static void WB_low_picker_callback(GtkColorButton *widget, dt_iop_module_t *self)
525{
526 if(dt_gui_widgets_suppressed()) return;
529
531
532 GdkRGBA c;
533 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget), &c);
534
535 dt_aligned_pixel_t RGB = { 2.0f - c.red, 2.0f - c.green, 2.0f - c.blue };
536
537 float RGB_min = v_minf(RGB);
538 for(size_t k = 0; k < 3; k++) p->wb_low[k] = RGB[k] / RGB_min;
539
541 dt_bauhaus_slider_set(g->wb_low_R, p->wb_low[0]);
542 dt_bauhaus_slider_set(g->wb_low_G, p->wb_low[1]);
543 dt_bauhaus_slider_set(g->wb_low_B, p->wb_low[2]);
545
549}
550
551
553{
556
557 GdkRGBA color;
558 color.alpha = 1.0f;
559
560 dt_aligned_pixel_t WB_high_invert;
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;
564
565 color.red = WB_high_invert[0];
566 color.green = WB_high_invert[1];
567 color.blue = WB_high_invert[2];
568
569 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(g->WB_high_picker), &color);
570}
571
572static void WB_high_picker_callback(GtkColorButton *widget, dt_iop_module_t *self)
573{
574 if(dt_gui_widgets_suppressed()) return;
577
579
580 GdkRGBA c;
581 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget), &c);
582
583 dt_aligned_pixel_t RGB = { 2.0f - c.red, 2.0f - c.green, 2.0f - c.blue };
584 float RGB_min = v_minf(RGB);
585 for(size_t k = 0; k < 3; k++) p->wb_high[k] = RGB[k] / RGB_min;
586
588 dt_bauhaus_slider_set(g->wb_high_R, p->wb_high[0]);
589 dt_bauhaus_slider_set(g->wb_high_G, p->wb_high[1]);
590 dt_bauhaus_slider_set(g->wb_high_B, p->wb_high[2]);
592
596}
597
598static void Wb_low_norm_callback(GtkColorButton *widget, dt_iop_module_t *self)
599{
600 if(dt_gui_widgets_suppressed()) return;
601
604
605
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;
609
610
612 dt_bauhaus_slider_set(g->wb_low_R, p->wb_low[0]);
613 dt_bauhaus_slider_set(g->wb_low_G, p->wb_low[1]);
614 dt_bauhaus_slider_set(g->wb_low_B, p->wb_low[2]);
616
620}
621
622static void Wb_high_norm_callback(GtkColorButton *widget, dt_iop_module_t *self)
623{
624 if(dt_gui_widgets_suppressed()) return;
625
628
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;
632
633
635 dt_bauhaus_slider_set(g->wb_high_R, p->wb_high[0]);
636 dt_bauhaus_slider_set(g->wb_high_G, p->wb_high[1]);
637 dt_bauhaus_slider_set(g->wb_high_B, p->wb_high[2]);
639
643}
644
645/* Color pickers auto-tuners */
646
647// measure Dmin from the film edges first
649{
650 if(dt_gui_widgets_suppressed()) return;
653
654 for(int k = 0; k < 4; k++) p->Dmin[k] = self->picked_color[k];
655
657 dt_bauhaus_slider_set(g->Dmin_R, p->Dmin[0]);
658 dt_bauhaus_slider_set(g->Dmin_G, p->Dmin[1]);
659 dt_bauhaus_slider_set(g->Dmin_B, p->Dmin[2]);
661
662 Dmin_picker_update(self);
665}
666
667// from Dmin, find out the range of density values of the film and compute Dmax
669{
670 if(dt_gui_widgets_suppressed()) return;
673
675 for(int c = 0; c < 3; c++)
676 {
677 RGB[c] = log10f(p->Dmin[c] / fmaxf(self->picked_color_min[c], THRESHOLD));
678 }
679
680 // Take the max(RGB) for safety. Big values unclip whites
681 p->D_max = v_maxf(RGB);
682
684 dt_bauhaus_slider_set(g->D_max, p->D_max);
686
689}
690
691// from Dmax, compute the offset so the range of density is rescaled between [0; 1]
693{
694 if(dt_gui_widgets_suppressed()) return;
697
699 for(int c = 0; c < 3; c++)
700 RGB[c] = log10f(p->Dmin[c] / fmaxf(self->picked_color_max[c], THRESHOLD)) / p->D_max;
701
702 // Take the min(RGB) for safety. Negative values unclip blacks
703 p->offset = v_minf(RGB);
704
706 dt_bauhaus_slider_set(g->offset, p->offset);
708
711}
712
713// from Dmax and offset, compute the white balance correction as multipliers of the offset
714// such that offset x wb[c] make black monochrome
716{
717 if(dt_gui_widgets_suppressed()) return;
720
721 dt_aligned_pixel_t RGB_min;
722 for(int c = 0; c < 3; c++)
723 RGB_min[c] = log10f(p->Dmin[c] / fmaxf(self->picked_color[c], THRESHOLD)) / p->D_max;
724
725 const float RGB_v_min = v_minf(RGB_min); // warning: can be negative
726 for(int c = 0; c < 3; c++) p->wb_low[c] = RGB_v_min / RGB_min[c];
727
729 dt_bauhaus_slider_set(g->wb_low_R, p->wb_low[0]);
730 dt_bauhaus_slider_set(g->wb_low_G, p->wb_low[1]);
731 dt_bauhaus_slider_set(g->wb_low_B, p->wb_low[2]);
733
737}
738
739// from Dmax, offset and white balance multipliers, compute the white balance of the illuminant as multipliers of 1/Dmax
740// such that WB[c] / Dmax make white monochrome
742{
743 if(dt_gui_widgets_suppressed()) return;
746
747 dt_aligned_pixel_t RGB_min;
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));
750
751 const float RGB_v_min = v_minf(RGB_min); // warning : must be positive
752 for(int c = 0; c < 3; c++) p->wb_high[c] = RGB_min[c] / RGB_v_min;
753
755 dt_bauhaus_slider_set(g->wb_high_R, p->wb_high[0]);
756 dt_bauhaus_slider_set(g->wb_high_G, p->wb_high[1]);
757 dt_bauhaus_slider_set(g->wb_high_B, p->wb_high[2]);
759
763}
764
765// from Dmax, offset and both white balances, compute the print black adjustment
766// such that the printed values range from 0 to + infinity
768{
769 if(dt_gui_widgets_suppressed()) return;
772
774 for(int c = 0; c < 3; c++)
775 {
776 RGB[c] = -log10f(p->Dmin[c] / fmaxf(self->picked_color_max[c], THRESHOLD));
777 RGB[c] *= p->wb_high[c] / p->D_max;
778 RGB[c] += p->wb_low[c] * p->offset * p->wb_high[c];
779 RGB[c] = 0.1f - (1.0f - fast_exp10f(RGB[c])); // actually, remap between -3.32 EV and infinity for safety because gamma comes later
780 }
781 p->black = v_maxf(RGB);
782
784 dt_bauhaus_slider_set(g->black, p->black);
786
789}
790
791// from Dmax, offset, both white balances, and printblack, compute the print exposure adjustment as a scaling factor
792// such that the printed values range from 0 to 1
794{
795 if(dt_gui_widgets_suppressed()) return;
798
800 for(int c = 0; c < 3; c++)
801 {
802 RGB[c] = -log10f(p->Dmin[c] / fmaxf(self->picked_color_min[c], THRESHOLD));
803 RGB[c] *= p->wb_high[c] / p->D_max;
804 RGB[c] += p->wb_low[c] * p->offset;
805 RGB[c] = 0.96f / (1.0f - fast_exp10f(RGB[c]) + p->black); // actually, remap in [0; 0.96] for safety
806 }
807 p->exposure = v_minf(RGB);
808
810 dt_bauhaus_slider_set(g->exposure, log2f(p->exposure));
812
815}
816
817
819{
820 (void)pipe;
821 (void)piece;
822 if(dt_gui_widgets_suppressed()) return;
824
825 if (picker == g->Dmin_sampler)
826 apply_auto_Dmin(self);
827 else if(picker == g->WB_high_sampler)
828 apply_auto_WB_high(self);
829 else if(picker == g->offset)
830 apply_auto_offset(self);
831 else if(picker == g->D_max)
832 apply_auto_Dmax(self);
833 else if(picker == g->WB_low_sampler)
834 apply_auto_WB_low(self);
835 else if(picker == g->exposure)
837 else if(picker == g->black)
838 apply_auto_black(self);
839 else
840 fprintf(stderr, "[negadoctor] unknown color picker\n");
841}
842
844{
846
847 g->notebook = dt_ui_notebook_new();
848 // Each page holds its own pickers (Dmin, D_max, offset, white balance samplers,
849 // black/exposure); reset any active one when the page it lives on is switched
850 // away from.
851 dt_ui_notebook_set_picker_owner(g->notebook, self);
852
853 // Page FILM PROPERTIES
854 GtkWidget *page1 = self->widget = dt_ui_notebook_page(g->notebook, N_("film properties"), NULL);
855
856 // Dmin
857
858 gtk_box_pack_start(GTK_BOX(page1), dt_ui_section_label_new(_("color of the film base")), FALSE, FALSE, 0);
859
860 GtkWidget *row1 = GTK_WIDGET(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING));
861
862 g->Dmin_picker = gtk_color_button_new();
863 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(g->Dmin_picker), FALSE);
864 gtk_color_button_set_title(GTK_COLOR_BUTTON(g->Dmin_picker), _("select color of film material from a swatch"));
865 gtk_box_pack_start(GTK_BOX(row1), GTK_WIDGET(g->Dmin_picker), TRUE, TRUE, 0);
866 g_signal_connect(G_OBJECT(g->Dmin_picker), "color-set", G_CALLBACK(Dmin_picker_callback), self);
867
868 g->Dmin_sampler = dt_color_picker_new(self, DT_COLOR_PICKER_AREA, row1);
869 gtk_widget_set_tooltip_text(g->Dmin_sampler , _("pick color of film material from image"));
870
871 gtk_box_pack_start(GTK_BOX(page1), GTK_WIDGET(row1), FALSE, FALSE, 0);
872
873 g->Dmin_R = dt_bauhaus_slider_from_params(self, "Dmin[0]");
875 dt_bauhaus_slider_set_format(g->Dmin_R, "%");
876 dt_bauhaus_slider_set_factor(g->Dmin_R, 100);
877 dt_bauhaus_widget_set_label(g->Dmin_R, N_("D min red component"));
878 gtk_widget_set_tooltip_text(g->Dmin_R, _("adjust the color and shade of the film transparent base.\n"
879 "this value depends on the film material, \n"
880 "the chemical fog produced while developing the film,\n"
881 "and the scanner white balance."));
882
883 g->Dmin_G = dt_bauhaus_slider_from_params(self, "Dmin[1]");
885 dt_bauhaus_slider_set_format(g->Dmin_G, "%");
886 dt_bauhaus_slider_set_factor(g->Dmin_G, 100);
887 dt_bauhaus_widget_set_label(g->Dmin_G, N_("D min green component"));
888 gtk_widget_set_tooltip_text(g->Dmin_G, _("adjust the color and shade of the film transparent base.\n"
889 "this value depends on the film material, \n"
890 "the chemical fog produced while developing the film,\n"
891 "and the scanner white balance."));
892
893 g->Dmin_B = dt_bauhaus_slider_from_params(self, "Dmin[2]");
895 dt_bauhaus_slider_set_format(g->Dmin_B, "%");
896 dt_bauhaus_slider_set_factor(g->Dmin_B, 100);
897 dt_bauhaus_widget_set_label(g->Dmin_B, N_("D min blue component"));
898 gtk_widget_set_tooltip_text(g->Dmin_B, _("adjust the color and shade of the film transparent base.\n"
899 "this value depends on the film material, \n"
900 "the chemical fog produced while developing the film,\n"
901 "and the scanner white balance."));
902
903 // D max and scanner bias
904
905 gtk_box_pack_start(GTK_BOX(page1), dt_ui_section_label_new(_("dynamic range of the film")), FALSE, FALSE, 0);
906
908 dt_bauhaus_slider_set_format(g->D_max, " dB");
909 gtk_widget_set_tooltip_text(g->D_max, _("maximum density of the film, corresponding to white after inversion.\n"
910 "this value depends on the film specifications, the developing process,\n"
911 "the dynamic range of the scene and the scanner exposure settings."));
912
913 gtk_box_pack_start(GTK_BOX(page1), dt_ui_section_label_new(_("scanner exposure settings")), FALSE, FALSE, 0);
914
916 dt_bauhaus_slider_set_format(g->offset, " dB");
917 gtk_widget_set_tooltip_text(g->offset, _("correct the exposure of the scanner, for all RGB channels,\n"
918 "before the inversion, so blacks are neither clipped or too pale."));
919
920 // Page CORRECTIONS
921 GtkWidget *page2 = self->widget = dt_ui_notebook_page(g->notebook, N_("corrections"), NULL);
922
923 // WB shadows
924 gtk_box_pack_start(GTK_BOX(page2), dt_ui_section_label_new(_("shadows color cast")), FALSE, FALSE, 0);
925
926 GtkWidget *row3 = GTK_WIDGET(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING));
927
928 g->WB_low_picker = gtk_color_button_new();
929 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(g->WB_low_picker), FALSE);
930 gtk_color_button_set_title(GTK_COLOR_BUTTON(g->WB_low_picker), _("select color of shadows from a swatch"));
931 gtk_box_pack_start(GTK_BOX(row3), GTK_WIDGET(g->WB_low_picker), TRUE, TRUE, 0);
932 g_signal_connect(G_OBJECT(g->WB_low_picker), "color-set", G_CALLBACK(WB_low_picker_callback), self);
933
934 g->WB_low_norm = dt_action_button_new((dt_lib_module_t *)self, N_("normalize"), Wb_low_norm_callback, self, _("normalize shadows white balance settings"), 0, 0);
935 gtk_box_pack_start(GTK_BOX(row3), GTK_WIDGET(g->WB_low_norm), FALSE, FALSE, 0);
936
937 g->WB_low_sampler = dt_color_picker_new(self, DT_COLOR_PICKER_AREA, row3);
938 gtk_widget_set_tooltip_text(g->WB_low_sampler, _("pick shadows color from image"));
939
940 gtk_box_pack_start(GTK_BOX(page2), GTK_WIDGET(row3), FALSE, FALSE, 0);
941
942 g->wb_low_R = dt_bauhaus_slider_from_params(self, "wb_low[0]");
943 dt_bauhaus_widget_set_label(g->wb_low_R, N_("shadows red offset"));
944 gtk_widget_set_tooltip_text(g->wb_low_R, _("correct the color cast in shadows so blacks are\n"
945 "truly achromatic. Setting this value before\n"
946 "the highlights illuminant white balance will help\n"
947 "recovering the global white balance in difficult cases."));
948
949 g->wb_low_G = dt_bauhaus_slider_from_params(self, "wb_low[1]");
950 dt_bauhaus_widget_set_label(g->wb_low_G, N_("shadows green offset"));
951 gtk_widget_set_tooltip_text(g->wb_low_G, _("correct the color cast in shadows so blacks are\n"
952 "truly achromatic. Setting this value before\n"
953 "the highlights illuminant white balance will help\n"
954 "recovering the global white balance in difficult cases."));
955
956 g->wb_low_B = dt_bauhaus_slider_from_params(self, "wb_low[2]");
957 dt_bauhaus_widget_set_label(g->wb_low_B, N_("shadows blue offset"));
958 gtk_widget_set_tooltip_text(g->wb_low_B, _("correct the color cast in shadows so blacks are\n"
959 "truly achromatic. Setting this value before\n"
960 "the highlights illuminant white balance will help\n"
961 "recovering the global white balance in difficult cases."));
962
963 // WB highlights
964 gtk_box_pack_start(GTK_BOX(page2), dt_ui_section_label_new(_("highlights white balance")), FALSE, FALSE, 0);
965
966 GtkWidget *row2 = GTK_WIDGET(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING));
967
968 g->WB_high_picker = gtk_color_button_new();
969 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(g->WB_high_picker), FALSE);
970 gtk_color_button_set_title(GTK_COLOR_BUTTON(g->WB_high_picker), _("select color of illuminant from a swatch"));
971 gtk_box_pack_start(GTK_BOX(row2), GTK_WIDGET(g->WB_high_picker), TRUE, TRUE, 0);
972 g_signal_connect(G_OBJECT(g->WB_high_picker), "color-set", G_CALLBACK(WB_high_picker_callback), self);
973
974 g->WB_high_norm = dt_action_button_new((dt_lib_module_t *)self, N_("normalize"), Wb_high_norm_callback, self, _("normalize highlight white balance settings"), 0, 0);
975 gtk_box_pack_start(GTK_BOX(row2), GTK_WIDGET(g->WB_high_norm), FALSE, FALSE, 0);
976
977 g->WB_high_sampler = dt_color_picker_new(self, DT_COLOR_PICKER_AREA, row2);
978 gtk_widget_set_tooltip_text(g->WB_high_sampler , _("pick illuminant color from image"));
979
980 gtk_box_pack_start(GTK_BOX(page2), GTK_WIDGET(row2), FALSE, FALSE, 0);
981
982 g->wb_high_R = dt_bauhaus_slider_from_params(self, "wb_high[0]");
983 dt_bauhaus_widget_set_label(g->wb_high_R, N_("illuminant red gain"));
984 gtk_widget_set_tooltip_text(g->wb_high_R, _("correct the color of the illuminant so whites are\n"
985 "truly achromatic. Setting this value after\n"
986 "the shadows color cast will help\n"
987 "recovering the global white balance in difficult cases."));
988
989 g->wb_high_G = dt_bauhaus_slider_from_params(self, "wb_high[1]");
990 dt_bauhaus_widget_set_label(g->wb_high_G, N_("illuminant green gain"));
991 gtk_widget_set_tooltip_text(g->wb_high_G, _("correct the color of the illuminant so whites are\n"
992 "truly achromatic. Setting this value after\n"
993 "the shadows color cast will help\n"
994 "recovering the global white balance in difficult cases."));
995
996 g->wb_high_B = dt_bauhaus_slider_from_params(self, "wb_high[2]");
997 dt_bauhaus_widget_set_label(g->wb_high_B, N_("illuminant blue gain"));
998 gtk_widget_set_tooltip_text(g->wb_high_B, _("correct the color of the illuminant so whites are\n"
999 "truly achromatic. Setting this value after\n"
1000 "the shadows color cast will help\n"
1001 "recovering the global white balance in difficult cases."));
1002
1003 // Page PRINT PROPERTIES
1004 GtkWidget *page3 = self->widget = dt_ui_notebook_page(g->notebook, N_("print properties"), NULL);
1005
1006 // print corrections
1007 gtk_box_pack_start(GTK_BOX(page3), dt_ui_section_label_new(_("virtual paper properties")), FALSE, FALSE, 0);
1008
1011 dt_bauhaus_slider_set_factor(g->black, 100);
1012 dt_bauhaus_slider_set_format(g->black, "%");
1013 gtk_widget_set_tooltip_text(g->black, _("correct the density of black after the inversion,\n"
1014 "to adjust the global contrast while avoiding clipping shadows."));
1015
1016 g->gamma = dt_bauhaus_slider_from_params(self, "gamma");
1017 dt_bauhaus_widget_set_label(g->gamma, N_("paper grade (gamma)"));
1018 gtk_widget_set_tooltip_text(g->gamma, _("select the grade of the virtual paper, which is actually\n"
1019 "equivalent to applying a gamma. it compensates the film D max\n"
1020 "and recovers the contrast. use a high grade for high D max."));
1021
1022 g->soft_clip = dt_bauhaus_slider_from_params(self, "soft_clip");
1023 dt_bauhaus_slider_set_factor(g->soft_clip, 100);
1024 dt_bauhaus_slider_set_digits(g->soft_clip, 4);
1025 dt_bauhaus_slider_set_format(g->soft_clip, "%");
1026 gtk_widget_set_tooltip_text(g->soft_clip, _("gradually compress specular highlights past this value\n"
1027 "to avoid clipping while pushing the exposure for mid-tones.\n"
1028 "this somewhat reproduces the behaviour of matte paper."));
1029
1030 gtk_box_pack_start(GTK_BOX(page3), dt_ui_section_label_new(_("virtual print emulation")), FALSE, FALSE, 0);
1031
1032 g->exposure = dt_color_picker_new(self, DT_COLOR_PICKER_AREA, dt_bauhaus_slider_from_params(self, "exposure"));
1033 dt_bauhaus_slider_set_soft_min(g->exposure, -1.0);
1034 dt_bauhaus_slider_set_soft_max(g->exposure, 1.0);
1035 dt_bauhaus_slider_set_default(g->exposure, 0.0);
1036 dt_bauhaus_slider_set_format(g->exposure, _(" EV"));
1037 gtk_widget_set_tooltip_text(g->exposure, _("correct the printing exposure after inversion to adjust\n"
1038 "the global contrast and avoid clipping highlights."));
1039
1040 // start building top level widget
1041 self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1042
1043 // Film emulsion
1044 g->film_stock = dt_bauhaus_combobox_from_params(self, "film_stock");
1045 gtk_widget_set_tooltip_text(g->film_stock, _("toggle on or off the color controls"));
1046
1047 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(g->notebook), FALSE, FALSE, 0);
1048 const int active_page = dt_conf_get_int("plugins/darkroom/negadoctor/gui_page");
1049 gtk_widget_show(gtk_notebook_get_nth_page(g->notebook, active_page));
1050 gtk_notebook_set_current_page(g->notebook, active_page);
1051
1052}
1053
1055{
1057 if(!IS_NULL_PTR(g) && GTK_IS_NOTEBOOK(g->notebook))
1058 dt_conf_set_int("plugins/darkroom/negadoctor/gui_page", gtk_notebook_get_current_page(g->notebook));
1060}
1061
1062void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
1063{
1066 if(IS_NULL_PTR(w) || w == g->film_stock)
1067 {
1069 Dmin_picker_update(self);
1070 }
1071 else if(w == g->Dmin_R && p->film_stock == DT_FILMSTOCK_NB)
1072 {
1073 dt_bauhaus_slider_set(g->Dmin_G, p->Dmin[0]);
1074 dt_bauhaus_slider_set(g->Dmin_B, p->Dmin[0]);
1075 }
1076 else if(w == g->Dmin_R || w == g->Dmin_G || w == g->Dmin_B)
1077 {
1078 Dmin_picker_update(self);
1079 }
1080 else if(w == g->exposure)
1081 {
1082 p->exposure = powf(2.0f, p->exposure);
1083 }
1084
1085 if(IS_NULL_PTR(w) || w == g->wb_high_R || w == g->wb_high_G || w == g->wb_high_B)
1086 {
1088 }
1089
1090 if(IS_NULL_PTR(w) || w == g->wb_low_R || w == g->wb_low_G || w == g->wb_low_B)
1091 {
1093 }
1094}
1095
1096
1098{
1099 // let gui slider match current parameters:
1102
1104
1105
1106 dt_bauhaus_slider_set(g->exposure, log2f(p->exposure)); // warning: GUI is in EV
1107
1108 // Update custom stuff
1109 gui_changed(self, NULL, NULL);
1110}
1111
1113{
1115}
1116// clang-format off
1117// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1118// vim: shiftwidth=2 expandtab tabstop=2 cindent
1119// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1120// clang-format on
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dt_bauhaus_slider_set_digits(GtkWidget *widget, int val)
Definition bauhaus.c:3549
void dt_bauhaus_slider_set_default(GtkWidget *widget, float def)
Definition bauhaus.c:1643
void dt_bauhaus_slider_set_soft_max(GtkWidget *widget, float val)
Definition bauhaus.c:1626
void dt_bauhaus_slider_set_soft_min(GtkWidget *widget, float val)
Definition bauhaus.c:1609
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3537
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
Definition bauhaus.c:1656
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
Definition bauhaus.c:3613
void dt_bauhaus_slider_set_factor(GtkWidget *widget, float factor)
Definition bauhaus.c:3626
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
@ DEVELOP_BLEND_CS_RGB_DISPLAY
Definition blend.h:59
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
@ IOP_CS_RGB
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)
@ DT_COLOR_PICKER_AREA
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...
Definition control.c:922
darktable_t darktable
Definition darktable.c:183
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1600
static const dt_aligned_pixel_simd_t const dt_aligned_pixel_simd_t row1
Definition darktable.h:645
#define dt_free_align(ptr)
Definition darktable.h:503
static void * dt_calloc_align(size_t size)
Definition darktable.h:510
@ DT_DEBUG_OPENCL
Definition darktable.h:744
#define dt_gui_freeze_begin()
Definition darktable.h:900
#define dt_gui_freeze_end()
Definition darktable.h:901
#define dt_free(ptr)
Definition darktable.h:478
void void void gboolean dt_gui_widgets_suppressed(void)
Definition gtk.c:194
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
Definition darktable.h:151
#define __DT_CLONE_TARGETS__
Definition darktable.h:379
static const dt_aligned_pixel_simd_t const dt_aligned_pixel_simd_t const dt_aligned_pixel_simd_t row2
Definition darktable.h:646
#define __OMP_FOR_SIMD__(...)
Definition darktable.h:272
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:293
#define dt_dev_add_history_item(dev, module, enable, redraw)
void dt_iop_params_t
Definition dev_history.h:42
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
Definition develop.h:118
GtkWidget * dt_ui_notebook_page(GtkNotebook *notebook, const char *text, const char *tooltip)
Definition gtk.c:2470
GtkNotebook * dt_ui_notebook_new()
Definition gtk.c:2465
void dt_ui_notebook_set_picker_owner(GtkNotebook *notebook, gpointer owner)
Register an opaque owner for a GtkNotebook's page switches, and relay every "switch_page" as DT_SIGNA...
Definition gtk.c:2500
static GtkWidget * dt_ui_section_label_new(const gchar *str)
Definition gtk.h:469
#define DT_GUI_BOX_SPACING
Definition gtk.h:109
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)
Definition imageop.c:321
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)
Definition imageop.c:3220
#define IOP_GUI_FREE
Definition imageop.h:641
@ IOP_FLAGS_INCLUDE_IN_STYLES
Definition imageop.h:196
@ IOP_FLAGS_ALLOW_TILING
Definition imageop.h:199
@ IOP_FLAGS_ONE_INSTANCE
Definition imageop.h:202
@ IOP_GROUP_FILM
Definition imageop.h:168
#define IOP_GUI_ALLOC(module)
Definition imageop.h:638
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
Definition imageop_gui.c:77
GtkWidget * dt_bauhaus_combobox_from_params(dt_iop_module_t *self, const char *param)
void *const ovoid
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)
Definition lib.c:1563
float *const restrict const size_t k
void init(dt_iop_module_t *module)
Definition negadoctor.c:348
const char ** description(struct dt_iop_module_t *self)
Definition negadoctor.c:155
static void WB_high_picker_update(dt_iop_module_t *self)
Definition negadoctor.c:552
int default_group()
Definition negadoctor.c:170
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)
Definition negadoctor.c:524
static void Wb_high_norm_callback(GtkColorButton *widget, dt_iop_module_t *self)
Definition negadoctor.c:622
static void toggle_stock_controls(dt_iop_module_t *const self)
Definition negadoctor.c:432
static void WB_high_picker_callback(GtkColorButton *widget, dt_iop_module_t *self)
Definition negadoctor.c:572
__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)
Definition negadoctor.c:266
#define THRESHOLD
Definition negadoctor.c:72
static void apply_auto_WB_high(dt_iop_module_t *self)
Definition negadoctor.c:741
dt_iop_negadoctor_filmstock_t
Definition negadoctor.c:79
@ DT_FILMSTOCK_NB
Definition negadoctor.c:81
@ DT_FILMSTOCK_COLOR
Definition negadoctor.c:82
static void apply_auto_WB_low(dt_iop_module_t *self)
Definition negadoctor.c:715
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)
Definition negadoctor.c:309
const char * aliases()
Definition negadoctor.c:150
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition negadoctor.c:410
const char * name()
Definition negadoctor.c:145
void gui_init(dt_iop_module_t *self)
Definition negadoctor.c:843
static void setup_color_variables(dt_iop_negadoctor_gui_data_t *const g, const gint state)
Definition negadoctor.c:425
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)
Definition negadoctor.c:234
void cleanup_global(dt_iop_module_so_t *module)
Definition negadoctor.c:403
static void apply_auto_exposure(dt_iop_module_t *self)
Definition negadoctor.c:793
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition negadoctor.c:176
int flags()
Definition negadoctor.c:164
static void apply_auto_Dmin(dt_iop_module_t *self)
Definition negadoctor.c:648
void gui_cleanup(struct dt_iop_module_t *self)
static void Dmin_picker_update(dt_iop_module_t *self)
Definition negadoctor.c:457
void init_presets(dt_iop_module_so_t *self)
Definition negadoctor.c:359
static void WB_low_picker_update(dt_iop_module_t *self)
Definition negadoctor.c:504
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition negadoctor.c:416
static void Wb_low_norm_callback(GtkColorButton *widget, dt_iop_module_t *self)
Definition negadoctor.c:598
static void apply_auto_offset(dt_iop_module_t *self)
Definition negadoctor.c:692
static void apply_auto_black(dt_iop_module_t *self)
Definition negadoctor.c:767
void init_global(dt_iop_module_so_t *module)
Definition negadoctor.c:392
static void apply_auto_Dmax(dt_iop_module_t *self)
Definition negadoctor.c:668
void color_picker_apply(dt_iop_module_t *self, GtkWidget *picker, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition negadoctor.c:818
int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params, const int new_version)
Definition negadoctor.c:181
static void Dmin_picker_callback(GtkColorButton *widget, dt_iop_module_t *self)
Definition negadoctor.c:479
float dt_aligned_pixel_t[4]
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
Definition opencl.c:2164
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2058
void dt_opencl_free_kernel(const int kernel)
Definition opencl.c:2101
int dt_opencl_set_kernel_arg(const int dev, const int kernel, const int num, const size_t size, const void *arg)
Definition opencl.c:2155
#define ROUNDUPDHT(a, b)
Definition opencl.h:82
#define ROUNDUPDWD(a, b)
Definition opencl.h:81
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
Definition splash.h:29
const float uint32_t state[4]
struct dt_develop_t * develop
Definition darktable.h:798
struct dt_iop_module_t *void * data
GModule *dt_dev_operation_t op
Definition imageop.h:260
dt_iop_global_data_t * data
Definition imageop.h:263
dt_iop_params_t * default_params
Definition imageop.h:344
GtkWidget * widget
Definition imageop.h:374
dt_iop_gui_data_t * gui_data
Definition imageop.h:348
dt_iop_global_data_t * global_data
Definition imageop.h:351
dt_aligned_pixel_t picked_color_min
Definition imageop.h:309
dt_aligned_pixel_t picked_color_max
Definition imageop.h:309
dt_aligned_pixel_t picked_color
Definition imageop.h:309
dt_iop_params_t * params
Definition imageop.h:344
dt_aligned_pixel_t wb_high
Definition negadoctor.c:113
dt_aligned_pixel_t offset
Definition negadoctor.c:114
dt_aligned_pixel_t Dmin
Definition negadoctor.c:112
dt_iop_negadoctor_filmstock_t film_stock
Definition negadoctor.c:88
Region of interest passed through the pixelpipe.
Definition imageop.h:72