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
30 darktable is free software: you can redistribute it and/or modify
31 it under the terms of the GNU General Public License as published by
32 the Free Software Foundation, either version 3 of the License, or
33 (at your option) any later version.
34
35 darktable is distributed in the hope that it will be useful,
36 but WITHOUT ANY WARRANTY; without even the implied warranty of
37 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
38 GNU General Public License for more details.
39
40 You should have received a copy of the GNU General Public License
41 along with darktable. If not, see <http://www.gnu.org/licenses/>.
42*/
43
44#ifdef HAVE_CONFIG_H
45#include "config.h"
46#endif
47#include <gtk/gtk.h>
48#include <stdlib.h>
49
51#include "system/openmp.h"
53#include "system/mem_alloc.h"
54#include "system/simd.h"
56#include "control/control.h"
57#include "develop/imageop.h"
59#include "develop/imageop_gui.h"
60#include "widgets/resetlabel.h"
62
63#include "iop/iop_api.h"
65
67
69{
70 float color[4]; // $DEFAULT: 1.0 color of film material
72
82
84{
85 float color[4]; // color of film material
87
88int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params,
89 const int new_version)
90{
91 if(old_version == 1 && new_version == 2)
92 {
93 typedef struct dt_iop_invert_params_v1_t
94 {
95 float color[3]; // color of film material
96 } dt_iop_invert_params_v1_t;
97
98 dt_iop_invert_params_v1_t *o = (dt_iop_invert_params_v1_t *)old_params;
100
101 n->color[0] = o->color[0];
102 n->color[1] = o->color[1];
103 n->color[2] = o->color[2];
104 n->color[3] = NAN;
105
106 if(self->dev && self->dev->image_storage.flags & DT_IMAGE_4BAYER)
107 {
108 double RGB_to_CAM[4][3];
109
110 // Get and store the matrix to go from camera to RGB for 4Bayer images (used for spot WB)
112 RGB_to_CAM, NULL,
113 self->dev->image_storage.d65_color_matrix, NULL))
114 {
115 const char *camera = self->dev->image_storage.camera_makermodel;
116 fprintf(stderr, "[invert] `%s' color matrix not found for 4bayer image\n", camera);
117 dt_control_log(_("`%s' color matrix not found for 4bayer image"), camera);
118 }
119 else
120 {
121 dt_colorspaces_rgb_to_cygm(n->color, 1, RGB_to_CAM);
122 }
123 }
124
125 return 0;
126 }
127 return 1;
128}
129
130
131const char *name()
132{
133 return _("invert");
134}
135
136const char *deprecated_msg()
137{
138 return _("this module is deprecated. please use the negadoctor module instead.");
139}
140
141const char **description(struct dt_iop_module_t *self)
142{
143 return dt_iop_set_description(self, _("invert film negatives"),
144 _("corrective"),
145 _("linear, raw, display-referred"),
146 _("linear, raw"),
147 _("linear, raw, display-referred"));
148}
149
150
152{
153 return IOP_GROUP_FILM;
154}
155
160
162{
163 return IOP_CS_RAW;
164}
165
168{
169 default_output_format(self, pipe, piece, dsc);
170}
171
173{
176
177 GdkRGBA color = (GdkRGBA){.red = p->color[0], .green = p->color[1], .blue = p->color[2], .alpha = 1.0 };
178
179 const dt_image_t *img = &self->dev->image_storage;
180 if(img->flags & DT_IMAGE_4BAYER)
181 {
183 for_four_channels(k) rgb[k] = p->color[k];
184
185 dt_colorspaces_cygm_to_rgb(rgb, 1, g->CAM_to_RGB);
186
187 color.red = rgb[0];
188 color.green = rgb[1];
189 color.blue = rgb[2];
190 }
191
192 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(g->colorpicker), &color);
193}
194
196{
197 static dt_aligned_pixel_t old = { 0.0f, 0.0f, 0.0f, 0.0f };
198
199 const float *grayrgb = self->picked_color;
200
201 if(grayrgb[0] == old[0] && grayrgb[1] == old[1] && grayrgb[2] == old[2] && grayrgb[3] == old[3]) return;
202
203 for_four_channels(k) old[k] = grayrgb[k];
204
206 for_four_channels(k) p->color[k] = grayrgb[k];
207
211
212 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
214}
215
216static void colorpicker_callback(GtkColorButton *widget, dt_iop_module_t *self)
217{
218 if(dt_gui_widgets_suppressed()) return;
221
223
224 GdkRGBA c;
225 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget), &c);
226 p->color[0] = c.red;
227 p->color[1] = c.green;
228 p->color[2] = c.blue;
229
230 const dt_image_t *img = &self->dev->image_storage;
231 if(img->flags & DT_IMAGE_4BAYER)
232 {
233 dt_colorspaces_rgb_to_cygm(p->color, 1, g->RGB_to_CAM);
234 }
235 else if(dt_image_is_monochrome(img))
236 { // Just to make sure the monochrome stays monochrome we take the luminosity of the chosen color on all channels
237 p->color[0] = p->color[1] = p->color[2] = 0.21f*c.red + 0.72f*c.green + 0.07f*c.blue ;
238 }
239 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
240}
241
243int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
244 void *const ovoid)
245{
246 const dt_iop_roi_t *const roi_out = &piece->roi_out;
247 const dt_iop_invert_data_t *const d = (dt_iop_invert_data_t *)piece->data;
248
249 const float *const m = piece->dsc_in.processed_maximum;
250
251 const dt_aligned_pixel_t film_rgb_f
252 = { d->color[0] * m[0], d->color[1] * m[1], d->color[2] * m[2], d->color[3] * m[3] };
253
254 // FIXME: it could be wise to make this a NOP when picking colors. not sure about that though.
255 // if(self->request_color_pick){
256 // do nothing
257 // }
258
259 const uint32_t filters = piece->dsc_in.filters;
260 const uint8_t(*const xtrans)[6] = (const uint8_t(*const)[6])piece->dsc_in.xtrans;
261
262 const float *const in = (const float *const)ivoid;
263 float *const out = (float *const)ovoid;
264
265 if(filters == 9u)
266 { // xtrans float mosaiced
267 __OMP_PARALLEL_FOR_SIMD__(collapse(2))
268 for(int j = 0; j < roi_out->height; j++)
269 {
270 for(int i = 0; i < roi_out->width; i++)
271 {
272 const size_t p = (size_t)j * roi_out->width + i;
273 out[p] = CLAMP(film_rgb_f[FCxtrans(j, i, roi_out, xtrans)] - in[p], 0.0f, 1.0f);
274 }
275 }
276 }
277 else if(filters)
278 { // bayer float mosaiced
279 __OMP_PARALLEL_FOR_SIMD__(collapse(2))
280 for(int j = 0; j < roi_out->height; j++)
281 {
282 for(int i = 0; i < roi_out->width; i++)
283 {
284 const size_t p = (size_t)j * roi_out->width + i;
285 out[p] = CLAMP(film_rgb_f[FC(j + roi_out->y, i + roi_out->x, filters)] - in[p], 0.0f, 1.0f);
286 }
287 }
288 }
289 else
290 { // non-mosaiced
291 const int ch = piece->dsc_in.channels;
292 __OMP_PARALLEL_FOR_SIMD__(collapse(2))
293 for(size_t k = 0; k < (size_t)ch * roi_out->width * roi_out->height; k += ch)
294 {
295 for(int c = 0; c < 3; c++)
296 {
297 const size_t p = (size_t)k + c;
298 out[p] = d->color[c] - in[p];
299 }
300 }
301
302 if(pipe->mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK) dt_iop_alpha_copy(ivoid, ovoid, roi_out->width, roi_out->height);
303 }
304 return 0;
305}
306
307// invert has no image-dependent default_params, so it needs no reload_defaults(): the per-image
308// GUI state (reset label text + the GUI-only 4-Bayer CAM matrices used for spot WB) is set up in
309// gui_update() instead, which runs on the GUI thread when the widgets exist.
310
313{
316
317 for(int k = 0; k < 4; k++) d->color[k] = p->color[k];
318
319 // x-trans images not implemented in OpenCL yet
320 if(pipe->dev->image_storage.dsc.filters == 9u) piece->process_cl_ready = 0;
321
322 // 4Bayer images not implemented in OpenCL yet
324
325 if(self->hide_enable_button) piece->enabled = 0;
326
327 if(piece->dsc_in.filters)
328 for(int k = 0; k < 4; k++) piece->dsc_out.processed_maximum[k] = 1.0f;
329}
330
332{
333 piece->data = dt_calloc_align(sizeof(dt_iop_invert_data_t));
334 piece->data_size = sizeof(dt_iop_invert_data_t);
335}
336
338{
339 dt_free_align(piece->data);
340 piece->data = NULL;
341}
342
344{
346 const dt_image_t *const img = &self->dev->image_storage;
347
348 // Per-image GUI state (moved here from reload_defaults so it only runs on the GUI thread with
349 // live widgets): the reset label, and the 4-Bayer CAM<->RGB matrices used by spot WB below.
351 {
352 // No monochrome camera has a Bayer sensor, so no RGB_to_CAM / CAM_to_RGB correction is needed.
353 dtgtk_reset_label_set_text(g->label, _("brightness of film material"));
354 }
355 else
356 {
357 dtgtk_reset_label_set_text(g->label, _("color of film material"));
358
359 if(img->flags & DT_IMAGE_4BAYER)
360 {
361 // Get and store the matrix to go from camera to RGB for 4Bayer images (used for spot WB)
362 if(!dt_colorspaces_conversion_matrices_rgb(&img->adobe_XYZ_to_CAM[0][0], g->RGB_to_CAM, g->CAM_to_RGB,
363 img->d65_color_matrix, NULL))
364 {
365 const char *camera = img->camera_makermodel;
366 fprintf(stderr, "[invert] `%s' color matrix not found for 4bayer image\n", camera);
367 dt_control_log(_("`%s' color matrix not found for 4bayer image"), camera);
368 }
369 }
370 }
371
373}
374
376{
379
380 self->gui->widget = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
381 g->label = DTGTK_RESET_LABEL(dt_iop_gui_reset_label_new("", self, &p->color, sizeof(float) * 4));
382 gtk_box_pack_start(GTK_BOX(self->gui->widget), GTK_WIDGET(g->label), TRUE, TRUE, 0);
383
384 g->pickerbuttons = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING));
385 gtk_box_pack_start(GTK_BOX(self->gui->widget), GTK_WIDGET(g->pickerbuttons), TRUE, TRUE, 0);
386
387 GdkRGBA color = (GdkRGBA){.red = p->color[0], .green = p->color[1], .blue = p->color[2], .alpha = 1.0 };
388 g->colorpicker = gtk_color_button_new_with_rgba(&color);
389 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(g->colorpicker), FALSE);
390 gtk_color_button_set_title(GTK_COLOR_BUTTON(g->colorpicker), _("select color of film material"));
391 g_signal_connect(G_OBJECT(g->colorpicker), "color-set", G_CALLBACK(colorpicker_callback), self);
392 gtk_box_pack_start(GTK_BOX(g->pickerbuttons), GTK_WIDGET(g->colorpicker), TRUE, TRUE, 0);
393
394 g->picker = dt_color_picker_new(self, DT_COLOR_PICKER_AREA, GTK_WIDGET(g->pickerbuttons));
395}
396
397// clang-format off
398// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
399// vim: shiftwidth=2 expandtab tabstop=2 cindent
400// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
401// 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_log(const char *msg,...)
Definition control.c:824
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
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:140
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_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:141
int default_group()
Definition invert.c:151
static void gui_update_from_coeffs(dt_iop_module_t *self)
Definition invert.c:172
__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:243
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:311
void gui_update(dt_iop_module_t *self)
Refresh GUI controls from current params and configuration.
Definition invert.c:343
static void colorpicker_callback(GtkColorButton *widget, dt_iop_module_t *self)
Definition invert.c:216
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition invert.c:331
const char * name()
Definition invert.c:131
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:166
void gui_init(dt_iop_module_t *self)
Definition invert.c:375
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition invert.c:161
int flags()
Definition invert.c:156
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition invert.c:337
const char * deprecated_msg()
Definition invert.c:136
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:195
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:88
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_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
dt_iop_buffer_dsc_t dsc_out
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
struct dt_develop_t * dev
dt_image_t image_storage
Definition develop.h:225
char camera_makermodel[128]
Definition image.h:382
int32_t flags
Definition image.h:401
float d65_color_matrix[9]
Definition image.h:421
dt_iop_buffer_dsc_t dsc
Definition image.h:419
float adobe_XYZ_to_CAM[4][3]
Definition image.h:449
uint32_t filters
Definition format.h:89
unsigned int channels
Definition format.h:83
uint8_t xtrans[6][6]
Definition format.h:99
dt_aligned_pixel_t processed_maximum
Definition format.h:114
double RGB_to_CAM[4][3]
Definition invert.c:79
double CAM_to_RGB[3][4]
Definition invert.c:80
GtkWidget * picker
Definition invert.c:78
GtkWidget * colorpicker
Definition invert.c:75
GtkDarktableResetLabel * label
Definition invert.c:76
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