Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
overexposed.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-2014, 2016, 2019 Tobias Ellinghaus.
5 Copyright (C) 2011 Antony Dovgal.
6 Copyright (C) 2011 Ger Siemerink.
7 Copyright (C) 2011-2012 Henrik Andersson.
8 Copyright (C) 2011-2013, 2016 johannes hanika.
9 Copyright (C) 2011 Jérémy Rosen.
10 Copyright (C) 2011 Olivier Tribout.
11 Copyright (C) 2011 Robert Bieber.
12 Copyright (C) 2012 Pascal de Bruijn.
13 Copyright (C) 2012 Richard Wonka.
14 Copyright (C) 2012 Simon Spannagel.
15 Copyright (C) 2012-2014, 2017 Ulrich Pegelow.
16 Copyright (C) 2013, 2020 Aldric Renaudin.
17 Copyright (C) 2014 Edouard Gomez.
18 Copyright (C) 2014-2016 Roman Lebedev.
19 Copyright (C) 2015 Pedro Côrte-Real.
20 Copyright (C) 2017 Heiko Bauke.
21 Copyright (C) 2018, 2020, 2023, 2025-2026 Aurélien PIERRE.
22 Copyright (C) 2018-2019 Edgardo Hoszowski.
23 Copyright (C) 2019 Andreas Schneider.
24 Copyright (C) 2020 Dan Torop.
25 Copyright (C) 2020 Diederik Ter Rahe.
26 Copyright (C) 2020 Hubert Kowalski.
27 Copyright (C) 2020 Pascal Obry.
28 Copyright (C) 2020-2022 Ralf Brown.
29 Copyright (C) 2021 Sakari Kapanen.
30 Copyright (C) 2022 Hanno Schwalm.
31 Copyright (C) 2022 Martin Bařinka.
32 Copyright (C) 2022 Philipp Lutz.
33
34 darktable is free software: you can redistribute it and/or modify
35 it under the terms of the GNU General Public License as published by
36 the Free Software Foundation, either version 3 of the License, or
37 (at your option) any later version.
38
39 darktable is distributed in the hope that it will be useful,
40 but WITHOUT ANY WARRANTY; without even the implied warranty of
41 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
42 GNU General Public License for more details.
43
44 You should have received a copy of the GNU General Public License
45 along with darktable. If not, see <http://www.gnu.org/licenses/>.
46*/
47#ifdef HAVE_CONFIG_H
48#include "common/darktable.h"
49#include "config.h"
50#endif
51#include <stdlib.h>
52
53#include <cairo.h>
54
55#include "common/opencl.h"
56#include "common/imagebuf.h"
57#include "common/iop_profile.h"
58#include "control/control.h"
59#include "develop/develop.h"
60#include "develop/imageop.h"
62
63#include "develop/tiling.h"
64#include "iop/iop_api.h"
65
66DT_MODULE(3)
67
74
76 = { {
77 { 0.0f, 0.0f, 0.0f, 1.0f }, // black
78 { 1.0f, 1.0f, 1.0f, 1.0f } // white
79 },
80 {
81 { 1.0f, 0.0f, 0.0f, 1.0f }, // red
82 { 0.0f, 0.0f, 1.0f, 1.0f } // blue
83 },
84 {
85 { 0.371f, 0.434f, 0.934f, 1.0f }, // purple (#5f6fef)
86 { 0.512f, 0.934f, 0.371f, 1.0f } // green (#83ef5f)
87 } };
88
93
98
99const char *name()
100{
101 return _("overexposed");
102}
103
105{
106 return IOP_GROUP_TECHNICAL;
107}
108
113
115{
116 return IOP_CS_RGB;
117}
118
119
120int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version,
121 void *new_params, const int new_version)
122{
123 // we do no longer have module params in here and just ignore any legacy entries
124 return 0;
125}
126
127
129int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
130 void *const ovoid)
131{
132 const dt_iop_roi_t *const roi_out = &piece->roi_out;
133
134 dt_develop_t *dev = self->dev;
135
136 const int ch = 4;
137
138 const float lower = exp2f(fminf(dev->overexposed.lower, -4.f)); // in EV
139 const float upper = dev->overexposed.upper / 100.0f; // in %
140
141 const int colorscheme = dev->overexposed.colorscheme;
142 const float *const upper_color = dt_iop_overexposed_colors[colorscheme][0];
143 const float *const lower_color = dt_iop_overexposed_colors[colorscheme][1];
144
145 const float *const restrict in = __builtin_assume_aligned((const float *const restrict)ivoid, 64);
146 float *const restrict out = __builtin_assume_aligned((float *const restrict)ovoid, 64);
147
148 const dt_iop_order_iccprofile_info_t *const current_profile = dt_ioppr_get_pipe_current_profile_info(self, pipe);
149
151 {
152 // Any of the RGB channels is out of bounds
154 for(size_t k = 0; k < (size_t)ch * roi_out->width * roi_out->height; k += ch)
155 {
156 if(in[k + 0] >= upper || in[k + 1] >= upper || in[k + 2] >= upper)
157 {
158 copy_pixel(out + k, upper_color);
159 }
160 else if(in[k + 0] <= lower && in[k + 1] <= lower && in[k + 2] <= lower)
161 {
162 copy_pixel(out + k, lower_color);
163 }
164 else
165 {
166 copy_pixel(out + k, in + k);
167 }
168 }
169 }
170
171 else if(dev->overexposed.mode == DT_CLIPPING_PREVIEW_GAMUT && !IS_NULL_PTR(current_profile))
172 {
173 // Gamut is out of bounds
175 for(size_t k = 0; k < (size_t)ch * roi_out->width * roi_out->height; k += ch)
176 {
177 const float luminance = dt_ioppr_get_rgb_matrix_luminance(in + k,
178 current_profile->matrix_in, current_profile->lut_in,
179 current_profile->unbounded_coeffs_in,
180 current_profile->lutsize, current_profile->nonlinearlut);
181
182 // luminance is out of bounds
183 if(luminance >= upper)
184 {
185 copy_pixel(out + k, upper_color);
186 }
187 else if(luminance <= lower)
188 {
189 copy_pixel(out + k, lower_color);
190 }
191 // luminance is ok, so check for saturation
192 else
193 {
194 dt_aligned_pixel_t saturation = { 0.f };
195
196 for_each_channel(c,aligned(saturation, in : 64))
197 {
198 saturation[c] = (in[k + c] - luminance);
199 saturation[c] = sqrtf(saturation[c] * saturation[c] / (luminance * luminance + in[k + c] * in[k + c]));
200 }
201
202 // we got over-saturation, relatively to luminance or absolutely over RGB
203 if(saturation[0] > upper || saturation[1] > upper || saturation[2] > upper ||
204 in[k + 0] >= upper || in[k + 1] >= upper || in[k + 2] >= upper)
205 {
206 copy_pixel(out + k, upper_color);
207 }
208
209 // saturation is fine but we got out-of-bounds RGB
210 else if(in[k + 0] <= lower && in[k + 1] <= lower && in[k + 2] <= lower)
211 {
212 copy_pixel(out + k, lower_color);
213 }
214
215 // evererything is fine
216 else
217 {
218 copy_pixel(out + k, in + k);
219 }
220 }
221 }
222 }
223
224 else if(dev->overexposed.mode == DT_CLIPPING_PREVIEW_LUMINANCE && !IS_NULL_PTR(current_profile))
225 {
226 // Luminance channel is out of bounds
228 for(size_t k = 0; k < (size_t)ch * roi_out->width * roi_out->height; k += ch)
229 {
230 const float luminance = dt_ioppr_get_rgb_matrix_luminance(in + k,
231 current_profile->matrix_in, current_profile->lut_in,
232 current_profile->unbounded_coeffs_in,
233 current_profile->lutsize, current_profile->nonlinearlut);
234
235 if(luminance >= upper)
236 {
237 copy_pixel(out + k, upper_color);
238 }
239
240 else if(luminance <= lower)
241 {
242 copy_pixel(out + k, lower_color);
243 }
244 else
245 {
246 copy_pixel(out + k, in + k);
247 }
248 }
249 }
250
251 else if(dev->overexposed.mode == DT_CLIPPING_PREVIEW_SATURATION && !IS_NULL_PTR(current_profile))
252 {
253 // Show saturation out of bounds where luminance is valid
255 for(size_t k = 0; k < (size_t)ch * roi_out->width * roi_out->height; k += ch)
256 {
257 const float luminance = dt_ioppr_get_rgb_matrix_luminance(in + k,
258 current_profile->matrix_in, current_profile->lut_in,
259 current_profile->unbounded_coeffs_in,
260 current_profile->lutsize, current_profile->nonlinearlut);
261 if(luminance < upper && luminance > lower)
262 {
263 dt_aligned_pixel_t saturation = { 0.f };
264
265 for_each_channel(c,aligned(saturation, in : 64))
266 {
267 saturation[c] = (in[k + c] - luminance);
268 saturation[c] = sqrtf(saturation[c] * saturation[c] / (luminance * luminance + in[k + c] * in[k + c]));
269 }
270
271 // we got over-saturation, relatively to luminance or absolutely over RGB
272 if(saturation[0] > upper || saturation[1] > upper || saturation[2] > upper ||
273 in[k + 0] >= upper || in[k + 1] >= upper || in[k + 2] >= upper)
274 {
275 copy_pixel(out + k, upper_color);
276 }
277 else if(in[k + 0] <= lower && in[k + 1] <= lower && in[k + 2] <= lower)
278 {
279 copy_pixel(out + k, lower_color);
280 }
281 else
282 {
283 copy_pixel(out + k, in + k);
284 }
285 }
286
287 else
288 {
289 copy_pixel(out + k, in + k);
290 }
291 }
292 }
293
295 dt_iop_alpha_copy(ivoid, ovoid, roi_out->width, roi_out->height);
296
297 return 0;
298}
299
300#ifdef HAVE_OPENCL
301int process_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out)
302{
303 const dt_iop_roi_t *const roi_out = &piece->roi_out;
304 dt_develop_t *dev = self->dev;
306
307 cl_int err = -999;
308 const int devid = pipe->devid;
309 const int width = roi_out->width;
310 const int height = roi_out->height;
311
312 const dt_iop_order_iccprofile_info_t *const current_profile = dt_ioppr_get_pipe_current_profile_info(self, pipe);
313
314 const int use_work_profile = (IS_NULL_PTR(current_profile)) ? 0 : 1;
315 cl_mem dev_profile_info = NULL;
316 cl_mem dev_profile_lut = NULL;
318 cl_float *profile_lut_cl = NULL;
319
320 err = dt_ioppr_build_iccprofile_params_cl(current_profile, devid, &profile_info_cl, &profile_lut_cl,
321 &dev_profile_info, &dev_profile_lut);
322 if(err != CL_SUCCESS) goto error;
323
324 const float lower = exp2f(fminf(dev->overexposed.lower, -4.f)); // in EV
325 const float upper = dev->overexposed.upper / 100.0f; // in %
326 const int colorscheme = dev->overexposed.colorscheme;
327
328 const float *upper_color = dt_iop_overexposed_colors[colorscheme][0];
329 const float *lower_color = dt_iop_overexposed_colors[colorscheme][1];
330 const int mode = dev->overexposed.mode;
331
332 size_t sizes[2] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid) };
333 dt_opencl_set_kernel_arg(devid, gd->kernel_overexposed, 0, sizeof(cl_mem), &dev_in);
334 dt_opencl_set_kernel_arg(devid, gd->kernel_overexposed, 1, sizeof(cl_mem), &dev_out);
335 dt_opencl_set_kernel_arg(devid, gd->kernel_overexposed, 2, sizeof(int), &width);
336 dt_opencl_set_kernel_arg(devid, gd->kernel_overexposed, 3, sizeof(int), &height);
337 dt_opencl_set_kernel_arg(devid, gd->kernel_overexposed, 4, sizeof(float), &lower);
338 dt_opencl_set_kernel_arg(devid, gd->kernel_overexposed, 5, sizeof(float), &upper);
339 dt_opencl_set_kernel_arg(devid, gd->kernel_overexposed, 6, 4 * sizeof(float), lower_color);
340 dt_opencl_set_kernel_arg(devid, gd->kernel_overexposed, 7, 4 * sizeof(float), upper_color);
341 dt_opencl_set_kernel_arg(devid, gd->kernel_overexposed, 8, sizeof(cl_mem), (void *)&dev_profile_info);
342 dt_opencl_set_kernel_arg(devid, gd->kernel_overexposed, 9, sizeof(cl_mem), (void *)&dev_profile_lut);
343 dt_opencl_set_kernel_arg(devid, gd->kernel_overexposed, 10, sizeof(int), (void *)&use_work_profile);
344 dt_opencl_set_kernel_arg(devid, gd->kernel_overexposed, 11, sizeof(int), (void *)&mode);
345 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_overexposed, sizes);
346 if(err != CL_SUCCESS) goto error;
347 return TRUE;
348
349error:
350 dt_print(DT_DEBUG_OPENCL, "[opencl_overexposed] couldn't enqueue kernel! %i\n", err);
351 return FALSE;
352}
353#endif
354
355void tiling_callback(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, const struct dt_dev_pixelpipe_iop_t *piece, struct dt_develop_tiling_t *tiling)
356{
357 tiling->factor = 3.0f; // in + out + temp
358 tiling->factor_cl = 3.0f;
359 tiling->maxbuf = 1.0f;
360 tiling->maxbuf_cl = 1.0f;
361 tiling->overhead = 0;
362 tiling->overlap = 0;
363 tiling->xalign = 1;
364 tiling->yalign = 1;
365}
366
367
369{
370 const int program = 2; // basic.cl from programs.conf
373 module->data = gd;
374 gd->kernel_overexposed = dt_opencl_create_kernel(program, "overexposed");
375}
376
377
384
387{
388 if(pipe->type != DT_DEV_PIXELPIPE_FULL || !self->dev->overexposed.enabled || !self->dev->gui_attached)
389 piece->enabled = 0;
390}
391
393{
394 piece->data = NULL;
395 piece->data_size = 0;
396}
397
399{
400}
401
403{
404 module->params = calloc(1, sizeof(dt_iop_overexposed_t));
405 module->default_params = calloc(1, sizeof(dt_iop_overexposed_t));
406 module->hide_enable_button = 1;
407 module->default_enabled = 1;
408 module->params_size = sizeof(dt_iop_overexposed_t);
409 module->gui_data = NULL;
410
411 // This module permanently bypasses the cache because it takes input from GUI
412 // and doesn't leave internal parameters to compute an integrity hash on.
414}
415
416// clang-format off
417// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
418// vim: shiftwidth=2 expandtab tabstop=2 cindent
419// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
420// 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
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
@ IOP_CS_RGB
const dt_colormatrix_t dt_aligned_pixel_t out
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1542
@ DT_DEBUG_OPENCL
Definition darktable.h:722
#define DT_ALIGNED_ARRAY
Definition darktable.h:388
static void copy_pixel(float *const __restrict__ out, const float *const __restrict__ in)
Definition darktable.h:688
#define for_each_channel(_var,...)
Definition darktable.h:662
#define DT_MODULE(MODVER)
Definition darktable.h:140
#define dt_free(ptr)
Definition darktable.h:456
#define __DT_CLONE_TARGETS__
Definition darktable.h:367
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:258
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:281
void dt_iop_params_t
Definition dev_history.h:41
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
Definition develop.h:118
@ DT_CLIPPING_PREVIEW_ANYRGB
Definition develop.h:151
@ DT_CLIPPING_PREVIEW_GAMUT
Definition develop.h:150
@ DT_CLIPPING_PREVIEW_SATURATION
Definition develop.h:153
@ DT_CLIPPING_PREVIEW_LUMINANCE
Definition develop.h:152
void dt_iop_set_cache_bypass(dt_iop_module_t *module, gboolean state)
Definition imageop.c:2915
@ IOP_FLAGS_HIDDEN
Definition imageop.h:170
@ IOP_FLAGS_ALLOW_TILING
Definition imageop.h:169
@ IOP_FLAGS_ONE_INSTANCE
Definition imageop.h:172
@ IOP_FLAGS_NO_HISTORY_STACK
Definition imageop.h:174
@ IOP_GROUP_TECHNICAL
Definition imageop.h:143
void *const ovoid
dt_iop_order_iccprofile_info_t * dt_ioppr_get_pipe_current_profile_info(dt_iop_module_t *module, const struct dt_dev_pixelpipe_t *pipe)
cl_int dt_ioppr_build_iccprofile_params_cl(const dt_iop_order_iccprofile_info_t *const profile_info, const int devid, dt_colorspaces_iccprofile_info_cl_t **_profile_info_cl, cl_float **_profile_lut_cl, cl_mem *_dev_profile_info, cl_mem *_dev_profile_lut)
float *const restrict luminance
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
float dt_aligned_pixel_t[4]
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
Definition opencl.c:2136
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2030
void dt_opencl_free_kernel(const int kernel)
Definition opencl.c:2073
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:2127
#define ROUNDUPDHT(a, b)
Definition opencl.h:82
#define ROUNDUPDWD(a, b)
Definition opencl.h:81
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)
int default_group()
__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)
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
const char * name()
Definition overexposed.c:99
void tiling_callback(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, const struct dt_dev_pixelpipe_iop_t *piece, struct dt_develop_tiling_t *tiling)
void cleanup_global(dt_iop_module_so_t *module)
dt_iop_overexposed_colorscheme_t
Definition overexposed.c:69
@ DT_IOP_OVEREXPOSED_BLACKWHITE
Definition overexposed.c:70
@ DT_IOP_OVEREXPOSED_REDBLUE
Definition overexposed.c:71
@ DT_IOP_OVEREXPOSED_PURPLEGREEN
Definition overexposed.c:72
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
int flags()
static const float DT_ALIGNED_ARRAY dt_iop_overexposed_colors[][2][4]
Definition overexposed.c:76
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
void init_global(dt_iop_module_so_t *module)
int process_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out)
int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params, const int new_version)
@ DT_DEV_PIXELPIPE_FULL
Definition pixelpipe.h:39
struct dt_iop_module_t *void * data
dt_dev_pixelpipe_type_t type
int32_t gui_attached
Definition develop.h:162
dt_clipping_preview_mode_t mode
Definition develop.h:455
float lower
Definition develop.h:453
dt_dev_overexposed_colorscheme_t colorscheme
Definition develop.h:452
gboolean enabled
Definition develop.h:387
float upper
Definition develop.h:454
struct dt_develop_t::@21 overexposed
dt_iop_global_data_t * data
Definition imageop.h:233
struct dt_develop_t * dev
Definition imageop.h:296
dt_iop_global_data_t * global_data
Definition imageop.h:314
Region of interest passed through the pixelpipe.
Definition imageop.h:72