Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
invert.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2011, 2014 Henrik Andersson.
4 Copyright (C) 2011-2013, 2016 johannes hanika.
5 Copyright (C) 2011 Robert Bieber.
6 Copyright (C) 2011-2014, 2016, 2018-2019 Tobias Ellinghaus.
7 Copyright (C) 2012 parafin.
8 Copyright (C) 2012 Richard Wonka.
9 Copyright (C) 2013, 2020 Aldric Renaudin.
10 Copyright (C) 2013 Dennis Gnad.
11 Copyright (C) 2013-2016 Roman Lebedev.
12 Copyright (C) 2013-2014, 2016-2017 Ulrich Pegelow.
13 Copyright (C) 2014 Dan Torop.
14 Copyright (C) 2015-2016 Pedro Côrte-Real.
15 Copyright (C) 2018-2020, 2023, 2025-2026 Aurélien PIERRE.
16 Copyright (C) 2018-2019 Edgardo Hoszowski.
17 Copyright (C) 2018 Kelvie Wong.
18 Copyright (C) 2018 Maurizio Paglia.
19 Copyright (C) 2018-2022 Pascal Obry.
20 Copyright (C) 2018 rawfiner.
21 Copyright (C) 2019 Andreas Schneider.
22 Copyright (C) 2019-2020, 2022 Diederik Ter Rahe.
23 Copyright (C) 2019, 2022 Hanno Schwalm.
24 Copyright (C) 2020 Hubert Kowalski.
25 Copyright (C) 2020-2021 Ralf Brown.
26 Copyright (C) 2021 Chris Elston.
27 Copyright (C) 2022 Martin Bařinka.
28 Copyright (C) 2022 Philipp Lutz.
29 Copyright (C) 2026 Guillaume Stutin.
30
31 darktable is free software: you can redistribute it and/or modify
32 it under the terms of the GNU General Public License as published by
33 the Free Software Foundation, either version 3 of the License, or
34 (at your option) any later version.
35
36 darktable is distributed in the hope that it will be useful,
37 but WITHOUT ANY WARRANTY; without even the implied warranty of
38 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
39 GNU General Public License for more details.
40
41 You should have received a copy of the GNU General Public License
42 along with darktable. If not, see <http://www.gnu.org/licenses/>.
43*/
44
45#ifdef HAVE_CONFIG_H
46#include "config.h"
47#endif
48#include <gtk/gtk.h>
49#include <stdlib.h>
50
52#include "system/openmp.h"
54#include "system/mem_alloc.h"
55#include "system/simd.h"
57#include "control/control.h"
58#include "develop/imageop.h"
60#include "develop/imageop_gui.h"
61#include "widgets/resetlabel.h"
63
64#include "iop/iop_api.h"
66
68
70{
71 float color[4]; // $DEFAULT: 1.0 color of film material
73
83
85{
86 float color[4]; // color of film material
88
89int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params,
90 const int new_version)
91{
92 if(old_version == 1 && new_version == 2)
93 {
94 typedef struct dt_iop_invert_params_v1_t
95 {
96 float color[3]; // color of film material
97 } dt_iop_invert_params_v1_t;
98
99 dt_iop_invert_params_v1_t *o = (dt_iop_invert_params_v1_t *)old_params;
101
102 n->color[0] = o->color[0];
103 n->color[1] = o->color[1];
104 n->color[2] = o->color[2];
105 n->color[3] = NAN;
106
107 if(self->dev && self->dev->image_storage.flags & DT_IMAGE_4BAYER)
108 {
109 double RGB_to_CAM[4][3];
110
111 // Get and store the matrix to go from camera to RGB for 4Bayer images (used for spot WB)
113 RGB_to_CAM, NULL,
114 self->dev->image_storage.d65_color_matrix, NULL))
115 {
116 const char *camera = self->dev->image_storage.camera_makermodel;
117 gchar *item = g_strdup_printf("%s (%s)", self->dev->image_storage.fullpath, camera);
118 dt_control_alert(_("Camera data missing"), _("color matrix not found for 4bayer image"), item);
119 dt_free(item);
120 }
121 else
122 {
123 dt_colorspaces_rgb_to_cygm(n->color, 1, RGB_to_CAM);
124 }
125 }
126
127 return 0;
128 }
129 return 1;
130}
131
132
133const char *name()
134{
135 return _("invert");
136}
137
138const char *deprecated_msg()
139{
140 return _("this module is deprecated. please use the negadoctor module instead.");
141}
142
143const char **description(struct dt_iop_module_t *self)
144{
145 return dt_iop_set_description(self, _("invert film negatives"),
146 _("corrective"),
147 _("linear, raw, display-referred"),
148 _("linear, raw"),
149 _("linear, raw, display-referred"));
150}
151
152
154{
155 return IOP_GROUP_FILM;
156}
157
162
167
173
175{
178
179 GdkRGBA color = (GdkRGBA){.red = p->color[0], .green = p->color[1], .blue = p->color[2], .alpha = 1.0 };
180
181 const dt_image_t *img = &self->dev->image_storage;
182 if(img->flags & DT_IMAGE_4BAYER)
183 {
185 for_four_channels(k) rgb[k] = p->color[k];
186
187 dt_colorspaces_cygm_to_rgb(rgb, 1, g->CAM_to_RGB);
188
189 color.red = rgb[0];
190 color.green = rgb[1];
191 color.blue = rgb[2];
192 }
193
194 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(g->colorpicker), &color);
195}
196
198{
199 static dt_aligned_pixel_t old = { 0.0f, 0.0f, 0.0f, 0.0f };
200
201 const float *grayrgb = self->picked_color;
202
203 if(grayrgb[0] == old[0] && grayrgb[1] == old[1] && grayrgb[2] == old[2] && grayrgb[3] == old[3]) return;
204
205 for_four_channels(k) old[k] = grayrgb[k];
206
208 for_four_channels(k) p->color[k] = grayrgb[k];
209
213
214 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
216}
217
218static void colorpicker_callback(GtkColorButton *widget, dt_iop_module_t *self)
219{
220 if(dt_gui_widgets_suppressed()) return;
223
225
226 GdkRGBA c;
227 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget), &c);
228 p->color[0] = c.red;
229 p->color[1] = c.green;
230 p->color[2] = c.blue;
231
232 const dt_image_t *img = &self->dev->image_storage;
233 if(img->flags & DT_IMAGE_4BAYER)
234 {
235 dt_colorspaces_rgb_to_cygm(p->color, 1, g->RGB_to_CAM);
236 }
237 else if(dt_image_is_monochrome(img))
238 { // Just to make sure the monochrome stays monochrome we take the luminosity of the chosen color on all channels
239 p->color[0] = p->color[1] = p->color[2] = 0.21f*c.red + 0.72f*c.green + 0.07f*c.blue ;
240 }
241 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
242}
243
245int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
246 void *const ovoid)
247{
248 const dt_iop_roi_t *const roi_out = &piece->roi_out;
249 const dt_iop_invert_data_t *const d = (dt_iop_invert_data_t *)piece->data;
250
251 const float *const m = piece->dsc_in.processed_maximum;
252
253 const dt_aligned_pixel_t film_rgb_f
254 = { d->color[0] * m[0], d->color[1] * m[1], d->color[2] * m[2], d->color[3] * m[3] };
255
256 // FIXME: it could be wise to make this a NOP when picking colors. not sure about that though.
257 // if(self->request_color_pick){
258 // do nothing
259 // }
260
261 const uint32_t filters = piece->dsc_in.filters;
262 const uint8_t(*const xtrans)[6] = (const uint8_t(*const)[6])piece->dsc_in.xtrans;
263
264 const float *const in = (const float *const)ivoid;
265 float *const out = (float *const)ovoid;
266
267 if(filters == 9u)
268 { // xtrans float mosaiced
269 __OMP_PARALLEL_FOR_SIMD__(collapse(2))
270 for(int j = 0; j < roi_out->height; j++)
271 {
272 for(int i = 0; i < roi_out->width; i++)
273 {
274 const size_t p = (size_t)j * roi_out->width + i;
275 out[p] = CLAMP(film_rgb_f[FCxtrans(j, i, roi_out, xtrans)] - in[p], 0.0f, 1.0f);
276 }
277 }
278 }
279 else if(filters)
280 { // bayer float mosaiced
281 __OMP_PARALLEL_FOR_SIMD__(collapse(2))
282 for(int j = 0; j < roi_out->height; j++)
283 {
284 for(int i = 0; i < roi_out->width; i++)
285 {
286 const size_t p = (size_t)j * roi_out->width + i;
287 out[p] = CLAMP(film_rgb_f[FC(j + roi_out->y, i + roi_out->x, filters)] - in[p], 0.0f, 1.0f);
288 }
289 }
290 }
291 else
292 { // non-mosaiced
293 const int ch = piece->dsc_in.channels;
294 __OMP_PARALLEL_FOR_SIMD__(collapse(2))
295 for(size_t k = 0; k < (size_t)ch * roi_out->width * roi_out->height; k += ch)
296 {
297 for(int c = 0; c < 3; c++)
298 {
299 const size_t p = (size_t)k + c;
300 out[p] = d->color[c] - in[p];
301 }
302 }
303
304 if(pipe->mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK) dt_iop_alpha_copy(ivoid, ovoid, roi_out->width, roi_out->height);
305 }
306 return 0;
307}
308
309// invert has no image-dependent default_params, so it needs no reload_defaults(): the per-image
310// GUI state (reset label text + the GUI-only 4-Bayer CAM matrices used for spot WB) is set up in
311// gui_update() instead, which runs on the GUI thread when the widgets exist.
312
315{
318
319 for(int k = 0; k < 4; k++) d->color[k] = p->color[k];
320
321 // x-trans images not implemented in OpenCL yet
322 if(pipe->dev->image_storage.dsc.filters == 9u) piece->process_cl_ready = 0;
323
324 // 4Bayer images not implemented in OpenCL yet
325 if(self->dev->image_storage.flags & DT_IMAGE_4BAYER) piece->process_cl_ready = 0;
326
327 if(self->hide_enable_button) piece->enabled = 0;
328
329 if(piece->dsc_in.filters)
330 for(int k = 0; k < 4; k++) piece->dsc_out.processed_maximum[k] = 1.0f;
331}
332
338
340{
341 dt_free_align(piece->data);
342 piece->data = NULL;
343}
344
346{
348 const dt_image_t *const img = &self->dev->image_storage;
349
350 // Per-image GUI state (moved here from reload_defaults so it only runs on the GUI thread with
351 // live widgets): the reset label, and the 4-Bayer CAM<->RGB matrices used by spot WB below.
353 {
354 // No monochrome camera has a Bayer sensor, so no RGB_to_CAM / CAM_to_RGB correction is needed.
355 dtgtk_reset_label_set_text(g->label, _("brightness of film material"));
356 }
357 else
358 {
359 dtgtk_reset_label_set_text(g->label, _("color of film material"));
360
361 if(img->flags & DT_IMAGE_4BAYER)
362 {
363 // Get and store the matrix to go from camera to RGB for 4Bayer images (used for spot WB)
364 if(!dt_colorspaces_conversion_matrices_rgb(&img->adobe_XYZ_to_CAM[0][0], g->RGB_to_CAM, g->CAM_to_RGB,
365 img->d65_color_matrix, NULL))
366 {
367 const char *camera = img->camera_makermodel;
368 dt_control_alert(_("Camera data missing"), _("color matrix not found for 4bayer image"), camera);
369 }
370 }
371 }
372
374}
375
377{
380
381 self->gui->widget = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
382 g->label = DTGTK_RESET_LABEL(dt_iop_gui_reset_label_new("", self, &p->color, sizeof(float) * 4));
383 gtk_box_pack_start(GTK_BOX(self->gui->widget), GTK_WIDGET(g->label), TRUE, TRUE, 0);
384
385 g->pickerbuttons = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING));
386 gtk_box_pack_start(GTK_BOX(self->gui->widget), GTK_WIDGET(g->pickerbuttons), TRUE, TRUE, 0);
387
388 GdkRGBA color = (GdkRGBA){.red = p->color[0], .green = p->color[1], .blue = p->color[2], .alpha = 1.0 };
389 g->colorpicker = gtk_color_button_new_with_rgba(&color);
390 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(g->colorpicker), FALSE);
391 gtk_color_button_set_title(GTK_COLOR_BUTTON(g->colorpicker), _("select color of film material"));
392 g_signal_connect(G_OBJECT(g->colorpicker), "color-set", G_CALLBACK(colorpicker_callback), self);
393 gtk_box_pack_start(GTK_BOX(g->pickerbuttons), GTK_WIDGET(g->colorpicker), TRUE, TRUE, 0);
394
395 g->picker = dt_color_picker_new(self, DT_COLOR_PICKER_AREA, GTK_WIDGET(g->pickerbuttons));
396}
397
398// clang-format off
399// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
400// vim: shiftwidth=2 expandtab tabstop=2 cindent
401// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
402// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
#define m
Definition basecurve.c:283
@ IOP_CS_RAW
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
void dt_colorspaces_rgb_to_cygm(float *out, int num, double RGB_to_CAM[4][3])
Convert an RGB buffer to 4-channel CYGM, in place.
__DT_CLONE_TARGETS__ int dt_colorspaces_conversion_matrices_rgb(const float *adobe_XYZ_to_CAM, double(*out_RGB_to_CAM)[3], double(*out_CAM_to_RGB)[4], const float *embedded_matrix, double *mul)
Compute the sRGB->camera and camera->sRGB matrices, and the default white balance multipliers.
__DT_CLONE_TARGETS__ void dt_colorspaces_cygm_to_rgb(float *out, int num, double CAM_to_RGB[3][4])
Convert a 4-channel CYGM buffer to RGB, in place.
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
gboolean dt_image_is_monochrome(const dt_image_t *img)
void dt_control_alert(const char *title, const char *message, const char *item)
Tell the user something that must not go unseen: what it says stays true after a toast would have fad...
Definition control.c:883
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:1015
static int FCxtrans(const int row, const int col, global const unsigned char(*const xtrans)[6])
static int FC(const int row, const int col, const unsigned int filters)
#define dt_dev_add_history_item(dev, module, enable, redraw)
void dt_iop_params_t
Definition dev_history.h:43
void default_output_format(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_iop_buffer_dsc_t *dsc)
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
Definition develop.h:123
GdkRGBA color[]
Definition geotagging.c:541
@ DT_IMAGE_4BAYER
Definition image.h:149
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:1909
@ IOP_FLAGS_DEPRECATED
Definition imageop.h:187
@ IOP_FLAGS_ONE_INSTANCE
Definition imageop.h:191
@ IOP_GROUP_FILM
Definition imageop.h:157
GtkWidget * dt_iop_gui_reset_label_new(const gchar *label, dt_iop_module_t *module, void *param, int param_size)
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
const char ** description(struct dt_iop_module_t *self)
Definition invert.c:143
int default_group()
Definition invert.c:153
static void gui_update_from_coeffs(dt_iop_module_t *self)
Definition invert.c:174
__DT_CLONE_TARGETS__ 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)
Definition invert.c:245
void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *params, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition invert.c:313
void gui_update(dt_iop_module_t *self)
Refresh GUI controls from current params and configuration.
Definition invert.c:345
static void colorpicker_callback(GtkColorButton *widget, dt_iop_module_t *self)
Definition invert.c:218
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition invert.c:333
const char * name()
Definition invert.c:133
void output_format(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_iop_buffer_dsc_t *dsc)
Definition invert.c:168
void gui_init(dt_iop_module_t *self)
Definition invert.c:376
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition invert.c:163
int flags()
Definition invert.c:158
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition invert.c:339
const char * deprecated_msg()
Definition invert.c:138
void color_picker_apply(dt_iop_module_t *self, GtkWidget *picker, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition invert.c:197
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 invert.c:89
static const dt_dev_pixelpipe_t * pipe
Definition layers.h:49
static const dt_dev_pixelpipe_t const dt_dev_pixelpipe_iop_t * piece
Definition layers.h:50
static const dt_iop_drawlayer_params_t * params
Definition layers.h:80
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_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
#define o
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
DT_MODULE() for a module whose params struct is introspected.
#define __OMP_PARALLEL_FOR_SIMD__(...)
Definition openmp.h:96
void dtgtk_reset_label_set_text(GtkDarktableResetLabel *label, const gchar *str)
Definition resetlabel.c:95
#define DTGTK_RESET_LABEL(obj)
Definition resetlabel.h:30
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
Definition simd.h:53
#define for_four_channels(_var,...)
Definition simd.h:89
struct dt_develop_t * dev
dt_image_t image_storage
Definition develop.h:225
char camera_makermodel[128]
Definition image.h:391
int32_t flags
Definition image.h:410
float d65_color_matrix[9]
Definition image.h:430
dt_iop_buffer_dsc_t dsc
Definition image.h:428
float adobe_XYZ_to_CAM[4][3]
Definition image.h:458
char fullpath[DT_PATH_MAX]
Definition image.h:396
uint32_t filters
Definition format.h:89
double RGB_to_CAM[4][3]
Definition invert.c:80
double CAM_to_RGB[3][4]
Definition invert.c:81
GtkWidget * picker
Definition invert.c:79
GtkWidget * colorpicker
Definition invert.c:76
GtkDarktableResetLabel * label
Definition invert.c:77
GtkWidget * widget
Definition imageop_gui.h:47
int32_t hide_enable_button
Definition imageop.h:270
struct dt_iop_module_gui_t * gui
Definition imageop.h:346
struct dt_develop_t * dev
Definition imageop.h:311
dt_aligned_pixel_t picked_color
Definition imageop.h:287
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_freeze_begin()
#define dt_gui_freeze_end()
#define DT_GUI_BOX_SPACING