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 "system/macros.h"
49#include "system/mem_alloc.h"
51#include "common/logging.h"
52#include "system/openmp.h"
53#include "system/simd.h"
55#include "config.h"
56#endif
57#include "develop/imageop_gui.h"
58#include <stdlib.h>
59
60#include <cairo.h>
61
62#include "common/opencl.h"
63#include "develop/iop_profile.h"
64#include "develop/develop.h"
65#include "develop/imageop.h"
67
68#include "develop/tiling.h"
69#include "iop/iop_api.h"
70
71DT_MODULE(3)
72
79
81 = { {
82 { 0.0f, 0.0f, 0.0f, 1.0f }, // black
83 { 1.0f, 1.0f, 1.0f, 1.0f } // white
84 },
85 {
86 { 1.0f, 0.0f, 0.0f, 1.0f }, // red
87 { 0.0f, 0.0f, 1.0f, 1.0f } // blue
88 },
89 {
90 { 0.371f, 0.434f, 0.934f, 1.0f }, // purple (#5f6fef)
91 { 0.512f, 0.934f, 0.371f, 1.0f } // green (#83ef5f)
92 } };
93
98
103
104const char *name()
105{
106 return _("overexposed");
107}
108
110{
111 return IOP_GROUP_TECHNICAL;
112}
113
118
120{
121 return IOP_CS_RGB;
122}
123
124
125int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version,
126 void *new_params, const int new_version)
127{
128 // we do no longer have module params in here and just ignore any legacy entries
129 return 0;
130}
131
132
134int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
135 void *const ovoid)
136{
137 const dt_iop_roi_t *const roi_out = &piece->roi_out;
138
139 dt_develop_t *dev = self->dev;
140
141 const int ch = 4;
142
143 const float lower = exp2f(fminf(dev->overexposed.lower, -4.f)); // in EV
144 const float upper = dev->overexposed.upper / 100.0f; // in %
145
146 const int colorscheme = dev->overexposed.colorscheme;
147 const float *const upper_color = dt_iop_overexposed_colors[colorscheme][0];
148 const float *const lower_color = dt_iop_overexposed_colors[colorscheme][1];
149
150 const float *const restrict in = __builtin_assume_aligned((const float *const restrict)ivoid, 64);
151 float *const restrict out = __builtin_assume_aligned((float *const restrict)ovoid, 64);
152
153 const dt_iop_order_iccprofile_info_t *const current_profile = dt_ioppr_get_pipe_current_profile_info(self, pipe);
154
156 {
157 // Any of the RGB channels is out of bounds
159 for(size_t k = 0; k < (size_t)ch * roi_out->width * roi_out->height; k += ch)
160 {
161 if(in[k + 0] >= upper || in[k + 1] >= upper || in[k + 2] >= upper)
162 {
163 copy_pixel(out + k, upper_color);
164 }
165 else if(in[k + 0] <= lower && in[k + 1] <= lower && in[k + 2] <= lower)
166 {
167 copy_pixel(out + k, lower_color);
168 }
169 else
170 {
171 copy_pixel(out + k, in + k);
172 }
173 }
174 }
175
176 else if(dev->overexposed.mode == DT_CLIPPING_PREVIEW_GAMUT && !IS_NULL_PTR(current_profile))
177 {
178 // Gamut is out of bounds
180 for(size_t k = 0; k < (size_t)ch * roi_out->width * roi_out->height; k += ch)
181 {
182 const float luminance = dt_ioppr_get_rgb_matrix_luminance(in + k,
183 current_profile->matrix_in, current_profile->lut_in,
184 current_profile->unbounded_coeffs_in,
185 current_profile->lutsize, current_profile->nonlinearlut);
186
187 // luminance is out of bounds
188 if(luminance >= upper)
189 {
190 copy_pixel(out + k, upper_color);
191 }
192 else if(luminance <= lower)
193 {
194 copy_pixel(out + k, lower_color);
195 }
196 // luminance is ok, so check for saturation
197 else
198 {
199 dt_aligned_pixel_t saturation = { 0.f };
200
201 for_each_channel(c,aligned(saturation, in : 64))
202 {
203 saturation[c] = (in[k + c] - luminance);
204 saturation[c] = sqrtf(saturation[c] * saturation[c] / (luminance * luminance + in[k + c] * in[k + c]));
205 }
206
207 // we got over-saturation, relatively to luminance or absolutely over RGB
208 if(saturation[0] > upper || saturation[1] > upper || saturation[2] > upper ||
209 in[k + 0] >= upper || in[k + 1] >= upper || in[k + 2] >= upper)
210 {
211 copy_pixel(out + k, upper_color);
212 }
213
214 // saturation is fine but we got out-of-bounds RGB
215 else if(in[k + 0] <= lower && in[k + 1] <= lower && in[k + 2] <= lower)
216 {
217 copy_pixel(out + k, lower_color);
218 }
219
220 // evererything is fine
221 else
222 {
223 copy_pixel(out + k, in + k);
224 }
225 }
226 }
227 }
228
229 else if(dev->overexposed.mode == DT_CLIPPING_PREVIEW_LUMINANCE && !IS_NULL_PTR(current_profile))
230 {
231 // Luminance channel is out of bounds
233 for(size_t k = 0; k < (size_t)ch * roi_out->width * roi_out->height; k += ch)
234 {
235 const float luminance = dt_ioppr_get_rgb_matrix_luminance(in + k,
236 current_profile->matrix_in, current_profile->lut_in,
237 current_profile->unbounded_coeffs_in,
238 current_profile->lutsize, current_profile->nonlinearlut);
239
240 if(luminance >= upper)
241 {
242 copy_pixel(out + k, upper_color);
243 }
244
245 else if(luminance <= lower)
246 {
247 copy_pixel(out + k, lower_color);
248 }
249 else
250 {
251 copy_pixel(out + k, in + k);
252 }
253 }
254 }
255
256 else if(dev->overexposed.mode == DT_CLIPPING_PREVIEW_SATURATION && !IS_NULL_PTR(current_profile))
257 {
258 // Show saturation out of bounds where luminance is valid
260 for(size_t k = 0; k < (size_t)ch * roi_out->width * roi_out->height; k += ch)
261 {
262 const float luminance = dt_ioppr_get_rgb_matrix_luminance(in + k,
263 current_profile->matrix_in, current_profile->lut_in,
264 current_profile->unbounded_coeffs_in,
265 current_profile->lutsize, current_profile->nonlinearlut);
266 if(luminance < upper && luminance > lower)
267 {
268 dt_aligned_pixel_t saturation = { 0.f };
269
270 for_each_channel(c,aligned(saturation, in : 64))
271 {
272 saturation[c] = (in[k + c] - luminance);
273 saturation[c] = sqrtf(saturation[c] * saturation[c] / (luminance * luminance + in[k + c] * in[k + c]));
274 }
275
276 // we got over-saturation, relatively to luminance or absolutely over RGB
277 if(saturation[0] > upper || saturation[1] > upper || saturation[2] > upper ||
278 in[k + 0] >= upper || in[k + 1] >= upper || in[k + 2] >= upper)
279 {
280 copy_pixel(out + k, upper_color);
281 }
282 else if(in[k + 0] <= lower && in[k + 1] <= lower && in[k + 2] <= lower)
283 {
284 copy_pixel(out + k, lower_color);
285 }
286 else
287 {
288 copy_pixel(out + k, in + k);
289 }
290 }
291
292 else
293 {
294 copy_pixel(out + k, in + k);
295 }
296 }
297 }
298
300 dt_iop_alpha_copy(ivoid, ovoid, roi_out->width, roi_out->height);
301
302 return 0;
303}
304
305#ifdef HAVE_OPENCL
306int 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)
307{
308 const dt_iop_roi_t *const roi_out = &piece->roi_out;
309 dt_develop_t *dev = self->dev;
311
312 cl_int err = -999;
313 const int devid = pipe->devid;
314 const int width = roi_out->width;
315 const int height = roi_out->height;
316
317 const dt_iop_order_iccprofile_info_t *const current_profile = dt_ioppr_get_pipe_current_profile_info(self, pipe);
318
319 const int use_work_profile = (IS_NULL_PTR(current_profile)) ? 0 : 1;
320 cl_mem dev_profile_info = NULL;
321 cl_mem dev_profile_lut = NULL;
323 cl_float *profile_lut_cl = NULL;
324
325 err = dt_ioppr_build_iccprofile_params_cl(current_profile, devid, &profile_info_cl, &profile_lut_cl,
326 &dev_profile_info, &dev_profile_lut);
327 if(err != CL_SUCCESS) goto error;
328
329 const float lower = exp2f(fminf(dev->overexposed.lower, -4.f)); // in EV
330 const float upper = dev->overexposed.upper / 100.0f; // in %
331 const int colorscheme = dev->overexposed.colorscheme;
332
333 const float *upper_color = dt_iop_overexposed_colors[colorscheme][0];
334 const float *lower_color = dt_iop_overexposed_colors[colorscheme][1];
335 const int mode = dev->overexposed.mode;
336
337 size_t sizes[2] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid) };
338 dt_opencl_set_kernel_arg(devid, gd->kernel_overexposed, 0, sizeof(cl_mem), &dev_in);
339 dt_opencl_set_kernel_arg(devid, gd->kernel_overexposed, 1, sizeof(cl_mem), &dev_out);
340 dt_opencl_set_kernel_arg(devid, gd->kernel_overexposed, 2, sizeof(int), &width);
341 dt_opencl_set_kernel_arg(devid, gd->kernel_overexposed, 3, sizeof(int), &height);
342 dt_opencl_set_kernel_arg(devid, gd->kernel_overexposed, 4, sizeof(float), &lower);
343 dt_opencl_set_kernel_arg(devid, gd->kernel_overexposed, 5, sizeof(float), &upper);
344 dt_opencl_set_kernel_arg(devid, gd->kernel_overexposed, 6, 4 * sizeof(float), lower_color);
345 dt_opencl_set_kernel_arg(devid, gd->kernel_overexposed, 7, 4 * sizeof(float), upper_color);
346 dt_opencl_set_kernel_arg(devid, gd->kernel_overexposed, 8, sizeof(cl_mem), (void *)&dev_profile_info);
347 dt_opencl_set_kernel_arg(devid, gd->kernel_overexposed, 9, sizeof(cl_mem), (void *)&dev_profile_lut);
348 dt_opencl_set_kernel_arg(devid, gd->kernel_overexposed, 10, sizeof(int), (void *)&use_work_profile);
349 dt_opencl_set_kernel_arg(devid, gd->kernel_overexposed, 11, sizeof(int), (void *)&mode);
350 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_overexposed, sizes);
351 if(err != CL_SUCCESS) goto error;
352 return TRUE;
353
354error:
355 dt_print(DT_DEBUG_OPENCL, "[opencl_overexposed] couldn't enqueue kernel! %i\n", err);
356 return FALSE;
357}
358#endif
359
360void 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)
361{
362 tiling->factor = 3.0f; // in + out + temp
363 tiling->factor_cl = 3.0f;
364 tiling->maxbuf = 1.0f;
365 tiling->maxbuf_cl = 1.0f;
366 tiling->overhead = 0;
367 tiling->overlap = 0;
368 tiling->xalign = 1;
369 tiling->yalign = 1;
370}
371
372
374{
375 const int program = 2; // basic.cl from programs.conf
378 module->data = gd;
379 gd->kernel_overexposed = dt_opencl_create_kernel(program, "overexposed");
380}
381
382
389
392{
393 if(pipe->type != DT_DEV_PIXELPIPE_FULL || !self->dev->overexposed.enabled || !self->dev->gui_attached)
394 piece->enabled = 0;
395}
396
398{
399 piece->data = NULL;
400 piece->data_size = 0;
401}
402
404{
405}
406
408{
409 module->params = calloc(1, sizeof(dt_iop_overexposed_t));
410 module->default_params = calloc(1, sizeof(dt_iop_overexposed_t));
411 module->hide_enable_button = 1;
412 module->default_enabled = 1;
413 module->params_size = sizeof(dt_iop_overexposed_t);
414
415 // This module permanently bypasses the cache because it takes input from GUI
416 // and doesn't leave internal parameters to compute an integrity hash on.
418}
419
420// clang-format off
421// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
422// vim: shiftwidth=2 expandtab tabstop=2 cindent
423// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
424// 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
@ IOP_CS_RGB
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)
build the required parameters for a kernel that uses a profile info.
const dt_colormatrix_t dt_aligned_pixel_t out
void dt_iop_params_t
Definition dev_history.h:43
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)
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
Definition develop.h:123
@ DT_CLIPPING_PREVIEW_ANYRGB
Definition develop.h:156
@ DT_CLIPPING_PREVIEW_GAMUT
Definition develop.h:155
@ DT_CLIPPING_PREVIEW_SATURATION
Definition develop.h:158
@ DT_CLIPPING_PREVIEW_LUMINANCE
Definition develop.h:157
void dt_iop_set_cache_bypass(dt_iop_module_t *module, gboolean state)
Definition imageop.c:1669
@ IOP_FLAGS_HIDDEN
Definition imageop.h:189
@ IOP_FLAGS_ALLOW_TILING
Definition imageop.h:188
@ IOP_FLAGS_ONE_INSTANCE
Definition imageop.h:191
@ IOP_FLAGS_NO_HISTORY_STACK
Definition imageop.h:193
@ IOP_GROUP_TECHNICAL
Definition imageop.h:162
void *const ovoid
@ 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 luminance
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
#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_ALIGNED_ARRAY
Align an object on a cacheline boundary, so AVX2 can load it whole.
Definition mem_alloc.h:80
#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(MODVER)
Instantiate the version handshake every module must export. Use once, at file scope,...
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_PARALLEL_FOR__(...)
Definition openmp.h:95
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()
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:74
@ DT_IOP_OVEREXPOSED_BLACKWHITE
Definition overexposed.c:75
@ DT_IOP_OVEREXPOSED_REDBLUE
Definition overexposed.c:76
@ DT_IOP_OVEREXPOSED_PURPLEGREEN
Definition overexposed.c:77
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:81
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:43
static void copy_pixel(float *const __restrict__ out, const float *const __restrict__ in)
Definition simd.h:217
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
Definition simd.h:53
#define for_each_channel(_var,...)
Definition simd.h:87
The device-side view of a dt_iop_order_iccprofile_info_t: the scalar fields only.
struct dt_iop_module_t *void * data
dt_dev_pixelpipe_type_t type
int32_t gui_attached
Definition develop.h:167
dt_clipping_preview_mode_t mode
Definition develop.h:441
float lower
Definition develop.h:439
struct dt_develop_t::@15 overexposed
dt_dev_overexposed_colorscheme_t colorscheme
Definition develop.h:438
gboolean enabled
Definition develop.h:370
float upper
Definition develop.h:440
dt_iop_global_data_t * data
Definition imageop.h:238
struct dt_develop_t * dev
Definition imageop.h:311
dt_iop_global_data_t * global_data
Definition imageop.h:337
A profile reduced to the arithmetic the pixel loop can run: two matrices and six tone-curve LUTs,...
int nonlinearlut
Non-zero when the profile has tone curves at all; tested as a boolean everywhere, but it is really th...
int lutsize
Entry count of each of the six LUTs. Always 65536 in practice: both callers of dt_ioppr_init_profile_...
float * lut_in[3]
Per-channel encoded -> linear tone curve, lutsize entries each, sampled over [0,1]....
dt_colormatrix_t matrix_in
RGB -> XYZ (D50), row-major. matrix_in[1][*] is the luminance row. NaN in [0][0] marks the whole prof...
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__