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#include "common/conf.h"
34#endif
35
36#include "widgets/bauhaus.h"
37#include "system/macros.h"
38#include "system/openmp.h"
40#include "system/mem_alloc.h"
41#include "system/simd.h"
42#include "common/logging.h"
44#include "common/opencl.h"
45#include "control/control.h"
46#include "develop/develop.h"
47#include "develop/imageop.h"
49#include "develop/imageop_gui.h"
50#include "math/openmp_maths.h"
51
52#include "gui/presets.h"
54#include "iop/iop_api.h"
55
56#include <glib.h>
57#include <math.h>
58#include <stdlib.h>
59#include "widgets/label.h"
60#include "widgets/notebook.h"
61
78 #define THRESHOLD 2.3283064365386963e-10f // -32 EV
79
80
82
83
85{
86 // What kind of emulsion are we working on ?
87 DT_FILMSTOCK_NB = 0, // $DESCRIPTION: "black and white film"
88 DT_FILMSTOCK_COLOR = 1 // $DESCRIPTION: "color film"
90
91
93{
94 dt_iop_negadoctor_filmstock_t film_stock; /* $DEFAULT: DT_FILMSTOCK_COLOR $DESCRIPTION: "film stock" */
95 float Dmin[4]; /* color of film substrate
96 $MIN: 0.00001 $MAX: 1.5 $DEFAULT: 1.0 */
97 float wb_high[4]; /* white balance RGB coeffs (illuminant)
98 $MIN: 0.25 $MAX: 2 $DEFAULT: 1.0 */
99 float wb_low[4]; /* white balance RGB offsets (base light)
100 $MIN: 0.25 $MAX: 2 $DEFAULT: 1.0 */
101 float D_max; /* max density of film
102 $MIN: 0.1 $MAX: 6 $DEFAULT: 2.046 */
103 float offset; /* inversion offset
104 $MIN: -1.0 $MAX: 1.0 $DEFAULT: -0.05 $DESCRIPTION: "scan exposure bias" */
105 float black; /* display black level
106 $MIN: -0.5 $MAX: 0.5 $DEFAULT: 0.0755 $DESCRIPTION: "paper black (density correction)" */
107 float gamma; /* display gamma
108 $MIN: 1.0 $MAX: 8.0 $DEFAULT: 4.0 $DESCRIPTION: "paper grade (gamma)" */
109 float soft_clip; /* highlights roll-off
110 $MIN: 0.0001 $MAX: 1.0 $DEFAULT: 0.75 $DESCRIPTION: "paper gloss (specular highlights)" */
111 float exposure; /* extra exposure
112 $MIN: -2.0 $MAX: 2.0 $DEFAULT: 0.9245 $DESCRIPTION: "print exposure adjustment" */
114
115
117{
118 dt_aligned_pixel_t Dmin; // color of film substrate
119 dt_aligned_pixel_t wb_high; // white balance RGB coeffs / Dmax
120 dt_aligned_pixel_t offset; // inversion offset
121 float black; // display black level
122 float gamma; // display gamma
123 float soft_clip; // highlights roll-off
124 float soft_clip_comp; // 1 - softclip, complement to 1
125 float exposure; // extra exposure
127
128
143
144
149
150
151const char *name()
152{
153 return _("ne_gadoctor");
154}
155
156const char *aliases()
157{
158 return _("film|invert|negative|scan");
159}
160
161const char **description(struct dt_iop_module_t *self)
162{
163 return dt_iop_set_description(self, _("invert film negative scans and simulate printing on paper"),
164 _("corrective and creative"),
165 _("linear, RGB, display-referred"),
166 _("non-linear, RGB"),
167 _("non-linear, RGB, display-referred"));
168}
169
174
175
177{
178 return IOP_GROUP_FILM;
179}
180
181
183{
184 return IOP_CS_RGB;
185}
186
187int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version,
188 void *new_params, const int new_version)
189{
190 if(old_version == 1 && new_version == 2)
191 {
192 typedef struct dt_iop_negadoctor_params_v1_t
193 {
195 dt_aligned_pixel_t Dmin; // color of film substrate
196 dt_aligned_pixel_t wb_high; // white balance RGB coeffs (illuminant)
197 dt_aligned_pixel_t wb_low; // white balance RGB offsets (base light)
198 float D_max; // max density of film
199 float offset; // inversion offset
200 float black; // display black level
201 float gamma; // display gamma
202 float soft_clip; // highlights roll-off
203 float exposure; // extra exposure
204 } dt_iop_negadoctor_params_v1_t;
205
206 dt_iop_negadoctor_params_v1_t *o = (dt_iop_negadoctor_params_v1_t *)old_params;
209
210 *n = *d; // start with a fresh copy of default parameters
211
212 // WARNING: when copying the arrays in a for loop, gcc wrongly assumed
213 // that n and o were aligned and used AVX instructions for me,
214 // which segfaulted. let's hope this doesn't get optimized too much.
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];
228 n->D_max = o->D_max;
229 n->offset = o->offset;
230 n->black = o->black;
231 n->gamma = o->gamma;
232 n->soft_clip = o->soft_clip;
233 n->exposure = o->exposure;
234
235 return 0;
236 }
237 return 1;
238}
239
242{
245
246 // keep WB_high even in B&W mode to apply sepia or warm tone look
247 // but premultiply it aheard with Dmax to spare one div per pixel
248 for(size_t c = 0; c < 4; c++) d->wb_high[c] = p->wb_high[c] / p->D_max;
249
250 for(size_t c = 0; c < 4; c++) d->offset[c] = p->wb_high[c] * p->offset * p->wb_low[c];
251
252 // ensure we use a monochrome Dmin for B&W film
253 if(p->film_stock == DT_FILMSTOCK_COLOR)
254 for(size_t c = 0; c < 4; c++) d->Dmin[c] = p->Dmin[c];
255 else if(p->film_stock == DT_FILMSTOCK_NB)
256 for(size_t c = 0; c < 4; c++) d->Dmin[c] = p->Dmin[0];
257
258 // arithmetic trick allowing to rewrite the pixel inversion as FMA
259 d->black = -p->exposure * (1.0f + p->black);
260
261 // highlights soft clip
262 d->soft_clip = p->soft_clip;
263 d->soft_clip_comp = 1.0f - p->soft_clip;
264
265 // copy
266 d->exposure = p->exposure;
267 d->gamma = p->gamma;
268}
269
270
272int process(struct dt_iop_module_t *const self, const dt_dev_pixelpipe_t *const pipe,
273 const dt_dev_pixelpipe_iop_t *const piece, const void *const restrict ivoid,
274 void *const restrict ovoid)
275{
276 const dt_iop_roi_t *const restrict roi_out = &piece->roi_out;
277 const dt_iop_negadoctor_data_t *const d = piece->data;
278 const float *const restrict in = (float *)ivoid;
279 float *const restrict out = (float *)ovoid;
280 __OMP_FOR_SIMD__(aligned(in, out:64) collapse(2))
281 for(size_t k = 0; k < (size_t)roi_out->height * roi_out->width * 4; k += 4)
282 {
283 for(size_t c = 0; c < 4; c++)
284 {
285 // Unpack vectors one by one with extra pragmas to be sure the compiler understands they can be vectorized
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);
291
292 // Convert transmission to density using Dmin as a fulcrum
293 const float density = - log10f(Dmin[c] / fmaxf(pix_in[c], THRESHOLD)); // threshold to -32 EV
294
295 // Correct density in log space
296 const float corrected_de = wb_high[c] * density + offset[c];
297
298 // Print density on paper : ((1 - 10^corrected_de + black) * exposure)^gamma rewritten for FMA
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); // note : this is always > 0
301
302 // Compress highlights. from https://lists.gnu.org/archive/html/openexr-devel/2005-03/msg00009.html
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
304 : print_gamma;
305 }
306 }
307
309 dt_iop_alpha_copy(ivoid, ovoid, roi_out->width, roi_out->height);
310 return 0;
311}
312
313
314#ifdef HAVE_OPENCL
315int process_cl(struct dt_iop_module_t *const self, const dt_dev_pixelpipe_t *const pipe,
316 const dt_dev_pixelpipe_iop_t *const piece, cl_mem dev_in, cl_mem dev_out)
317{
318 const dt_iop_roi_t *const restrict roi_in = &piece->roi_in;
321
322 cl_int err = -999;
323
324 const int devid = pipe->devid;
325 const int width = roi_in->width;
326 const int height = roi_in->height;
327
328 size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
329
330 dt_opencl_set_kernel_arg(devid, gd->kernel_negadoctor, 0, sizeof(cl_mem), (void *)&dev_in);
331 dt_opencl_set_kernel_arg(devid, gd->kernel_negadoctor, 1, sizeof(cl_mem), (void *)&dev_out);
332 dt_opencl_set_kernel_arg(devid, gd->kernel_negadoctor, 2, sizeof(int), (void *)&width);
333 dt_opencl_set_kernel_arg(devid, gd->kernel_negadoctor, 3, sizeof(int), (void *)&height);
334 dt_opencl_set_kernel_arg(devid, gd->kernel_negadoctor, 4, 4 * sizeof(float), (void *)&d->Dmin);
335 dt_opencl_set_kernel_arg(devid, gd->kernel_negadoctor, 5, 4 * sizeof(float), (void *)&d->wb_high);
336 dt_opencl_set_kernel_arg(devid, gd->kernel_negadoctor, 6, 4 * sizeof(float), (void *)&d->offset);
337 dt_opencl_set_kernel_arg(devid, gd->kernel_negadoctor, 7, sizeof(float), (void *)&d->exposure);
338 dt_opencl_set_kernel_arg(devid, gd->kernel_negadoctor, 8, sizeof(float), (void *)&d->black);
339 dt_opencl_set_kernel_arg(devid, gd->kernel_negadoctor, 9, sizeof(float), (void *)&d->gamma);
340 dt_opencl_set_kernel_arg(devid, gd->kernel_negadoctor, 10, sizeof(float), (void *)&d->soft_clip);
341 dt_opencl_set_kernel_arg(devid, gd->kernel_negadoctor, 11, sizeof(float), (void *)&d->soft_clip_comp);
342
343 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_negadoctor, sizes);
344 if(err != CL_SUCCESS) goto error;
345 return TRUE;
346
347error:
348 dt_print(DT_DEBUG_OPENCL, "[opencl_negadoctor] couldn't enqueue kernel! %d\n", err);
349 return FALSE;
350}
351#endif
352
353
355{
356 dt_iop_default_init(module);
357
358 dt_iop_negadoctor_params_t *d = module->default_params;
359
360 d->Dmin[0] = 1.00f;
361 d->Dmin[1] = 0.45f;
362 d->Dmin[2] = 0.25f;
363}
364
366{
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 },
371 .D_max = 1.6f,
372 .offset = -0.05f,
373 .gamma = 4.0f,
374 .soft_clip = 0.75f,
375 .exposure = 0.9245f,
376 .black = 0.0755f };
377
378
379 dt_gui_presets_add_generic(_("color film"), self->op,
380 self->version(), &tmp, sizeof(tmp), 1);
381
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 },
386 .D_max = 2.2f,
387 .offset = -0.05f,
388 .gamma = 5.0f,
389 .soft_clip = 0.75f,
390 .exposure = 1.f,
391 .black = 0.0755f };
392
393
394 dt_gui_presets_add_generic(_("black and white film"), self->op,
395 self->version(), &tmq, sizeof(tmq), 1);
396}
397
399{
402 if(IS_NULL_PTR(gd)) return;
403
404 module->data = gd;
405 const int program = 30; // negadoctor.cl, from programs.conf
406 gd->kernel_negadoctor = dt_opencl_create_kernel(program, "negadoctor");
407}
408
410{
411 dt_iop_negadoctor_global_data_t *gd = module->data;
413 dt_free(module->data);
414}
415
417{
419 piece->data_size = sizeof(dt_iop_negadoctor_data_t);
420}
421
423{
424 dt_free_align(piece->data);
425 piece->data = NULL;
426}
427
428
429/* Global GUI stuff */
430
432{
433 gtk_widget_set_visible(g->Dmin_G, state);
434 gtk_widget_set_visible(g->Dmin_B, state);
435}
436
437
439{
442
443 if(p->film_stock == DT_FILMSTOCK_NB)
444 {
445 // Hide color controls
447 dt_bauhaus_widget_set_label(g->Dmin_R, N_("D min"));
448 }
449 else if(p->film_stock == DT_FILMSTOCK_COLOR)
450 {
451 // Show color controls
453 dt_bauhaus_widget_set_label(g->Dmin_R, N_("D min red component"));
454 }
455 else
456 {
457 // We shouldn't be there
458 fprintf(stderr, "negadoctor film stock: undefined behaviour\n");
459 }
460}
461
462
464{
467
468 GdkRGBA color;
469 color.alpha = 1.0f;
470
471 if(p->film_stock == DT_FILMSTOCK_COLOR)
472 {
473 color.red = p->Dmin[0];
474 color.green = p->Dmin[1];
475 color.blue = p->Dmin[2];
476 }
477 else if(p->film_stock == DT_FILMSTOCK_NB)
478 {
479 color.red = color.green = color.blue = p->Dmin[0];
480 }
481
482 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(g->Dmin_picker), &color);
483}
484
485static void Dmin_picker_callback(GtkColorButton *widget, dt_iop_module_t *self)
486{
487 if(dt_gui_widgets_suppressed()) return;
490
492
493 GdkRGBA c;
494 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget), &c);
495 p->Dmin[0] = c.red;
496 p->Dmin[1] = c.green;
497 p->Dmin[2] = c.blue;
498
500 dt_bauhaus_slider_set(g->Dmin_R, p->Dmin[0]);
501 dt_bauhaus_slider_set(g->Dmin_G, p->Dmin[1]);
502 dt_bauhaus_slider_set(g->Dmin_B, p->Dmin[2]);
504
505 Dmin_picker_update(self);
507 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
508}
509
511{
514
515 GdkRGBA color;
516 color.alpha = 1.0f;
517
518 dt_aligned_pixel_t WB_low_invert;
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;
522
523 color.red = WB_low_invert[0];
524 color.green = WB_low_invert[1];
525 color.blue = WB_low_invert[2];
526
527 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(g->WB_low_picker), &color);
528}
529
530static void WB_low_picker_callback(GtkColorButton *widget, dt_iop_module_t *self)
531{
532 if(dt_gui_widgets_suppressed()) return;
535
537
538 GdkRGBA c;
539 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget), &c);
540
541 dt_aligned_pixel_t RGB = { 2.0f - c.red, 2.0f - c.green, 2.0f - c.blue };
542
543 float RGB_min = v_minf(RGB);
544 for(size_t k = 0; k < 3; k++) p->wb_low[k] = RGB[k] / RGB_min;
545
547 dt_bauhaus_slider_set(g->wb_low_R, p->wb_low[0]);
548 dt_bauhaus_slider_set(g->wb_low_G, p->wb_low[1]);
549 dt_bauhaus_slider_set(g->wb_low_B, p->wb_low[2]);
551
554 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
555}
556
557
559{
562
563 GdkRGBA color;
564 color.alpha = 1.0f;
565
566 dt_aligned_pixel_t WB_high_invert;
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;
570
571 color.red = WB_high_invert[0];
572 color.green = WB_high_invert[1];
573 color.blue = WB_high_invert[2];
574
575 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(g->WB_high_picker), &color);
576}
577
578static void WB_high_picker_callback(GtkColorButton *widget, dt_iop_module_t *self)
579{
580 if(dt_gui_widgets_suppressed()) return;
583
585
586 GdkRGBA c;
587 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget), &c);
588
589 dt_aligned_pixel_t RGB = { 2.0f - c.red, 2.0f - c.green, 2.0f - c.blue };
590 float RGB_min = v_minf(RGB);
591 for(size_t k = 0; k < 3; k++) p->wb_high[k] = RGB[k] / RGB_min;
592
594 dt_bauhaus_slider_set(g->wb_high_R, p->wb_high[0]);
595 dt_bauhaus_slider_set(g->wb_high_G, p->wb_high[1]);
596 dt_bauhaus_slider_set(g->wb_high_B, p->wb_high[2]);
598
601 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
602}
603
604static void Wb_low_norm_callback(GtkColorButton *widget, dt_iop_module_t *self)
605{
606 if(dt_gui_widgets_suppressed()) return;
607
610
611
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;
615
616
618 dt_bauhaus_slider_set(g->wb_low_R, p->wb_low[0]);
619 dt_bauhaus_slider_set(g->wb_low_G, p->wb_low[1]);
620 dt_bauhaus_slider_set(g->wb_low_B, p->wb_low[2]);
622
625 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
626}
627
628static void Wb_high_norm_callback(GtkColorButton *widget, dt_iop_module_t *self)
629{
630 if(dt_gui_widgets_suppressed()) return;
631
634
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;
638
639
641 dt_bauhaus_slider_set(g->wb_high_R, p->wb_high[0]);
642 dt_bauhaus_slider_set(g->wb_high_G, p->wb_high[1]);
643 dt_bauhaus_slider_set(g->wb_high_B, p->wb_high[2]);
645
648 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
649}
650
651/* Color pickers auto-tuners */
652
653// measure Dmin from the film edges first
655{
656 if(dt_gui_widgets_suppressed()) return;
659
660 for(int k = 0; k < 4; k++) p->Dmin[k] = self->picked_color[k];
661
663 dt_bauhaus_slider_set(g->Dmin_R, p->Dmin[0]);
664 dt_bauhaus_slider_set(g->Dmin_G, p->Dmin[1]);
665 dt_bauhaus_slider_set(g->Dmin_B, p->Dmin[2]);
667
668 Dmin_picker_update(self);
670 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
671}
672
673// from Dmin, find out the range of density values of the film and compute Dmax
675{
676 if(dt_gui_widgets_suppressed()) return;
679
681 for(int c = 0; c < 3; c++)
682 {
683 RGB[c] = log10f(p->Dmin[c] / fmaxf(self->picked_color_min[c], THRESHOLD));
684 }
685
686 // Take the max(RGB) for safety. Big values unclip whites
687 p->D_max = v_maxf(RGB);
688
690 dt_bauhaus_slider_set(g->D_max, p->D_max);
692
694 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
695}
696
697// from Dmax, compute the offset so the range of density is rescaled between [0; 1]
699{
700 if(dt_gui_widgets_suppressed()) return;
703
705 for(int c = 0; c < 3; c++)
706 RGB[c] = log10f(p->Dmin[c] / fmaxf(self->picked_color_max[c], THRESHOLD)) / p->D_max;
707
708 // Take the min(RGB) for safety. Negative values unclip blacks
709 p->offset = v_minf(RGB);
710
712 dt_bauhaus_slider_set(g->offset, p->offset);
714
716 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
717}
718
719// from Dmax and offset, compute the white balance correction as multipliers of the offset
720// such that offset x wb[c] make black monochrome
722{
723 if(dt_gui_widgets_suppressed()) return;
726
727 dt_aligned_pixel_t RGB_min;
728 for(int c = 0; c < 3; c++)
729 RGB_min[c] = log10f(p->Dmin[c] / fmaxf(self->picked_color[c], THRESHOLD)) / p->D_max;
730
731 const float RGB_v_min = v_minf(RGB_min); // warning: can be negative
732 for(int c = 0; c < 3; c++) p->wb_low[c] = RGB_v_min / RGB_min[c];
733
735 dt_bauhaus_slider_set(g->wb_low_R, p->wb_low[0]);
736 dt_bauhaus_slider_set(g->wb_low_G, p->wb_low[1]);
737 dt_bauhaus_slider_set(g->wb_low_B, p->wb_low[2]);
739
742 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
743}
744
745// from Dmax, offset and white balance multipliers, compute the white balance of the illuminant as multipliers of 1/Dmax
746// such that WB[c] / Dmax make white monochrome
748{
749 if(dt_gui_widgets_suppressed()) return;
752
753 dt_aligned_pixel_t RGB_min;
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));
756
757 const float RGB_v_min = v_minf(RGB_min); // warning : must be positive
758 for(int c = 0; c < 3; c++) p->wb_high[c] = RGB_min[c] / RGB_v_min;
759
761 dt_bauhaus_slider_set(g->wb_high_R, p->wb_high[0]);
762 dt_bauhaus_slider_set(g->wb_high_G, p->wb_high[1]);
763 dt_bauhaus_slider_set(g->wb_high_B, p->wb_high[2]);
765
768 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
769}
770
771// from Dmax, offset and both white balances, compute the print black adjustment
772// such that the printed values range from 0 to + infinity
774{
775 if(dt_gui_widgets_suppressed()) return;
778
780 for(int c = 0; c < 3; c++)
781 {
782 RGB[c] = -log10f(p->Dmin[c] / fmaxf(self->picked_color_max[c], THRESHOLD));
783 RGB[c] *= p->wb_high[c] / p->D_max;
784 RGB[c] += p->wb_low[c] * p->offset * p->wb_high[c];
785 RGB[c] = 0.1f - (1.0f - fast_exp10f(RGB[c])); // actually, remap between -3.32 EV and infinity for safety because gamma comes later
786 }
787 p->black = v_maxf(RGB);
788
790 dt_bauhaus_slider_set(g->black, p->black);
792
794 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
795}
796
797// from Dmax, offset, both white balances, and printblack, compute the print exposure adjustment as a scaling factor
798// such that the printed values range from 0 to 1
800{
801 if(dt_gui_widgets_suppressed()) return;
804
806 for(int c = 0; c < 3; c++)
807 {
808 RGB[c] = -log10f(p->Dmin[c] / fmaxf(self->picked_color_min[c], THRESHOLD));
809 RGB[c] *= p->wb_high[c] / p->D_max;
810 RGB[c] += p->wb_low[c] * p->offset;
811 RGB[c] = 0.96f / (1.0f - fast_exp10f(RGB[c]) + p->black); // actually, remap in [0; 0.96] for safety
812 }
813 p->exposure = v_minf(RGB);
814
816 dt_bauhaus_slider_set(g->exposure, log2f(p->exposure));
818
820 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
821}
822
823
825{
826 (void)pipe;
827 (void)piece;
828 if(dt_gui_widgets_suppressed()) return;
830
831 if (picker == g->Dmin_sampler)
832 apply_auto_Dmin(self);
833 else if(picker == g->WB_high_sampler)
834 apply_auto_WB_high(self);
835 else if(picker == g->offset)
836 apply_auto_offset(self);
837 else if(picker == g->D_max)
838 apply_auto_Dmax(self);
839 else if(picker == g->WB_low_sampler)
840 apply_auto_WB_low(self);
841 else if(picker == g->exposure)
843 else if(picker == g->black)
844 apply_auto_black(self);
845 else
846 fprintf(stderr, "[negadoctor] unknown color picker\n");
847}
848
850{
852
853 g->notebook = dt_ui_notebook_new();
854 // Each page holds its own pickers (Dmin, D_max, offset, white balance samplers,
855 // black/exposure); reset any active one when the page it lives on is switched
856 // away from.
857 dt_ui_notebook_set_picker_owner(g->notebook, self);
858
859 // Page FILM PROPERTIES
860 GtkWidget *page1 = self->gui->widget = dt_ui_notebook_page(g->notebook, N_("film properties"), NULL);
861
862 // Dmin
863
864 gtk_box_pack_start(GTK_BOX(page1), dt_ui_section_label_new(_("color of the film base")), FALSE, FALSE, 0);
865
866 GtkWidget *row1 = GTK_WIDGET(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING));
867
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);
872 g_signal_connect(G_OBJECT(g->Dmin_picker), "color-set", G_CALLBACK(Dmin_picker_callback), self);
873
874 g->Dmin_sampler = dt_color_picker_new(self, DT_COLOR_PICKER_AREA, row1);
875 gtk_widget_set_tooltip_text(g->Dmin_sampler , _("pick color of film material from image"));
876
877 gtk_box_pack_start(GTK_BOX(page1), GTK_WIDGET(row1), FALSE, FALSE, 0);
878
879 g->Dmin_R = dt_bauhaus_slider_from_params(self, "Dmin[0]");
881 dt_bauhaus_slider_set_format(g->Dmin_R, "%");
882 dt_bauhaus_slider_set_factor(g->Dmin_R, 100);
883 dt_bauhaus_widget_set_label(g->Dmin_R, N_("D min red component"));
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."));
888
889 g->Dmin_G = dt_bauhaus_slider_from_params(self, "Dmin[1]");
891 dt_bauhaus_slider_set_format(g->Dmin_G, "%");
892 dt_bauhaus_slider_set_factor(g->Dmin_G, 100);
893 dt_bauhaus_widget_set_label(g->Dmin_G, N_("D min green component"));
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."));
898
899 g->Dmin_B = dt_bauhaus_slider_from_params(self, "Dmin[2]");
901 dt_bauhaus_slider_set_format(g->Dmin_B, "%");
902 dt_bauhaus_slider_set_factor(g->Dmin_B, 100);
903 dt_bauhaus_widget_set_label(g->Dmin_B, N_("D min blue component"));
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."));
908
909 // D max and scanner bias
910
911 gtk_box_pack_start(GTK_BOX(page1), dt_ui_section_label_new(_("dynamic range of the film")), FALSE, FALSE, 0);
912
914 dt_bauhaus_slider_set_format(g->D_max, " dB");
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."));
918
919 gtk_box_pack_start(GTK_BOX(page1), dt_ui_section_label_new(_("scanner exposure settings")), FALSE, FALSE, 0);
920
922 dt_bauhaus_slider_set_format(g->offset, " dB");
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."));
925
926 // Page CORRECTIONS
927 GtkWidget *page2 = self->gui->widget = dt_ui_notebook_page(g->notebook, N_("corrections"), NULL);
928
929 // WB shadows
930 gtk_box_pack_start(GTK_BOX(page2), dt_ui_section_label_new(_("shadows color cast")), FALSE, FALSE, 0);
931
932 GtkWidget *row3 = GTK_WIDGET(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING));
933
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);
938 g_signal_connect(G_OBJECT(g->WB_low_picker), "color-set", G_CALLBACK(WB_low_picker_callback), self);
939
940 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);
941 gtk_box_pack_start(GTK_BOX(row3), GTK_WIDGET(g->WB_low_norm), FALSE, FALSE, 0);
942
943 g->WB_low_sampler = dt_color_picker_new(self, DT_COLOR_PICKER_AREA, row3);
944 gtk_widget_set_tooltip_text(g->WB_low_sampler, _("pick shadows color from image"));
945
946 gtk_box_pack_start(GTK_BOX(page2), GTK_WIDGET(row3), FALSE, FALSE, 0);
947
948 g->wb_low_R = dt_bauhaus_slider_from_params(self, "wb_low[0]");
949 dt_bauhaus_widget_set_label(g->wb_low_R, N_("shadows red offset"));
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."));
954
955 g->wb_low_G = dt_bauhaus_slider_from_params(self, "wb_low[1]");
956 dt_bauhaus_widget_set_label(g->wb_low_G, N_("shadows green offset"));
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."));
961
962 g->wb_low_B = dt_bauhaus_slider_from_params(self, "wb_low[2]");
963 dt_bauhaus_widget_set_label(g->wb_low_B, N_("shadows blue offset"));
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."));
968
969 // WB highlights
970 gtk_box_pack_start(GTK_BOX(page2), dt_ui_section_label_new(_("highlights white balance")), FALSE, FALSE, 0);
971
972 GtkWidget *row2 = GTK_WIDGET(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING));
973
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);
978 g_signal_connect(G_OBJECT(g->WB_high_picker), "color-set", G_CALLBACK(WB_high_picker_callback), self);
979
980 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);
981 gtk_box_pack_start(GTK_BOX(row2), GTK_WIDGET(g->WB_high_norm), FALSE, FALSE, 0);
982
983 g->WB_high_sampler = dt_color_picker_new(self, DT_COLOR_PICKER_AREA, row2);
984 gtk_widget_set_tooltip_text(g->WB_high_sampler , _("pick illuminant color from image"));
985
986 gtk_box_pack_start(GTK_BOX(page2), GTK_WIDGET(row2), FALSE, FALSE, 0);
987
988 g->wb_high_R = dt_bauhaus_slider_from_params(self, "wb_high[0]");
989 dt_bauhaus_widget_set_label(g->wb_high_R, N_("illuminant red gain"));
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."));
994
995 g->wb_high_G = dt_bauhaus_slider_from_params(self, "wb_high[1]");
996 dt_bauhaus_widget_set_label(g->wb_high_G, N_("illuminant green gain"));
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."));
1001
1002 g->wb_high_B = dt_bauhaus_slider_from_params(self, "wb_high[2]");
1003 dt_bauhaus_widget_set_label(g->wb_high_B, N_("illuminant blue gain"));
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."));
1008
1009 // Page PRINT PROPERTIES
1010 GtkWidget *page3 = self->gui->widget = dt_ui_notebook_page(g->notebook, N_("print properties"), NULL);
1011
1012 // print corrections
1013 gtk_box_pack_start(GTK_BOX(page3), dt_ui_section_label_new(_("virtual paper properties")), FALSE, FALSE, 0);
1014
1017 dt_bauhaus_slider_set_factor(g->black, 100);
1018 dt_bauhaus_slider_set_format(g->black, "%");
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."));
1021
1022 g->gamma = dt_bauhaus_slider_from_params(self, "gamma");
1023 dt_bauhaus_widget_set_label(g->gamma, N_("paper grade (gamma)"));
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."));
1027
1028 g->soft_clip = dt_bauhaus_slider_from_params(self, "soft_clip");
1029 dt_bauhaus_slider_set_factor(g->soft_clip, 100);
1030 dt_bauhaus_slider_set_digits(g->soft_clip, 4);
1031 dt_bauhaus_slider_set_format(g->soft_clip, "%");
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."));
1035
1036 gtk_box_pack_start(GTK_BOX(page3), dt_ui_section_label_new(_("virtual print emulation")), FALSE, FALSE, 0);
1037
1038 g->exposure = dt_color_picker_new(self, DT_COLOR_PICKER_AREA, dt_bauhaus_slider_from_params(self, "exposure"));
1039 dt_bauhaus_slider_set_soft_min(g->exposure, -1.0);
1040 dt_bauhaus_slider_set_soft_max(g->exposure, 1.0);
1041 dt_bauhaus_slider_set_default(g->exposure, 0.0);
1042 dt_bauhaus_slider_set_format(g->exposure, _(" EV"));
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."));
1045
1046 // start building top level widget
1047 self->gui->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1048
1049 // Film emulsion
1050 g->film_stock = dt_bauhaus_combobox_from_params(self, "film_stock");
1051 gtk_widget_set_tooltip_text(g->film_stock, _("toggle on or off the color controls"));
1052
1053 gtk_box_pack_start(GTK_BOX(self->gui->widget), GTK_WIDGET(g->notebook), FALSE, FALSE, 0);
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);
1057
1058}
1059
1061{
1063 if(!IS_NULL_PTR(g) && GTK_IS_NOTEBOOK(g->notebook))
1064 dt_conf_set_int("plugins/darkroom/negadoctor/gui_page", gtk_notebook_get_current_page(g->notebook));
1066}
1067
1068void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
1069{
1072 if(IS_NULL_PTR(w) || w == g->film_stock)
1073 {
1075 Dmin_picker_update(self);
1076 }
1077 else if(w == g->Dmin_R && p->film_stock == DT_FILMSTOCK_NB)
1078 {
1079 dt_bauhaus_slider_set(g->Dmin_G, p->Dmin[0]);
1080 dt_bauhaus_slider_set(g->Dmin_B, p->Dmin[0]);
1081 }
1082 else if(w == g->Dmin_R || w == g->Dmin_G || w == g->Dmin_B)
1083 {
1084 Dmin_picker_update(self);
1085 }
1086 else if(w == g->exposure)
1087 {
1088 p->exposure = powf(2.0f, p->exposure);
1089 }
1090
1091 if(IS_NULL_PTR(w) || w == g->wb_high_R || w == g->wb_high_G || w == g->wb_high_B)
1092 {
1094 }
1095
1096 if(IS_NULL_PTR(w) || w == g->wb_low_R || w == g->wb_low_G || w == g->wb_low_B)
1097 {
1099 }
1100}
1101
1102
1104{
1105 // let gui slider match current parameters:
1108
1110
1111
1112 dt_bauhaus_slider_set(g->exposure, log2f(p->exposure)); // warning: GUI is in EV
1113
1114 // Update custom stuff
1115 gui_changed(self, NULL, NULL);
1116}
1117
1119{
1121}
1122// clang-format off
1123// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1124// vim: shiftwidth=2 expandtab tabstop=2 cindent
1125// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1126// 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:3343
void dt_bauhaus_slider_set_default(GtkWidget *widget, float def)
Definition bauhaus.c:1491
void dt_bauhaus_slider_set_soft_max(GtkWidget *widget, float val)
Definition bauhaus.c:1474
void dt_bauhaus_slider_set_soft_min(GtkWidget *widget, float val)
Definition bauhaus.c:1457
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3331
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
Definition bauhaus.c:1504
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
Definition bauhaus.c:3407
void dt_bauhaus_slider_set_factor(GtkWidget *widget, float factor)
Definition bauhaus.c:3423
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
@ 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)
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
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...
Definition control.c:969
#define dt_dev_add_history_item(dev, module, enable, redraw)
void dt_iop_params_t
Definition dev_history.h:43
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
Definition develop.h:123
GdkRGBA color[]
Definition geotagging.c:541
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)
Definition imageop.c:308
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:1893
@ IOP_FLAGS_INCLUDE_IN_STYLES
Definition imageop.h:185
@ IOP_FLAGS_ALLOW_TILING
Definition imageop.h:188
@ IOP_FLAGS_ONE_INSTANCE
Definition imageop.h:191
@ IOP_GROUP_FILM
Definition imageop.h:157
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)
#define IOP_GUI_FREE
Definition imageop_gui.h:96
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...
Definition imageop_gui.h:81
#define IOP_GUI_ALLOC(module)
Definition imageop_gui.h:93
void *const ovoid
GtkWidget * dt_ui_section_label_new(const gchar *str)
Definition label.c:114
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:1336
@ DT_DEBUG_OPENCL
Definition logging.h:57
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...
Definition macros.h:96
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
Definition mem_alloc.h:214
static void * dt_calloc_align(size_t size)
dt_alloc_align() followed by a zero fill.
Definition mem_alloc.h:225
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
DT_MODULE() for a module whose params struct is introspected.
void init(dt_iop_module_t *module)
Definition negadoctor.c:354
const char ** description(struct dt_iop_module_t *self)
Definition negadoctor.c:161
static void WB_high_picker_update(dt_iop_module_t *self)
Definition negadoctor.c:558
int default_group()
Definition negadoctor.c:176
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:530
static void Wb_high_norm_callback(GtkColorButton *widget, dt_iop_module_t *self)
Definition negadoctor.c:628
static void toggle_stock_controls(dt_iop_module_t *const self)
Definition negadoctor.c:438
static void WB_high_picker_callback(GtkColorButton *widget, dt_iop_module_t *self)
Definition negadoctor.c:578
__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:272
#define THRESHOLD
Definition negadoctor.c:78
static void apply_auto_WB_high(dt_iop_module_t *self)
Definition negadoctor.c:747
dt_iop_negadoctor_filmstock_t
Definition negadoctor.c:85
@ DT_FILMSTOCK_NB
Definition negadoctor.c:87
@ DT_FILMSTOCK_COLOR
Definition negadoctor.c:88
static void apply_auto_WB_low(dt_iop_module_t *self)
Definition negadoctor.c:721
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:315
const char * aliases()
Definition negadoctor.c:156
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition negadoctor.c:416
const char * name()
Definition negadoctor.c:151
void gui_init(dt_iop_module_t *self)
Definition negadoctor.c:849
static void setup_color_variables(dt_iop_negadoctor_gui_data_t *const g, const gint state)
Definition negadoctor.c:431
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:240
void cleanup_global(dt_iop_module_so_t *module)
Definition negadoctor.c:409
static void apply_auto_exposure(dt_iop_module_t *self)
Definition negadoctor.c:799
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition negadoctor.c:182
int flags()
Definition negadoctor.c:170
static void apply_auto_Dmin(dt_iop_module_t *self)
Definition negadoctor.c:654
void gui_cleanup(struct dt_iop_module_t *self)
static void Dmin_picker_update(dt_iop_module_t *self)
Definition negadoctor.c:463
void init_presets(dt_iop_module_so_t *self)
Definition negadoctor.c:365
static void WB_low_picker_update(dt_iop_module_t *self)
Definition negadoctor.c:510
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition negadoctor.c:422
static void Wb_low_norm_callback(GtkColorButton *widget, dt_iop_module_t *self)
Definition negadoctor.c:604
static void apply_auto_offset(dt_iop_module_t *self)
Definition negadoctor.c:698
static void apply_auto_black(dt_iop_module_t *self)
Definition negadoctor.c:773
void init_global(dt_iop_module_so_t *module)
Definition negadoctor.c:398
static void apply_auto_Dmax(dt_iop_module_t *self)
Definition negadoctor.c:674
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:824
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:187
static void Dmin_picker_callback(GtkColorButton *widget, dt_iop_module_t *self)
Definition negadoctor.c:485
GtkWidget * dt_ui_notebook_page(GtkNotebook *notebook, const char *text, const char *tooltip)
Definition notebook.c:88
GtkNotebook * dt_ui_notebook_new()
Definition notebook.c:83
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...
Definition notebook.c:118
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
Definition opencl.c:2554
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2448
void dt_opencl_free_kernel(const int kernel)
Definition opencl.c:2491
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:2545
#define ROUNDUPDHT(a, b)
Definition opencl.h:86
#define ROUNDUPDWD(a, b)
Definition opencl.h:85
#define __OMP_FOR_SIMD__(...)
Definition openmp.h:97
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
Definition simd.h:190
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
Definition simd.h:53
static const dt_aligned_pixel_simd_t const dt_aligned_pixel_simd_t const dt_aligned_pixel_simd_t row2
Definition simd.h:191
const float uint32_t state[4]
struct dt_iop_module_t *void * data
GtkWidget * widget
Definition imageop_gui.h:47
dt_dev_operation_t op
Definition imageop.h:235
dt_iop_global_data_t * data
Definition imageop.h:238
dt_iop_params_t * default_params
Definition imageop.h:333
struct dt_iop_module_gui_t * gui
Definition imageop.h:346
struct dt_develop_t * dev
Definition imageop.h:311
dt_iop_global_data_t * global_data
Definition imageop.h:337
dt_aligned_pixel_t picked_color_min
Definition imageop.h:287
dt_aligned_pixel_t picked_color_max
Definition imageop.h:287
dt_aligned_pixel_t picked_color
Definition imageop.h:287
dt_iop_params_t * params
Definition imageop.h:333
dt_aligned_pixel_t wb_high
Definition negadoctor.c:119
dt_aligned_pixel_t offset
Definition negadoctor.c:120
dt_aligned_pixel_t Dmin
Definition negadoctor.c:118
dt_iop_negadoctor_filmstock_t film_stock
Definition negadoctor.c:94
Region of interest passed through the pixelpipe.
Definition format.h:49
#define __DT_CLONE_TARGETS__
gboolean dt_gui_widgets_suppressed(void)
#define dt_gui_freeze_begin()
#define dt_gui_freeze_end()
#define DT_GUI_BOX_SPACING