Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
channelmixer.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2010-2011 Bruce Guenter.
4 Copyright (C) 2010-2012 Henrik Andersson.
5 Copyright (C) 2010-2013, 2016 johannes hanika.
6 Copyright (C) 2010 José Carlos García Sogo.
7 Copyright (C) 2010 Milan Knížek.
8 Copyright (C) 2010 Stuart Henderson.
9 Copyright (C) 2011 Antony Dovgal.
10 Copyright (C) 2011 Brian Teague.
11 Copyright (C) 2011 Olivier Tribout.
12 Copyright (C) 2011 Robert Bieber.
13 Copyright (C) 2011-2014, 2016, 2019 Tobias Ellinghaus.
14 Copyright (C) 2012 Alexandre Prokoudine.
15 Copyright (C) 2012 Richard Wonka.
16 Copyright (C) 2012, 2014, 2016-2017 Ulrich Pegelow.
17 Copyright (C) 2013 hal.
18 Copyright (C) 2013-2016 Roman Lebedev.
19 Copyright (C) 2013 Simon Spannagel.
20 Copyright (C) 2013 Thomas Pryds.
21 Copyright (C) 2015 Pedro Côrte-Real.
22 Copyright (C) 2017 Heiko Bauke.
23 Copyright (C) 2017 Matthieu Moy.
24 Copyright (C) 2018, 2020, 2022-2023, 2025-2026 Aurélien PIERRE.
25 Copyright (C) 2018 Edgardo Hoszowski.
26 Copyright (C) 2018 Maurizio Paglia.
27 Copyright (C) 2018-2022 Pascal Obry.
28 Copyright (C) 2018 rawfiner.
29 Copyright (C) 2019 Andreas Schneider.
30 Copyright (C) 2019 Diederik ter Rahe.
31 Copyright (C) 2020 Aldric Renaudin.
32 Copyright (C) 2020-2021 Chris Elston.
33 Copyright (C) 2020, 2022 Diederik Ter Rahe.
34 Copyright (C) 2020 Harold le Clément de Saint-Marcq.
35 Copyright (C) 2020-2021 Ralf Brown.
36 Copyright (C) 2022 Hanno Schwalm.
37 Copyright (C) 2022 Martin Bařinka.
38 Copyright (C) 2022 Philipp Lutz.
39
40 darktable is free software: you can redistribute it and/or modify
41 it under the terms of the GNU General Public License as published by
42 the Free Software Foundation, either version 3 of the License, or
43 (at your option) any later version.
44
45 darktable is distributed in the hope that it will be useful,
46 but WITHOUT ANY WARRANTY; without even the implied warranty of
47 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
48 GNU General Public License for more details.
49
50 You should have received a copy of the GNU General Public License
51 along with darktable. If not, see <http://www.gnu.org/licenses/>.
52*/
53#ifdef HAVE_CONFIG_H
54#include "config.h"
55#endif
56#include "develop/imageop_gui.h"
57#include "widgets/bauhaus.h"
59#include "develop/develop.h"
60#include "system/openmp.h"
62#include "system/mem_alloc.h"
63#include "system/simd.h"
65#include "database/database.h"
66#include "develop/imageop.h"
68#include "math/openmp_maths.h"
69
70#include "gui/presets.h"
71#include "iop/iop_api.h"
72
73#include <assert.h>
74#include <gtk/gtk.h>
75#include <inttypes.h>
76#include <math.h>
77#include <stdlib.h>
78#include <string.h>
79
94
114
120
122{
124 float red[CHANNEL_SIZE]; // $MIN: -1.0 $MAX: 1.0
126 float green[CHANNEL_SIZE]; // $MIN: -1.0 $MAX: 1.0
128 float blue[CHANNEL_SIZE]; // $MIN: -1.0 $MAX: 1.0
132
139
147
154
155const char *name()
156{
157 return _("channel mixer");
158}
159
160const char *deprecated_msg()
161{
162 return _("this module is deprecated. please use the color calibration module instead.");
163}
164
165const char **description(struct dt_iop_module_t *self)
166{
167 return dt_iop_set_description(self, _("perform color space corrections\n"
168 "such as white balance, channels mixing\n"
169 "and conversions to monochrome emulating film"),
170 _("corrective or creative"),
171 _("linear, RGB, display-referred"),
172 _("linear, RGB"),
173 _("linear, RGB, display-referred"));
174}
175
176
181
183{
184 return IOP_GROUP_COLOR;
185}
186
188{
189 return IOP_CS_RGB;
190}
191
192int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params,
193 const int new_version)
194{
195 if(old_version == 1 && new_version == 2)
196 {
197 typedef struct dt_iop_channelmixer_params_v1_t
198 {
199 float red[7];
200 float green[7];
201 float blue[7];
202 } dt_iop_channelmixer_params_v1_t;
203
204 const dt_iop_channelmixer_params_v1_t *old = (dt_iop_channelmixer_params_v1_t *)old_params;
207
208 *new = *defaults; // start with a fresh copy of default parameters
210
211 // copy gray mixing parameters
212 new->red[CHANNEL_GRAY] = old->red[6];
213 new->green[CHANNEL_GRAY] = old->green[6];
214 new->blue[CHANNEL_GRAY] = old->blue[6];
215
216 // version 1 does not use RGB mixing when gray is enabled
217 if(new->red[CHANNEL_GRAY] == 0.0f && new->green[CHANNEL_GRAY] == 0.0f && new->blue[CHANNEL_GRAY] == 0.0f)
218 {
219 for(int i = 0; i < 3; i++)
220 {
221 new->red[CHANNEL_RED + i] = old->red[3 + i];
222 new->green[CHANNEL_RED + i] = old->green[3 + i];
223 new->blue[CHANNEL_RED + i] = old->blue[3 + i];
224 }
225 }
226
227 // copy HSL mixing parameters
228 for(int i = 0; i < 3; i++)
229 {
230 new->red[i] = old->red[i];
231 new->green[i] = old->green[i];
232 new->blue[i] = old->blue[i];
233 }
234 return 0;
235 }
236 return 1;
237}
238
240static void process_hsl_v1(const dt_dev_pixelpipe_iop_t *piece, const float *const restrict in,
241 float *const restrict out, const dt_iop_roi_t *const roi_out)
242{
244 const float *const restrict hsl_matrix = data->hsl_matrix;
245 const float *const restrict rgb_matrix = data->rgb_matrix;
246 const int ch = piece->dsc_in.channels;
247 const size_t pixel_count = (size_t)ch * roi_out->width * roi_out->height;
249 for(size_t k = 0; k < pixel_count; k += ch)
250 {
251 float h, s, l, hmix, smix, lmix;
253
254 // Calculate the HSL mix
255 hmix = clamp_simd(in[k + 0] * hsl_matrix[0]) + (in[k + 1] * hsl_matrix[1]) + (in[k + 2] * hsl_matrix[2]);
256 smix = clamp_simd(in[k + 0] * hsl_matrix[3]) + (in[k + 1] * hsl_matrix[4]) + (in[k + 2] * hsl_matrix[5]);
257 lmix = clamp_simd(in[k + 0] * hsl_matrix[6]) + (in[k + 1] * hsl_matrix[7]) + (in[k + 2] * hsl_matrix[8]);
258
259 // If HSL mix is used apply to out[]
260 if(hmix != 0.0f || smix != 0.0f || lmix != 0.0f)
261 {
262 // mix into HSL output channels
263 rgb2hsl(&(in[k]), &h, &s, &l);
264 h = (hmix != 0.0f) ? hmix : h;
265 s = (smix != 0.0f) ? smix : s;
266 l = (lmix != 0.0f) ? lmix : l;
267 hsl2rgb(rgb, h, s, l);
268 }
269 else // no HSL copy in[] to out[]
270 {
271 for_each_channel(c,aligned(rgb,in)) rgb[c] = in[k + c];
272 }
273
274 // Calculate RGB mix
275 for(int i = 0, j = 0; i < 3; i++, j += 3)
276 {
277 out[k + i] = clamp_simd(rgb_matrix[j + 0] * rgb[0]
278 + rgb_matrix[j + 1] * rgb[1]
279 + rgb_matrix[j + 2] * rgb[2]);
280 }
281 }
282}
283
285static void process_hsl_v2(const dt_dev_pixelpipe_iop_t *piece, const float *const restrict in,
286 float *const restrict out, const dt_iop_roi_t *const roi_out)
287{
289 const float *const restrict hsl_matrix = data->hsl_matrix;
290 const float *const restrict rgb_matrix = data->rgb_matrix;
291 const int ch = piece->dsc_in.channels;
292 const size_t pixel_count = (size_t)ch * roi_out->width * roi_out->height;
294 for(size_t k = 0; k < pixel_count; k += ch)
295 {
296 dt_aligned_pixel_t rgb = { in[k], in[k + 1], in[k + 2] };
297
298 dt_aligned_pixel_t hsl_mix;
299 for(int i = 0, j = 0; i < 3; i++, j += 3)
300 {
301 hsl_mix[i] = clamp_simd(hsl_matrix[j + 0] * rgb[0]
302 + hsl_matrix[j + 1] * rgb[1]
303 + hsl_matrix[j + 2] * rgb[2]);
304 }
305
306 // If HSL mix is used apply to out[]
307 if(hsl_mix[0] != 0.0 || hsl_mix[1] != 0.0 || hsl_mix[2] != 0.0)
308 {
310 // rgb2hsl expects all values to be clipped
312 {
313 rgb[c] = clamp_simd(rgb[c]);
314 }
315 // mix into HSL output channels
316 rgb2hsl(rgb, &hsl[0], &hsl[1], &hsl[2]);
317 for(int i = 0; i < 3; i++)
318 {
319 hsl[i] = (hsl_mix[i] != 0.0f) ? hsl_mix[i] : hsl[i];
320 }
321 hsl2rgb(rgb, hsl[0], hsl[1], hsl[2]);
322 }
323
324 // Calculate RGB mix
325 for(int i = 0, j = 0; i < 3; i++, j += 3)
326 {
327 out[k + i] = fmaxf(rgb_matrix[j + 0] * rgb[0]
328 + rgb_matrix[j + 1] * rgb[1]
329 + rgb_matrix[j + 2] * rgb[2], 0.0f);
330 }
331 }
332}
333
335static void process_rgb(const dt_dev_pixelpipe_iop_t *piece, const float *const restrict in,
336 float *const restrict out, const dt_iop_roi_t *const roi_out)
337{
339 const float *const restrict rgb_matrix = data->rgb_matrix;
340 const int ch = piece->dsc_in.channels;
341 const size_t pixel_count = (size_t)ch * roi_out->width * roi_out->height;
343 for(size_t k = 0; k < pixel_count; k += ch)
344 {
345 for(int i = 0, j = 0; i < 3; i++, j += 3)
346 {
347 out[k + i] = fmaxf(rgb_matrix[j + 0] * in[k + 0]
348 + rgb_matrix[j + 1] * in[k + 1]
349 + rgb_matrix[j + 2] * in[k + 2], 0.0f);
350 }
351 }
352}
353
355static void process_gray(const dt_dev_pixelpipe_iop_t *piece, const float *const restrict in,
356 float *const restrict out, const dt_iop_roi_t *const roi_out)
357{
359 const float *const restrict rgb_matrix = data->rgb_matrix;
360 const int ch = piece->dsc_in.channels;
361 const size_t pixel_count = (size_t)ch * roi_out->width * roi_out->height;
363 for(size_t k = 0; k < pixel_count; k += ch)
364 {
365 float gray = fmaxf(rgb_matrix[0] * in[k + 0]
366 + rgb_matrix[1] * in[k + 1]
367 + rgb_matrix[2] * in[k + 2], 0.0f);
368 out[k + 0] = gray;
369 out[k + 1] = gray;
370 out[k + 2] = gray;
371 }
372}
373
374int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
375 void *const ovoid)
376{
377 const dt_iop_roi_t *const roi_out = &piece->roi_out;
379 switch (data->operation_mode)
380 {
382 process_rgb(piece, (const float *const restrict)ivoid, (float *const restrict)ovoid, roi_out);
383 break;
385 process_gray(piece, (const float *const restrict)ivoid, (float *const restrict)ovoid, roi_out);
386 break;
388 process_hsl_v1(piece, (const float *const restrict)ivoid, (float *const restrict)ovoid, roi_out);
389 break;
391 process_hsl_v2(piece, (const float *const restrict)ivoid, (float *const restrict)ovoid, roi_out);
392 break;
393 default:
394 break;
395 }
396 if(pipe->mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK) dt_iop_alpha_copy(ivoid, ovoid, roi_out->width, roi_out->height);
397 return 0;
398}
399
400static void red_callback(GtkWidget *slider, gpointer user_data)
401{
402 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
403 if(dt_gui_widgets_suppressed()) return;
406 const int output_channel_index = dt_bauhaus_combobox_get(g->output_channel);
407 const float value = dt_bauhaus_slider_get(slider);
408 if(output_channel_index >= 0 && value != p->red[output_channel_index])
409 {
410 p->red[output_channel_index] = value;
411 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
412 }
413}
414
415static void green_callback(GtkWidget *slider, gpointer user_data)
416{
417 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
418 if(dt_gui_widgets_suppressed()) return;
421 const int output_channel_index = dt_bauhaus_combobox_get(g->output_channel);
422 const float value = dt_bauhaus_slider_get(slider);
423 if(output_channel_index >= 0 && value != p->green[output_channel_index])
424 {
425 p->green[output_channel_index] = value;
426 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
427 }
428}
429
430static void blue_callback(GtkWidget *slider, gpointer user_data)
431{
432 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
433 if(dt_gui_widgets_suppressed()) return;
436 const int output_channel_index = dt_bauhaus_combobox_get(g->output_channel);
437 const float value = dt_bauhaus_slider_get(slider);
438 if(output_channel_index >= 0 && value != p->blue[output_channel_index])
439 {
440 p->blue[output_channel_index] = value;
441 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
442 }
443}
444
445static void output_callback(GtkComboBox *combo, gpointer user_data)
446{
447 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
448 if(dt_gui_widgets_suppressed()) return;
451
452 const int output_channel_index = dt_bauhaus_combobox_get(g->output_channel);
453 if(output_channel_index >= 0)
454 {
455 dt_bauhaus_slider_set(g->scale_red, p->red[output_channel_index]);
456 dt_bauhaus_slider_set_default(g->scale_red, output_channel_index == CHANNEL_RED ? 1.0 : 0.0);
457 dt_bauhaus_slider_set(g->scale_green, p->green[output_channel_index]);
458 dt_bauhaus_slider_set_default(g->scale_green, output_channel_index == CHANNEL_GREEN ? 1.0 : 0.0);
459 dt_bauhaus_slider_set(g->scale_blue, p->blue[output_channel_index]);
460 dt_bauhaus_slider_set_default(g->scale_blue, output_channel_index == CHANNEL_BLUE ? 1.0 : 0.0);
461 }
462}
463
466{
469
470 // HSL mixer matrix
471 gboolean hsl_mix_mode = FALSE;
472 for(int i = CHANNEL_HUE, k = 0; i <= CHANNEL_LIGHTNESS; i++, k += 3)
473 {
474 d->hsl_matrix[k + 0] = p->red[i];
475 d->hsl_matrix[k + 1] = p->green[i];
476 d->hsl_matrix[k + 2] = p->blue[i];
477 hsl_mix_mode |= p->red[i] != 0.0f || p->green[i] != 0.0f || p->blue[i] != 0.0f;
478 }
479
480 // RGB mixer matrix
481 for(int i = CHANNEL_RED, k = 0; i <= CHANNEL_BLUE; i++, k += 3)
482 {
483 d->rgb_matrix[k + 0] = p->red[i];
484 d->rgb_matrix[k + 1] = p->green[i];
485 d->rgb_matrix[k + 2] = p->blue[i];
486 }
487
488 // Gray
489 dt_aligned_pixel_t graymix = { p->red[CHANNEL_GRAY], p->green[CHANNEL_GRAY], p->blue[CHANNEL_GRAY] };
490 const gboolean gray_mix_mode = (graymix[0] != 0.0f || graymix[1] != 0.0f || graymix[2] != 0.0f) ? TRUE : FALSE;
491
492 // Recompute the 3x3 RGB matrix
493 if(gray_mix_mode)
494 {
495 dt_aligned_pixel_t mixed_gray;
496 for(int i = 0; i < 3; i++)
497 {
498 mixed_gray[i] = (graymix[0] * d->rgb_matrix[i]
499 + graymix[1] * d->rgb_matrix[i + 3]
500 + graymix[2] * d->rgb_matrix[i + 6]);
501 }
502 for(int i = 0; i < 9; i += 3)
503 {
504 for(int j = 0; j < 3; j++)
505 {
506 d->rgb_matrix[i + j] = mixed_gray[j];
507 }
508 }
509 }
510
511 if(p->algorithm_version == CHANNEL_MIXER_VERSION_1)
512 {
513 d->operation_mode = OPERATION_MODE_HSL_V1;
514 }
515 else if(hsl_mix_mode)
516 {
517 d->operation_mode = OPERATION_MODE_HSL_V2;
518 }
519 else if(gray_mix_mode)
520 {
521 d->operation_mode = OPERATION_MODE_GRAY;
522 }
523 else
524 {
525 d->operation_mode = OPERATION_MODE_RGB;
526 }
527}
528
534
536{
537 dt_free_align(piece->data);
538 piece->data = NULL;
539}
540
541void gui_update(struct dt_iop_module_t *self)
542{
545
546 const int output_channel_index = dt_bauhaus_combobox_get(g->output_channel);
547 if(output_channel_index >= 0)
548 {
549 dt_bauhaus_slider_set(g->scale_red, p->red[output_channel_index]);
550 dt_bauhaus_slider_set(g->scale_green, p->green[output_channel_index]);
551 dt_bauhaus_slider_set(g->scale_blue, p->blue[output_channel_index]);
552 }
553}
554
556{
557 dt_iop_default_init(module);
558
559 dt_iop_channelmixer_params_t *d = module->default_params;
560
562 d->red[CHANNEL_RED] = d->green[CHANNEL_GREEN] = d->blue[CHANNEL_BLUE] = 1.0;
563}
564
565void gui_init(struct dt_iop_module_t *self)
566{
569
570 self->gui->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
571
572 /* output */
574 dt_bauhaus_widget_set_label(g->output_channel, N_("destination"));
575 dt_bauhaus_combobox_add(g->output_channel, _("hue"));
576 dt_bauhaus_combobox_add(g->output_channel, _("saturation"));
577 dt_bauhaus_combobox_add(g->output_channel, _("lightness"));
578 dt_bauhaus_combobox_add(g->output_channel, _("red"));
579 dt_bauhaus_combobox_add(g->output_channel, _("green"));
580 dt_bauhaus_combobox_add(g->output_channel, _("blue"));
581 dt_bauhaus_combobox_add(g->output_channel, C_("channelmixer", "gray"));
582 dt_bauhaus_combobox_set(g->output_channel, CHANNEL_RED);
583 g_signal_connect(G_OBJECT(g->output_channel), "value-changed", G_CALLBACK(output_callback), self);
584
585 /* red */
586 g->scale_red = dt_bauhaus_slider_new_with_range(dt_bauhaus_get_global(), DT_GUI_MODULE(self), -2.0, 2.0, 0, p->red[CHANNEL_RED], 3);
587 gtk_widget_set_tooltip_text(g->scale_red, _("amount of red channel in the output channel"));
588 dt_bauhaus_widget_set_label(g->scale_red, N_("red"));
589 g_signal_connect(G_OBJECT(g->scale_red), "value-changed", G_CALLBACK(red_callback), self);
590
591 /* green */
592 g->scale_green = dt_bauhaus_slider_new_with_range(dt_bauhaus_get_global(), DT_GUI_MODULE(self), -2.0, 2.0, 0, p->green[CHANNEL_RED], 3);
593 gtk_widget_set_tooltip_text(g->scale_green, _("amount of green channel in the output channel"));
594 dt_bauhaus_widget_set_label(g->scale_green, N_("green"));
595 g_signal_connect(G_OBJECT(g->scale_green), "value-changed", G_CALLBACK(green_callback), self);
596
597 /* blue */
598 g->scale_blue = dt_bauhaus_slider_new_with_range(dt_bauhaus_get_global(), DT_GUI_MODULE(self), -2.0, 2.0, 0, p->blue[CHANNEL_RED], 3);
599 gtk_widget_set_tooltip_text(g->scale_blue, _("amount of blue channel in the output channel"));
600 dt_bauhaus_widget_set_label(g->scale_blue, N_("blue"));
601 g_signal_connect(G_OBJECT(g->scale_blue), "value-changed", G_CALLBACK(blue_callback), self);
602
603
604 gtk_box_pack_start(GTK_BOX(self->gui->widget), GTK_WIDGET(g->output_channel), TRUE, TRUE, 0);
605
606 gtk_box_pack_start(GTK_BOX(self->gui->widget), GTK_WIDGET(g->scale_red), TRUE, TRUE, 0);
607 gtk_box_pack_start(GTK_BOX(self->gui->widget), GTK_WIDGET(g->scale_green), TRUE, TRUE, 0);
608 gtk_box_pack_start(GTK_BOX(self->gui->widget), GTK_WIDGET(g->scale_blue), TRUE, TRUE, 0);
609}
610
612{
614
615 dt_gui_presets_add_generic(_("swap R and B"), self->op, self->version(),
616 &(dt_iop_channelmixer_params_t){ { 0, 0, 0, 0, 0, 1, 0 },
617 { 0, 0, 0, 0, 1, 0, 0 },
618 { 0, 0, 0, 1, 0, 0, 0 },
621 dt_gui_presets_add_generic(_("swap G and B"), self->op, self->version(),
622 &(dt_iop_channelmixer_params_t){ { 0, 0, 0, 1, 0, 0, 0 },
623 { 0, 0, 0, 0, 0, 1, 0 },
624 { 0, 0, 0, 0, 1, 0, 0 },
627 dt_gui_presets_add_generic(_("color contrast boost"), self->op, self->version(),
628 &(dt_iop_channelmixer_params_t){ { 0, 0, 0.8, 1, 0, 0, 0 },
629 { 0, 0, 0.1, 0, 1, 0, 0 },
630 { 0, 0, 0.1, 0, 0, 1, 0 },
633 dt_gui_presets_add_generic(_("color details boost"), self->op, self->version(),
634 &(dt_iop_channelmixer_params_t){ { 0, 0, 0.1, 1, 0, 0, 0 },
635 { 0, 0, 0.8, 0, 1, 0, 0 },
636 { 0, 0, 0.1, 0, 0, 1, 0 },
639 dt_gui_presets_add_generic(_("color artifacts boost"), self->op, self->version(),
640 &(dt_iop_channelmixer_params_t){ { 0, 0, 0.1, 1, 0, 0, 0 },
641 { 0, 0, 0.1, 0, 1, 0, 0 },
642 { 0, 0, 0.8, 0, 0, 1, 0 },
645 dt_gui_presets_add_generic(_("B/W luminance-based"), self->op, self->version(),
646 &(dt_iop_channelmixer_params_t){ { 0, 0, 0, 1, 0, 0, 0.21 },
647 { 0, 0, 0, 0, 1, 0, 0.72 },
648 { 0, 0, 0, 0, 0, 1, 0.07 },
651 dt_gui_presets_add_generic(_("B/W artifacts boost"), self->op, self->version(),
652 &(dt_iop_channelmixer_params_t){ { 0, 0, 0, 1, 0, 0, -0.275 },
653 { 0, 0, 0, 0, 1, 0, -0.275 },
654 { 0, 0, 0, 0, 0, 1, 1.275 },
657 dt_gui_presets_add_generic(_("B/W smooth skin"), self->op, self->version(),
658 &(dt_iop_channelmixer_params_t){ { 0, 0, 0, 1, 0, 0, 1.0 },
659 { 0, 0, 0, 0, 1, 0, 0.325 },
660 { 0, 0, 0, 0, 0, 1, -0.4 },
663 dt_gui_presets_add_generic(_("B/W blue artifacts reduce"), self->op, self->version(),
664 &(dt_iop_channelmixer_params_t){ { 0, 0, 0, 1, 0, 0, 0.4 },
665 { 0, 0, 0, 0, 1, 0, 0.750 },
666 { 0, 0, 0, 0, 0, 1, -0.15 },
669
670 dt_gui_presets_add_generic(_("B/W Ilford Delta 100-400"), self->op, self->version(),
671 &(dt_iop_channelmixer_params_t){ { 0, 0, 0, 1, 0, 0, 0.21 },
672 { 0, 0, 0, 0, 1, 0, 0.42 },
673 { 0, 0, 0, 0, 0, 1, 0.37 },
676
677 dt_gui_presets_add_generic(_("B/W Ilford Delta 3200"), self->op, self->version(),
678 &(dt_iop_channelmixer_params_t){ { 0, 0, 0, 1, 0, 0, 0.31 },
679 { 0, 0, 0, 0, 1, 0, 0.36 },
680 { 0, 0, 0, 0, 0, 1, 0.33 },
683
684 dt_gui_presets_add_generic(_("B/W Ilford FP4"), self->op, self->version(),
685 &(dt_iop_channelmixer_params_t){ { 0, 0, 0, 1, 0, 0, 0.28 },
686 { 0, 0, 0, 0, 1, 0, 0.41 },
687 { 0, 0, 0, 0, 0, 1, 0.31 },
690
691 dt_gui_presets_add_generic(_("B/W Ilford HP5"), self->op, self->version(),
692 &(dt_iop_channelmixer_params_t){ { 0, 0, 0, 1, 0, 0, 0.23 },
693 { 0, 0, 0, 0, 1, 0, 0.37 },
694 { 0, 0, 0, 0, 0, 1, 0.40 },
697
698 dt_gui_presets_add_generic(_("B/W Ilford SFX"), self->op, self->version(),
699 &(dt_iop_channelmixer_params_t){ { 0, 0, 0, 1, 0, 0, 0.36 },
700 { 0, 0, 0, 0, 1, 0, 0.31 },
701 { 0, 0, 0, 0, 0, 1, 0.33 },
704
705 dt_gui_presets_add_generic(_("B/W Kodak T-Max 100"), self->op, self->version(),
706 &(dt_iop_channelmixer_params_t){ { 0, 0, 0, 1, 0, 0, 0.24 },
707 { 0, 0, 0, 0, 1, 0, 0.37 },
708 { 0, 0, 0, 0, 0, 1, 0.39 },
711
712 dt_gui_presets_add_generic(_("B/W Kodak T-max 400"), self->op, self->version(),
713 &(dt_iop_channelmixer_params_t){ { 0, 0, 0, 1, 0, 0, 0.27 },
714 { 0, 0, 0, 0, 1, 0, 0.36 },
715 { 0, 0, 0, 0, 0, 1, 0.37 },
718
719 dt_gui_presets_add_generic(_("B/W Kodak Tri-X 400"), self->op, self->version(),
720 &(dt_iop_channelmixer_params_t){ { 0, 0, 0, 1, 0, 0, 0.25 },
721 { 0, 0, 0, 0, 1, 0, 0.35 },
722 { 0, 0, 0, 0, 0, 1, 0.40 },
725
726
728}
729
730// clang-format off
731// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
732// vim: shiftwidth=2 expandtab tabstop=2 cindent
733// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
734// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dt_bauhaus_slider_set_default(GtkWidget *widget, float def)
Definition bauhaus.c:1491
float dt_bauhaus_slider_get(GtkWidget *widget)
Definition bauhaus.c:3280
int dt_bauhaus_combobox_get(GtkWidget *widget)
Definition bauhaus.c:2136
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3331
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
Definition bauhaus.c:2090
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
Definition bauhaus.c:1504
GtkWidget * dt_bauhaus_slider_new_with_range(dt_bauhaus_t *bh, dt_gui_module_t *self, float min, float max, float step, float defval, int digits)
Definition bauhaus.c:1632
GtkWidget * dt_bauhaus_combobox_new(dt_bauhaus_t *bh, dt_gui_module_t *self)
Definition bauhaus.c:1698
void dt_bauhaus_combobox_add(GtkWidget *widget, const char *text)
Definition bauhaus.c:1824
void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
void init(dt_iop_module_t *module)
const char ** description(struct dt_iop_module_t *self)
int default_group()
static void red_callback(GtkWidget *slider, gpointer user_data)
_channelmixer_algorithm_t
@ CHANNEL_MIXER_VERSION_1
@ CHANNEL_MIXER_VERSION_2
_channelmixer_operation_mode_t
@ OPERATION_MODE_HSL_V1
@ OPERATION_MODE_RGB
@ OPERATION_MODE_GRAY
@ OPERATION_MODE_HSL_V2
static void output_callback(GtkComboBox *combo, gpointer user_data)
static void blue_callback(GtkWidget *slider, gpointer user_data)
static __DT_CLONE_TARGETS__ void process_gray(const dt_dev_pixelpipe_iop_t *piece, const float *const restrict in, float *const restrict out, const dt_iop_roi_t *const roi_out)
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
_channelmixer_output_t
@ CHANNEL_HUE
@ CHANNEL_LIGHTNESS
@ CHANNEL_BLUE
@ CHANNEL_GRAY
@ CHANNEL_SATURATION
@ CHANNEL_SIZE
@ CHANNEL_GREEN
@ CHANNEL_RED
const char * name()
void gui_update(struct dt_iop_module_t *self)
void gui_init(struct dt_iop_module_t *self)
static __DT_CLONE_TARGETS__ void process_hsl_v2(const dt_dev_pixelpipe_iop_t *piece, const float *const restrict in, float *const restrict out, const dt_iop_roi_t *const roi_out)
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
int flags()
static __DT_CLONE_TARGETS__ void process_hsl_v1(const dt_dev_pixelpipe_iop_t *piece, const float *const restrict in, float *const restrict out, const dt_iop_roi_t *const roi_out)
void init_presets(dt_iop_module_so_t *self)
int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid)
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static void green_callback(GtkWidget *slider, gpointer user_data)
const char * deprecated_msg()
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 __DT_CLONE_TARGETS__ void process_rgb(const dt_dev_pixelpipe_iop_t *piece, const float *const restrict in, float *const restrict out, const dt_iop_roi_t *const roi_out)
@ IOP_CS_RGB
void rgb2hsl(const dt_aligned_pixel_t rgb, float *h, float *s, float *l)
Convert RGB to HSL. Common helper used by iop modules.
void hsl2rgb(dt_aligned_pixel_t rgb, float h, float s, float l)
Convert HSL back to RGB. Common helper used by iop modules.
The colour-profile module's API: which profiles exist, and how to apply one.
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
static dt_aligned_pixel_t rgb
const dt_colormatrix_t dt_aligned_pixel_t out
struct dt_bauhaus_t * dt_bauhaus_get_global(void)
Definition darktable.c:646
#define dt_database_start_transaction()
Definition database.h:290
#define dt_database_release_transaction()
Definition database.h:291
#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
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)
#define DT_GUI_MODULE(x)
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_DEPRECATED
Definition imageop.h:187
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:186
@ IOP_FLAGS_ALLOW_TILING
Definition imageop.h:188
@ IOP_GROUP_COLOR
Definition imageop.h:158
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
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
#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_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
DT_MODULE() for a module whose params struct is introspected.
#define __OMP_PARALLEL_FOR__(...)
Definition openmp.h:95
static float clamp_simd(const float x)
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
Definition simd.h:53
#define for_each_channel(_var,...)
Definition simd.h:87
static const dt_aligned_pixel_simd_t value
Definition simd.h:144
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
unsigned int channels
Definition format.h:83
_channelmixer_operation_mode_t operation_mode
float green[CHANNEL_SIZE]
float red[CHANNEL_SIZE]
_channelmixer_algorithm_t algorithm_version
float blue[CHANNEL_SIZE]
GtkWidget * widget
Definition imageop_gui.h:47
dt_dev_operation_t op
Definition imageop.h:235
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_params_t * params
Definition imageop.h:333
Region of interest passed through the pixelpipe.
Definition format.h:49
int width
Definition format.h:50
int height
Definition format.h:50
#define __DT_CLONE_TARGETS__
gboolean dt_gui_widgets_suppressed(void)
#define DT_GUI_BOX_SPACING