Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
highlights.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2010 Bruce Guenter.
4 Copyright (C) 2010-2011 Henrik Andersson.
5 Copyright (C) 2010-2014, 2016 johannes hanika.
6 Copyright (C) 2010 Stuart Henderson.
7 Copyright (C) 2011 Antony Dovgal.
8 Copyright (C) 2011 Robert Bieber.
9 Copyright (C) 2011-2014, 2016, 2019 Tobias Ellinghaus.
10 Copyright (C) 2011-2012, 2014, 2016-2017 Ulrich Pegelow.
11 Copyright (C) 2012, 2015 Edouard Gomez.
12 Copyright (C) 2012 Jérémy Rosen.
13 Copyright (C) 2012 Richard Wonka.
14 Copyright (C) 2013, 2020 Aldric Renaudin.
15 Copyright (C) 2014, 2016 Dan Torop.
16 Copyright (C) 2014-2016 Roman Lebedev.
17 Copyright (C) 2015-2016 Pedro Côrte-Real.
18 Copyright (C) 2017 Heiko Bauke.
19 Copyright (C) 2017 luzpaz.
20 Copyright (C) 2018, 2020-2026 Aurélien PIERRE.
21 Copyright (C) 2018 Edgardo Hoszowski.
22 Copyright (C) 2018 Maurizio Paglia.
23 Copyright (C) 2018-2020, 2022 Pascal Obry.
24 Copyright (C) 2018 rawfiner.
25 Copyright (C) 2019 Andreas Schneider.
26 Copyright (C) 2019 Diederik ter Rahe.
27 Copyright (C) 2019-2020, 2022 Hanno Schwalm.
28 Copyright (C) 2020 Chris Elston.
29 Copyright (C) 2020, 2022 Diederik Ter Rahe.
30 Copyright (C) 2020-2021 Ralf Brown.
31 Copyright (C) 2021 Hubert Kowalski.
32 Copyright (C) 2022 Martin Bařinka.
33 Copyright (C) 2022 Philipp Lutz.
34 Copyright (C) 2022 Victor Forsiuk.
35 Copyright (C) 2023 Alynx Zhou.
36 Copyright (C) 2023 Guillaume Stutin.
37 Copyright (C) 2023 Luca Zulberti.
38
39 darktable is free software: you can redistribute it and/or modify
40 it under the terms of the GNU General Public License as published by
41 the Free Software Foundation, either version 3 of the License, or
42 (at your option) any later version.
43
44 darktable is distributed in the hope that it will be useful,
45 but WITHOUT ANY WARRANTY; without even the implied warranty of
46 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
47 GNU General Public License for more details.
48
49 You should have received a copy of the GNU General Public License
50 along with darktable. If not, see <http://www.gnu.org/licenses/>.
51 */
52#ifdef HAVE_CONFIG_H
53#include "common/darktable.h"
54#include "config.h"
55#endif
56#include "bauhaus/bauhaus.h"
57#include "common/box_filters.h"
58#include "common/bspline.h"
60#include "common/gaussian.h"
61#include "common/imagebuf.h"
62#include "common/opencl.h"
63#include "common/solvers/choleski.h" // dense Cholesky solve (SPD) for the direct biharmonic dome (needs control.h)
64#include "control/control.h"
65#include "develop/develop.h"
66#include "develop/imageop.h"
67#include "develop/imageop_gui.h"
70#include "develop/tiling.h"
71#include <assert.h>
72#include <math.h>
73#include <stdlib.h>
74#include <string.h>
75
76#include "gui/gtk.h"
77#include "iop/iop_api.h"
78// Highlights internals: harmonic mode is a set of compiled per-stage TUs; include only the
79// shared types (params + per-module OpenCL global data) and the high-level harmonic driver.
80#include "iop/highlights/clip.h"
83#include "iop/highlights/lch.h"
85
86#include <gtk/gtk.h>
87#include <inttypes.h>
88
89// Downsampling factor for guided-laplacian
90
92
103
104const char *name()
105{
106 return _("_highlight reconstruction");
107}
108
109const char **description(struct dt_iop_module_t *self)
110{
111 return dt_iop_set_description(self, _("avoid magenta highlights and try to recover highlights colors"),
112 _("corrective"), _("linear, raw, scene-referred"), _("reconstruction, raw"),
113 _("linear, raw, scene-referred"));
114}
115
117{
118 return IOP_GROUP_REPAIR;
119}
120
125
127{
128 if(piece && piece->dsc_in.cst != IOP_CS_RAW) return IOP_CS_RGB;
129 return IOP_CS_RAW;
130}
131
134{
135 default_output_format(self, pipe, piece, dsc);
136}
137
138void autoset(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe,
139 const struct dt_dev_pixelpipe_iop_t *piece, const void *input)
140{
142 const dt_iop_roi_t *const roi_out = &piece->roi_out;
143 const float *const restrict in = (const float *)input;
144 float max_RGB[3] = { 0.0f };
145
146 __OMP_PARALLEL_FOR__(reduction(max : max_RGB[:3]) collapse(2))
147 for(size_t i = 0; i < roi_out->height; i++)
148 for(size_t j = 0; j < roi_out->width; j++)
149 {
150 const size_t channel = (piece->dsc_in.filters == 9u)
151 ? FCxtrans(i, j, roi_out, piece->dsc_in.xtrans)
152 : FC(i + roi_out->y, j + roi_out->x, piece->dsc_in.filters);
153 const float pixel_max = in[i * roi_out->width + j] / piece->dsc_in.processed_maximum[channel];
154 max_RGB[channel] = MAX(max_RGB[channel], pixel_max);
155 }
156
157 p->clip = MIN(MIN(max_RGB[0], max_RGB[1]), max_RGB[2]);
158}
159
160int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params,
161 const int new_version)
162{
163 if(old_version == 1 && new_version == 4)
164 {
165 /*
166 params of v2 :
167 float clip
168 + params of v3
169 + params of v4
170 */
171 memcpy(new_params, old_params,
172 sizeof(dt_iop_highlights_params_t) - 5 * sizeof(float) - 2 * sizeof(int)
175 n->clip = 1.0f;
176 n->noise_level = 0.0f;
177 n->reconstructing = 0.4f;
178 n->combine = 2.f;
179 n->debugmode = 0;
180 n->iterations = 1;
181 n->scales = 5;
182 n->solid_color = 0.f;
183 return 0;
184 }
185 if(old_version == 2 && new_version == 4)
186 {
187 /*
188 params of v3 :
189 float noise_level;
190 int iterations;
191 dt_atrous_wavelets_scales_t scales;
192 float reconstructing;
193 float combine;
194 int debugmode;
195 + params of v4
196 */
197 memcpy(new_params, old_params,
198 sizeof(dt_iop_highlights_params_t) - 4 * sizeof(float) - 2 * sizeof(int)
201 n->noise_level = 0.0f;
202 n->reconstructing = 0.4f;
203 n->combine = 2.f;
204 n->debugmode = 0;
205 n->iterations = 1;
206 n->scales = 5;
207 n->solid_color = 0.f;
208 return 0;
209 }
210 if(old_version == 3 && new_version == 4)
211 {
212 /*
213 params of v4 :
214 float solid_color;
215 */
216 memcpy(new_params, old_params, sizeof(dt_iop_highlights_params_t) - sizeof(float));
218 n->solid_color = 0.f;
219 return 0;
220 }
221
222 return 1;
223}
224
225#ifdef HAVE_OPENCL
226
227int process_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece,
228 cl_mem dev_in, cl_mem dev_out)
229{
230 const dt_iop_roi_t *const roi_in = &piece->roi_in;
231 const dt_iop_roi_t *const roi_out = &piece->roi_out;
235
236 const uint32_t filters = piece->dsc_in.filters;
237 const int devid = pipe->devid;
238 const int width = roi_in->width;
239 const int height = roi_in->height;
240
241 /* This transient preview belongs to the central darkroom view. Do not infer
242 * its owner from ROI geometry: at zoom-to-fit the main and navigation pipes
243 * can produce identical dimensions while both still need distinct outputs. */
244 const gboolean visualizing
245 = !IS_NULL_PTR(g) && g->show_visualize && self->dev->gui_attached && pipe == self->dev->pipe;
246
247 cl_int err = DT_OPENCL_DEFAULT_ERROR;
248 cl_mem dev_xtrans = NULL;
249
250 // this works for bayer and X-Trans sensors
251 if(visualizing)
252 {
253 float clips[4] = { 0.995f * d->clip * piece->dsc_in.processed_maximum[0],
254 0.995f * d->clip * piece->dsc_in.processed_maximum[1],
255 0.995f * d->clip * piece->dsc_in.processed_maximum[2], d->clip };
256
257 cl_mem dev_clips = dt_opencl_copy_host_to_device_constant(devid, 4 * sizeof(float), clips);
258 if(IS_NULL_PTR(dev_clips)) goto error;
259
260 // bayer sensor raws with LCH mode
261 size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
262 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_false_color, 0, sizeof(cl_mem), (void *)&dev_in);
263 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_false_color, 1, sizeof(cl_mem), (void *)&dev_out);
264 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_false_color, 2, sizeof(int), (void *)&width);
265 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_false_color, 3, sizeof(int), (void *)&height);
266 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_false_color, 4, sizeof(int), (void *)&roi_out->x);
267 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_false_color, 5, sizeof(int), (void *)&roi_out->y);
268 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_false_color, 6, sizeof(int), (void *)&filters);
269 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_false_color, 7, sizeof(cl_mem), (void *)&dev_clips);
270
272 if(err != CL_SUCCESS) goto error;
273
274 /* The clipping preview is the final output of this module. Blending would
275 * interpret PASSTHRU as a channel-display request and replace RAW output
276 * with zeroes before the downstream demosaic stage can display it. */
278 ((dt_dev_pixelpipe_t *)pipe)->bypass_blendif = 1;
280 return TRUE;
281 }
282
283 const float clip = d->clip
284 * fminf(piece->dsc_in.processed_maximum[0],
285 fminf(piece->dsc_in.processed_maximum[1], piece->dsc_in.processed_maximum[2]));
286
287 if(!filters)
288 {
289 // non-raw images use dedicated kernel which just clips
290 size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
291 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_4f_clip, 0, sizeof(cl_mem), (void *)&dev_in);
292 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_4f_clip, 1, sizeof(cl_mem), (void *)&dev_out);
293 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_4f_clip, 2, sizeof(int), (void *)&width);
294 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_4f_clip, 3, sizeof(int), (void *)&height);
295 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_4f_clip, 4, sizeof(int), (void *)&d->mode);
296 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_4f_clip, 5, sizeof(float), (void *)&clip);
298 if(err != CL_SUCCESS) goto error;
299 }
300 else if(d->mode == DT_IOP_HIGHLIGHTS_CLIP || d->mode > DT_IOP_HIGHLIGHTS_HARMONIC)
301 {
302 // raw images with clip mode (both bayer and xtrans)
303 // This is also the fallback if d->mode is set with something invalid
304 size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
305 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_clip, 0, sizeof(cl_mem), (void *)&dev_in);
306 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_clip, 1, sizeof(cl_mem), (void *)&dev_out);
307 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_clip, 2, sizeof(int), (void *)&width);
308 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_clip, 3, sizeof(int), (void *)&height);
309 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_clip, 4, sizeof(float), (void *)&clip);
310 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_clip, 5, sizeof(int), (void *)&roi_out->x);
311 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_clip, 6, sizeof(int), (void *)&roi_out->y);
312 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_clip, 7, sizeof(int), (void *)&filters);
314 if(err != CL_SUCCESS) goto error;
315 }
316 else if(d->mode == DT_IOP_HIGHLIGHTS_LCH && filters != 9u)
317 {
318 // bayer sensor raws with LCH mode
319 size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
320 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_bayer, 0, sizeof(cl_mem), (void *)&dev_in);
321 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_bayer, 1, sizeof(cl_mem), (void *)&dev_out);
322 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_bayer, 2, sizeof(int), (void *)&width);
323 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_bayer, 3, sizeof(int), (void *)&height);
324 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_bayer, 4, sizeof(float), (void *)&clip);
325 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_bayer, 5, sizeof(int), (void *)&roi_out->x);
326 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_bayer, 6, sizeof(int), (void *)&roi_out->y);
327 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_bayer, 7, sizeof(int), (void *)&filters);
329 if(err != CL_SUCCESS) goto error;
330 }
331 else if(d->mode == DT_IOP_HIGHLIGHTS_LCH && filters == 9u)
332 {
333 // xtrans sensor raws with LCH mode
334 int blocksizex, blocksizey;
335
337 .xfactor = 1,
338 .yoffset = 2 * 2,
339 .yfactor = 1,
340 .cellsize = sizeof(float),
341 .overhead = 0,
342 .sizex = 1 << 8,
343 .sizey = 1 << 8 };
344
346 {
347 blocksizex = locopt.sizex;
348 blocksizey = locopt.sizey;
349 }
350 else
351 blocksizex = blocksizey = 1;
352
353 dev_xtrans = dt_opencl_copy_host_to_device_constant(devid, sizeof(piece->dsc_in.xtrans),
354 (void *)piece->dsc_in.xtrans);
355 if(IS_NULL_PTR(dev_xtrans)) goto error;
356
357 size_t sizes[] = { ROUNDUP(width, blocksizex), ROUNDUP(height, blocksizey), 1 };
358 size_t local[] = { blocksizex, blocksizey, 1 };
359 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_xtrans, 0, sizeof(cl_mem), (void *)&dev_in);
360 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_xtrans, 1, sizeof(cl_mem), (void *)&dev_out);
361 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_xtrans, 2, sizeof(int), (void *)&width);
362 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_xtrans, 3, sizeof(int), (void *)&height);
363 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_xtrans, 4, sizeof(float), (void *)&clip);
364 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_xtrans, 5, sizeof(int), (void *)&roi_out->x);
365 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_xtrans, 6, sizeof(int), (void *)&roi_out->y);
366 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_xtrans, 7, sizeof(cl_mem), (void *)&dev_xtrans);
368 sizeof(float) * (blocksizex + 4) * (blocksizey + 4), NULL);
369
371 if(err != CL_SUCCESS) goto error;
372 }
373 else if(d->mode == DT_IOP_HIGHLIGHTS_HARMONIC)
374 {
375 // hybrid CPU-orchestrated driver: the raw roundtrips through the host inside the module
376 // (bit-identical to the CPU path), the rest of the pipe stays on the GPU
377 const dt_aligned_pixel_t clips = { 0.995f * d->clip * piece->dsc_in.processed_maximum[0],
378 0.995f * d->clip * piece->dsc_in.processed_maximum[1],
379 0.995f * d->clip * piece->dsc_in.processed_maximum[2], clip };
380 err = process_harmonic_cl(self, pipe, piece, dev_in, dev_out, roi_in, roi_out, clips);
381 if(err != CL_SUCCESS) goto error;
382 }
383 else if(d->mode == DT_IOP_HIGHLIGHTS_LAPLACIAN)
384 {
385 const dt_aligned_pixel_t clips = { 0.995f * d->clip * piece->dsc_in.processed_maximum[0],
386 0.995f * d->clip * piece->dsc_in.processed_maximum[1],
387 0.995f * d->clip * piece->dsc_in.processed_maximum[2], clip };
388 err = (filters == 9u) ? process_laplacian_xtrans_cl(self, pipe, piece, dev_in, dev_out, roi_in, roi_out, clips)
389 : process_laplacian_bayer_cl(self, pipe, piece, dev_in, dev_out, roi_in, roi_out, clips);
390 if(err != CL_SUCCESS) goto error;
391 }
392
394 return TRUE;
395
396error:
398 dt_print(DT_DEBUG_OPENCL, "[opencl_highlights] couldn't enqueue kernel! %i\n", err);
399 return FALSE;
400}
401#endif
402
403void tiling_callback(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe,
404 const struct dt_dev_pixelpipe_iop_t *piece, struct dt_develop_tiling_t *tiling)
405{
406 const dt_iop_roi_t *const roi_in = &piece->roi_in;
408 const uint32_t filters = piece->dsc_in.filters;
409
410 if((d->mode == DT_IOP_HIGHLIGHTS_LAPLACIAN || d->mode == DT_IOP_HIGHLIGHTS_HARMONIC) && filters)
411 {
412 // Mosaic CFA and guided laplacian method: prepare for wavelets decomposition.
413 // Harmonic transposition reuses the same conservative budget (it disables tiling in
414 // commit_params and allocates region buffers well under this envelope).
415 const float scale = DS_FACTOR * dt_dev_get_module_scale(pipe, roi_in);
416 const float final_radius = (float)((int)(1 << d->scales)) / scale;
417 const int scales = CLAMP((int)ceilf(log2f(final_radius)), 1, MAX_NUM_SCALES);
418 const int max_filter_radius = (1 << scales);
419
420 // Warning: in and out are single-channel in RAW mode.
421 // in + out + interpolated + ds_interpolated + ds_tmp + 2 * ds_LF + ds_HF + mask + ds_mask
422 tiling->factor = 2.f + 2.f * 4 + 6.f * 4 / DS_FACTOR;
423 // OpenCL adds a downsampled scratch accumulator to keep the guided-laplacian read/write images distinct.
424 // in + out + interpolated + temp + mask + ds_interpolated + reconstructed_scratch + 2 * ds_LF + ds_HF + ds_mask
425 tiling->factor_cl = 2.f + 3.f * 4 + 6.f * 4 / DS_FACTOR;
426
427 // The wavelets decomposition uses a temp buffer of size 4 x ds_width
428 tiling->maxbuf = 1.f / roi_in->height * 4.f / DS_FACTOR;
429
430 // No temp buffer on GPU
431 tiling->maxbuf_cl = 1.0f;
432 tiling->overhead = 0;
433
434 // Note : if we were not doing anything iterative,
435 // max_filter_radius would not need to be factored more.
436 // Since we are iterating within tiles, we need more padding.
437 // The clean way of doing it would be an internal tiling mechanism
438 // where we restitch the tiles between each new iteration.
439 tiling->overlap = max_filter_radius * 1.5f / DS_FACTOR;
440 tiling->xalign = (filters == 9u) ? 6 : 2;
441 tiling->yalign = (filters == 9u) ? 6 : 2;
442
443 return;
444 }
445
446 tiling->factor = 2.0f; // in + out
447 tiling->maxbuf = 1.0f;
448 tiling->overhead = 0;
449
450 if(filters == 9u)
451 {
452 // xtrans
453 tiling->xalign = 6;
454 tiling->yalign = 6;
455 tiling->overlap = (d->mode == DT_IOP_HIGHLIGHTS_LCH) ? 2 : 0;
456 }
457 else if(filters)
458 {
459 // bayer
460 tiling->xalign = 2;
461 tiling->yalign = 2;
462 tiling->overlap = (d->mode == DT_IOP_HIGHLIGHTS_LCH) ? 1 : 0;
463 }
464 else
465 {
466 // non-raw
467 tiling->xalign = 1;
468 tiling->yalign = 1;
469 tiling->overlap = 0;
470 }
471}
472
473#undef SQRT3
474#undef SQRT12
475
476#ifdef HAVE_OPENCL
477#endif
478
480int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece,
481 const void *const ivoid, void *const ovoid)
482{
483 const dt_iop_roi_t *const roi_in = &piece->roi_in;
484 const dt_iop_roi_t *const roi_out = &piece->roi_out;
485 // process_visualize and interpolate_color (INPAINT mode) below read FC(row, col, filters)
486 // with tile-local row/col, so filters must be pre-shifted for roi_in's crop position here.
487 // The laplacian dispatch below passes roi_in/roi_out, not this filters value, and derives its
488 // own (correctly shifted) copy internally -- shifting this local doesn't affect it. The
489 // filters==9u / !filters checks are shift-invariant.
490 const uint32_t filters = dt_dev_get_roi_filters(piece, roi_in);
493
494 /* This transient preview belongs to the central darkroom view. Do not infer
495 * its owner from ROI geometry: at zoom-to-fit the main and navigation pipes
496 * can produce identical dimensions while both still need distinct outputs. */
497 const gboolean visualizing
498 = !IS_NULL_PTR(g) && g->show_visualize && self->dev->gui_attached && pipe == self->dev->pipe;
499
500 if(visualizing)
501 {
502 process_visualize(piece, ivoid, ovoid, roi_in, roi_out, filters, data);
503 /* The clipping preview is the final output of this module. Blending would
504 * interpret PASSTHRU as a channel-display request and replace RAW output
505 * with zeroes before the downstream demosaic stage can display it. */
507 ((dt_dev_pixelpipe_t *)pipe)->bypass_blendif = 1;
508 return 0;
509 }
510
511 const float clip = data->clip
512 * fminf(piece->dsc_in.processed_maximum[0],
513 fminf(piece->dsc_in.processed_maximum[1], piece->dsc_in.processed_maximum[2]));
514
515 if(!filters)
516 {
517 process_clip(piece, ivoid, ovoid, roi_in, roi_out, clip);
518 return 0;
519 }
520
521 switch(data->mode)
522 {
523 case DT_IOP_HIGHLIGHTS_INPAINT: // a1ex's (magiclantern) idea of color inpainting:
524 {
525 const float clips[4] = { 0.987 * data->clip * piece->dsc_in.processed_maximum[0],
526 0.987 * data->clip * piece->dsc_in.processed_maximum[1],
527 0.987 * data->clip * piece->dsc_in.processed_maximum[2], clip };
528
529 if(filters == 9u)
530 {
531 const uint8_t(*const xtrans)[6] = (const uint8_t(*const)[6])piece->dsc_in.xtrans;
533 for(int j = 0; j < roi_out->height; j++)
534 {
535 interpolate_color_xtrans(ivoid, ovoid, roi_in, roi_out, 0, 1, j, clips, xtrans, 0);
536 interpolate_color_xtrans(ivoid, ovoid, roi_in, roi_out, 0, -1, j, clips, xtrans, 1);
537 }
538
540 for(int i = 0; i < roi_out->width; i++)
541 {
542 interpolate_color_xtrans(ivoid, ovoid, roi_in, roi_out, 1, 1, i, clips, xtrans, 2);
543 interpolate_color_xtrans(ivoid, ovoid, roi_in, roi_out, 1, -1, i, clips, xtrans, 3);
544 }
545 }
546 else
547 {
549 for(int j = 0; j < roi_out->height; j++)
550 {
551 interpolate_color(ivoid, ovoid, roi_out, 0, 1, j, clips, filters, 0);
552 interpolate_color(ivoid, ovoid, roi_out, 0, -1, j, clips, filters, 1);
553 }
554
555
556 // up/down directions
558 for(int i = 0; i < roi_out->width; i++)
559 {
560 interpolate_color(ivoid, ovoid, roi_out, 1, 1, i, clips, filters, 2);
561 interpolate_color(ivoid, ovoid, roi_out, 1, -1, i, clips, filters, 3);
562 }
563 }
564 break;
565 }
567 if(filters == 9u)
568 process_lch_xtrans(self, piece, ivoid, ovoid, roi_in, roi_out, clip);
569 else
570 process_lch_bayer(self, piece, ivoid, ovoid, roi_in, roi_out, clip);
571 break;
573 {
574 const dt_aligned_pixel_t clips = { 0.995f * data->clip * piece->dsc_in.processed_maximum[0],
575 0.995f * data->clip * piece->dsc_in.processed_maximum[1],
576 0.995f * data->clip * piece->dsc_in.processed_maximum[2], clip };
577 if((filters == 9u && process_laplacian_xtrans(self, pipe, piece, ivoid, ovoid, roi_in, roi_out, clips))
578 || (filters != 9u && process_laplacian_bayer(self, pipe, piece, ivoid, ovoid, roi_in, roi_out, clips)))
579 return 1;
580 break;
581 }
583 {
584 const dt_aligned_pixel_t clips = { 0.995f * data->clip * piece->dsc_in.processed_maximum[0],
585 0.995f * data->clip * piece->dsc_in.processed_maximum[1],
586 0.995f * data->clip * piece->dsc_in.processed_maximum[2], clip };
587 if((filters == 9u && process_harmonic_xtrans(self, pipe, piece, ivoid, ovoid, roi_in, roi_out, clips))
588 || (filters != 9u && process_harmonic_bayer(self, pipe, piece, ivoid, ovoid, roi_in, roi_out, clips)))
589 return 1;
590 break;
591 }
592 default:
594 process_clip(piece, ivoid, ovoid, roi_in, roi_out, clip);
595 break;
596 }
597
599 dt_iop_alpha_copy(ivoid, ovoid, roi_out->width, roi_out->height);
600 return 0;
601}
602
605{
608
609 memcpy(d, p, sizeof(*p));
610
611 // Image-type gating (raw colorimetry, not monochrome) is handled at history level by
612 // enable()/force_enable()/reload_defaults(); nothing type-related is decided here. process()
613 // still has a dedicated !filters branch so already-demosaiced raw (sRAW / linear DNG) is
614 // processed correctly when the module is enabled.
615 const dt_image_t *const img = &self->dev->image_storage;
616 dt_iop_fmt_log(self, "commit: class=%s filters=%u mode=%d -> enabled=%d",
618 piece->enabled);
619
620 // no OpenCL for DT_IOP_HIGHLIGHTS_INPAINT. HARMONIC runs the hybrid CPU-orchestrated
621 // driver (host roundtrip inside the module keeps the pipe's CL chain intact).
622 piece->process_cl_ready = (d->mode == DT_IOP_HIGHLIGHTS_INPAINT) ? 0 : 1;
623
625 piece->cache_output_on_ram = TRUE;
626
627 if(d->mode == DT_IOP_HIGHLIGHTS_HARMONIC)
628 {
629 // The segmented full-resolution reconstruction needs each clipped region intact: tiling
630 // would split a large blown highlight (e.g. the sun) across tile borders, and the per-region
631 // biharmonic core dome would then be solved on each half against a fake tile edge (-> a
632 // half-recovered, dark-cored disc). Process the whole frame instead.
633 piece->process_tiling_ready = 0;
634 }
635
637 {
638 if(!piece->dsc_in.filters)
639 {
640 const float m = fminf(piece->dsc_in.processed_maximum[0],
641 fminf(piece->dsc_in.processed_maximum[1], piece->dsc_in.processed_maximum[2]));
642 for(int k = 0; k < 3; k++) piece->dsc_out.processed_maximum[k] = m;
643 }
644 else
645 {
646 const float m = fmaxf(piece->dsc_in.processed_maximum[0],
647 fmaxf(piece->dsc_in.processed_maximum[1], piece->dsc_in.processed_maximum[2]));
648 for(int k = 0; k < 3; k++) piece->dsc_out.processed_maximum[k] = m;
649 }
650 }
651}
652
653static gboolean enable(dt_image_t *image)
654{
655 // raw colorimetry (raw or sraw/linear-DNG), but not real monochrome. Must match the
656 // commit_params() gate above.
657 return dt_image_needs_rawprepare(image) && !dt_image_is_monochrome(image);
658}
659
660gboolean force_enable(struct dt_iop_module_t *self, const gboolean current_state)
661{
662 // History sanitization: clamp against the SAME support rule as enable()/reload_defaults()
663 // (raw colorimetry, not monochrome). The previous version only handled the monochrome case and
664 // let a highlights entry pasted onto a non-raw image survive until commit_params() patched it.
665 const gboolean active = enable(&self->dev->image_storage);
666 const gboolean state = current_state && active;
667 dt_iop_fmt_log(self, "force_enable: class=%s supported=%d current=%d -> %d",
668 dt_image_pipe_class_name(dt_image_pipe_class(&self->dev->image_storage)), active, current_state,
669 state);
670 return state;
671}
672
674{
675 const int program = 2; // basic.cl, from programs.conf
678 module->data = gd;
679 gd->kernel_highlights_1f_clip = dt_opencl_create_kernel(program, "highlights_1f_clip");
680 const int harmonic_program = 38; // highlights_harmonic.cl (harmonic transposition, fp32)
681 const int sparse_program = 37; // highlights_sparse.cl (fp64 sparse solvers)
682 gd->kernel_sparse_chol_update_level = dt_opencl_create_kernel(sparse_program, "sparse_chol_update_level");
683 gd->kernel_sparse_chol_final_level = dt_opencl_create_kernel(sparse_program, "sparse_chol_final_level");
684 gd->kernel_sparse_chol_fwd_level = dt_opencl_create_kernel(sparse_program, "sparse_chol_fwd_level");
685 gd->kernel_sparse_chol_bwd_level = dt_opencl_create_kernel(sparse_program, "sparse_chol_bwd_level");
686 gd->kernel_hl_pde_rhs = dt_opencl_create_kernel(sparse_program, "hl_pde_rhs");
687 gd->kernel_hl_pde_scatter = dt_opencl_create_kernel(sparse_program, "hl_pde_scatter");
688 gd->kernel_hl_aniso_rhs = dt_opencl_create_kernel(sparse_program, "hl_aniso_rhs");
689 gd->kernel_hl_aniso_scatter = dt_opencl_create_kernel(sparse_program, "hl_aniso_scatter");
690 gd->kernel_hl_cg_r1 = dt_opencl_create_kernel(sparse_program, "hl_cg_r1");
691 gd->kernel_hl_cg_ap = dt_opencl_create_kernel(sparse_program, "hl_cg_ap");
692 gd->kernel_hl_cg_update = dt_opencl_create_kernel(sparse_program, "hl_cg_update");
693 gd->kernel_hl_cfa_steer = dt_opencl_create_kernel(harmonic_program, "hl_cfa_steer");
694 gd->kernel_hl_cfa_down = dt_opencl_create_kernel(harmonic_program, "hl_cfa_down");
695 gd->kernel_hl_cfa_box = dt_opencl_create_kernel(harmonic_program, "hl_cfa_box");
696 gd->kernel_hl_cfa_grad = dt_opencl_create_kernel(harmonic_program, "hl_cfa_grad");
697 gd->kernel_hl_cfa_tensor = dt_opencl_create_kernel(harmonic_program, "hl_cfa_tensor");
698 gd->kernel_hl_cfa_gnorm = dt_opencl_create_kernel(harmonic_program, "hl_cfa_gnorm");
699 gd->kernel_hl_cfa_weights = dt_opencl_create_kernel(harmonic_program, "hl_cfa_weights");
700 gd->kernel_hl_cfa_jacobi = dt_opencl_create_kernel(harmonic_program, "hl_cfa_jacobi");
701 gd->kernel_hl_cfa_jacobi_block = dt_opencl_create_kernel(harmonic_program, "hl_cfa_jacobi_block");
702 gd->kernel_hl_fill_down = dt_opencl_create_kernel(harmonic_program, "hl_fill_down");
703 gd->kernel_hl_fill_seed = dt_opencl_create_kernel(harmonic_program, "hl_fill_seed");
704 gd->kernel_hl_fill_seed_up = dt_opencl_create_kernel(harmonic_program, "hl_fill_seed_up");
705 gd->kernel_hl_fill_jacobi = dt_opencl_create_kernel(harmonic_program, "hl_fill_jacobi");
706 gd->kernel_hl_fill_jacobi_block = dt_opencl_create_kernel(harmonic_program, "hl_fill_jacobi_block");
707 gd->kernel_hl_fill_up = dt_opencl_create_kernel(harmonic_program, "hl_fill_up");
708 gd->kernel_hl_cf_lref_partials = dt_opencl_create_kernel(harmonic_program, "hl_cf_lref_partials");
709 gd->kernel_hl_cf_pack_joint = dt_opencl_create_kernel(harmonic_program, "hl_cf_pack_joint");
710 gd->kernel_hl_cf_fit_joint = dt_opencl_create_kernel(harmonic_program, "hl_cf_fit_joint");
711 gd->kernel_hl_cf_eval_joint = dt_opencl_create_kernel(harmonic_program, "hl_cf_eval_joint");
712 gd->kernel_hl_cf_pack_pair = dt_opencl_create_kernel(harmonic_program, "hl_cf_pack_pair");
713 gd->kernel_hl_cf_fit_pair = dt_opencl_create_kernel(harmonic_program, "hl_cf_fit_pair");
714 gd->kernel_hl_cf_eval_pair = dt_opencl_create_kernel(harmonic_program, "hl_cf_eval_pair");
715 gd->kernel_hl_cf_pack_deepmask = dt_opencl_create_kernel(harmonic_program, "hl_cf_pack_deepmask");
716 gd->kernel_hl_cf_eval_deep = dt_opencl_create_kernel(harmonic_program, "hl_cf_eval_deep");
717 gd->kernel_hl_buf_to_img = dt_opencl_create_kernel(harmonic_program, "hl_buf_to_img");
718 gd->kernel_hl_hf_pack = dt_opencl_create_kernel(harmonic_program, "hl_hf_pack");
719 gd->kernel_hl_hf_fit = dt_opencl_create_kernel(harmonic_program, "hl_hf_fit");
720 gd->kernel_hl_hf_energy = dt_opencl_create_kernel(harmonic_program, "hl_hf_energy");
721 gd->kernel_hl_hf_eval = dt_opencl_create_kernel(harmonic_program, "hl_hf_eval");
722 gd->kernel_hl_hf_damp = dt_opencl_create_kernel(harmonic_program, "hl_hf_damp");
723 gd->kernel_hl_soft_floor = dt_opencl_create_kernel(harmonic_program, "hl_soft_floor");
724 gd->kernel_hl_hard_floor = dt_opencl_create_kernel(harmonic_program, "hl_hard_floor");
725 gd->kernel_hl_lsb_hole = dt_opencl_create_kernel(harmonic_program, "hl_lsb_hole");
726 gd->kernel_hl_ratio_plane = dt_opencl_create_kernel(harmonic_program, "hl_ratio_plane");
727 gd->kernel_hl_dome_down = dt_opencl_create_kernel(harmonic_program, "hl_dome_down");
728 gd->kernel_hl_dome_blend = dt_opencl_create_kernel(harmonic_program, "hl_dome_blend");
729 gd->kernel_hl_core_floor = dt_opencl_create_kernel(harmonic_program, "hl_core_floor");
730 gd->kernel_hl_cmean_reduce = dt_opencl_create_kernel(harmonic_program, "hl_cmean_reduce");
731 gd->kernel_hl_pde_init = dt_opencl_create_kernel(harmonic_program, "hl_pde_init");
732 gd->kernel_hl_mask_to_img1 = dt_opencl_create_kernel(harmonic_program, "hl_mask_to_img1");
733 gd->kernel_hl_core_blend = dt_opencl_create_kernel(harmonic_program, "hl_core_blend");
734 gd->kernel_hl_aniso_prep = dt_opencl_create_kernel(harmonic_program, "hl_aniso_prep");
735 gd->kernel_hl_box3 = dt_opencl_create_kernel(harmonic_program, "hl_box3");
736 gd->kernel_hl_grad_reduce = dt_opencl_create_kernel(harmonic_program, "hl_grad_reduce");
737 gd->kernel_hl_aniso_tensor = dt_opencl_create_kernel(harmonic_program, "hl_aniso_tensor");
738 gd->kernel_hl_aniso_weights = dt_opencl_create_kernel(harmonic_program, "hl_aniso_weights");
739 gd->kernel_hl_aniso_reassemble = dt_opencl_create_kernel(harmonic_program, "hl_aniso_reassemble");
740 gd->kernel_hl_knee_bin = dt_opencl_create_kernel(harmonic_program, "hl_knee_bin");
741 gd->kernel_hl_knee_jmom = dt_opencl_create_kernel(harmonic_program, "hl_knee_jmom");
742 gd->kernel_hl_knee_pmom = dt_opencl_create_kernel(harmonic_program, "hl_knee_pmom");
743 gd->kernel_hl_knee_joint_reg = dt_opencl_create_kernel(harmonic_program, "hl_knee_joint_reg");
744 gd->kernel_hl_knee_pair_reg = dt_opencl_create_kernel(harmonic_program, "hl_knee_pair_reg");
745 gd->kernel_hl_knee_apply = dt_opencl_create_kernel(harmonic_program, "hl_knee_apply");
746 gd->kernel_hl_mask_pack = dt_opencl_create_kernel(harmonic_program, "hl_mask_pack");
747 gd->kernel_hl_region_gather = dt_opencl_create_kernel(harmonic_program, "hl_region_gather");
748 gd->kernel_hl_region_scatter = dt_opencl_create_kernel(harmonic_program, "hl_region_scatter");
749 gd->kernel_hl_region_stats = dt_opencl_create_kernel(harmonic_program, "hl_region_stats");
750 gd->kernel_hl_need_self = dt_opencl_create_kernel(harmonic_program, "hl_need_self");
751 gd->kernel_hl_knee_apply_interp = dt_opencl_create_kernel(harmonic_program, "hl_knee_apply_interp");
752 gd->kernel_hl_cg_embed = dt_opencl_create_kernel(harmonic_program, "hl_cg_embed");
753 gd->kernel_hl_cg_op = dt_opencl_create_kernel(harmonic_program, "hl_cg_op");
754 gd->kernel_hl_cg_r0 = dt_opencl_create_kernel(harmonic_program, "hl_cg_r0");
755 gd->kernel_hl_cg_beta = dt_opencl_create_kernel(harmonic_program, "hl_cg_beta");
756 gd->kernel_hl_relu = dt_opencl_create_kernel(harmonic_program, "hl_relu");
757 gd->kernel_hl_aniso_pyr_down = dt_opencl_create_kernel(harmonic_program, "hl_aniso_pyr_down");
758 gd->kernel_hl_pyr_getc = dt_opencl_create_kernel(harmonic_program, "hl_pyr_getc");
759 gd->kernel_hl_pyr_getc4 = dt_opencl_create_kernel(harmonic_program, "hl_pyr_getc4");
760 gd->kernel_hl_pyr_putc4 = dt_opencl_create_kernel(harmonic_program, "hl_pyr_putc4");
761 gd->kernel_hl_pyr_project = dt_opencl_create_kernel(harmonic_program, "hl_pyr_project");
762 gd->kernel_hl_aniso_obs_full = dt_opencl_create_kernel(harmonic_program, "hl_aniso_obs_full");
763 gd->kernel_hl_aniso_obs_flags = dt_opencl_create_kernel(harmonic_program, "hl_aniso_obs_flags");
764 gd->kernel_hl_window_pack = dt_opencl_create_kernel(harmonic_program, "hl_window_pack");
765 gd->kernel_hl_window_unpack = dt_opencl_create_kernel(harmonic_program, "hl_window_unpack");
766 gd->kernel_hl_pyr_putc = dt_opencl_create_kernel(harmonic_program, "hl_pyr_putc");
767 gd->kernel_hl_aniso_iter = dt_opencl_create_kernel(harmonic_program, "hl_aniso_iter");
768 gd->kernel_hl_aniso_iter_block = dt_opencl_create_kernel(harmonic_program, "hl_aniso_iter_block");
769 gd->kernel_hl_aniso_splat = dt_opencl_create_kernel(harmonic_program, "hl_aniso_splat");
770 gd->kernel_highlights_1f_lch_bayer = dt_opencl_create_kernel(program, "highlights_1f_lch_bayer");
771 gd->kernel_highlights_1f_lch_xtrans = dt_opencl_create_kernel(program, "highlights_1f_lch_xtrans");
772 gd->kernel_highlights_4f_clip = dt_opencl_create_kernel(program, "highlights_4f_clip");
773 gd->kernel_highlights_bilinear_and_mask = dt_opencl_create_kernel(program, "interpolate_and_mask");
774 gd->kernel_highlights_bilinear_and_mask_xtrans = dt_opencl_create_kernel(program, "interpolate_and_mask_xtrans");
776 = dt_opencl_create_kernel(program, "highlights_normalize_reduce_first");
778 = dt_opencl_create_kernel(program, "highlights_normalize_reduce_first_xtrans");
780 = dt_opencl_create_kernel(program, "highlights_normalize_reduce_second");
781 gd->kernel_highlights_remosaic_and_replace = dt_opencl_create_kernel(program, "remosaic_and_replace");
783 = dt_opencl_create_kernel(program, "remosaic_and_replace_xtrans");
784 gd->kernel_highlights_box_blur = dt_opencl_create_kernel(program, "box_blur_5x5");
785 gd->kernel_highlights_guide_laplacians = dt_opencl_create_kernel(program, "guide_laplacians");
786 gd->kernel_highlights_diffuse_color = dt_opencl_create_kernel(program, "diffuse_color");
787 gd->kernel_highlights_false_color = dt_opencl_create_kernel(program, "highlights_false_color");
788 gd->kernel_interpolate_bilinear = dt_opencl_create_kernel(program, "interpolate_bilinear");
789
790 const int wavelets = 35; // bspline.cl, from programs.conf
791 gd->kernel_filmic_bspline_horizontal = dt_opencl_create_kernel(wavelets, "blur_2D_Bspline_horizontal");
792 gd->kernel_filmic_bspline_vertical = dt_opencl_create_kernel(wavelets, "blur_2D_Bspline_vertical");
794 = dt_opencl_create_kernel(wavelets, "blur_2D_Bspline_horizontal_local");
795 gd->kernel_filmic_bspline_vertical_local = dt_opencl_create_kernel(wavelets, "blur_2D_Bspline_vertical_local");
796}
797
799{
904
909
911
912 dt_free(module->data);
913}
914
916{
918 piece->data_size = sizeof(dt_iop_highlights_data_t);
919}
920
922{
923 dt_free_align(piece->data);
924 piece->data = NULL;
925}
926
927void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
928{
931
932 const gboolean raw = (self->dev->image_storage.dsc.filters != 0);
933 const gboolean israw = (self->dev->image_storage.dsc.filters != 0);
934 dt_iop_highlights_mode_t mode = p->mode;
935
936 // harmonic transposition reads noise_level (regrain) and solid_color (all-clip core reaction);
937 // iterations and scales belong to the a-trous guided laplacians only
938 gtk_widget_set_visible(g->noise_level,
939 raw && (mode == DT_IOP_HIGHLIGHTS_LAPLACIAN || mode == DT_IOP_HIGHLIGHTS_HARMONIC));
940 gtk_widget_set_visible(g->iterations, raw && mode == DT_IOP_HIGHLIGHTS_LAPLACIAN);
941 gtk_widget_set_visible(g->scales, raw && mode == DT_IOP_HIGHLIGHTS_LAPLACIAN);
942 gtk_widget_set_visible(g->solid_color,
943 raw && (mode == DT_IOP_HIGHLIGHTS_LAPLACIAN || mode == DT_IOP_HIGHLIGHTS_HARMONIC));
944
946}
947
948void gui_update(struct dt_iop_module_t *self)
949{
951 const gboolean monochrome = dt_image_is_monochrome(&self->dev->image_storage);
952 // enable this per default if raw or sraw if not real monochrome
953 self->default_enabled = dt_image_needs_rawprepare(&self->dev->image_storage) && !monochrome;
954
955 // Neuter the on/off button only if not already enabled.
956 // It can be enabled by history copy & paste from a RAW image.
957 self->hide_enable_button = monochrome && !self->enabled;
958 gtk_stack_set_visible_child_name(GTK_STACK(self->widget), self->default_enabled ? "default" : "monochrome");
959
960 // capability entries, added once (moved here from reload_defaults so it never touches widgets off
961 // the GUI thread / on a widget-less export dev)
964 GINT_TO_POINTER(DT_IOP_HIGHLIGHTS_LAPLACIAN), NULL, TRUE);
966 dt_bauhaus_combobox_add_full(g->mode, _("harmonic transposition"), DT_BAUHAUS_COMBOBOX_ALIGN_RIGHT,
967 GINT_TO_POINTER(DT_IOP_HIGHLIGHTS_HARMONIC), NULL, TRUE);
968
970 g->show_visualize = FALSE;
971 gui_changed(self, NULL, NULL);
972}
973
975{
976 // we might be called from presets update infrastructure => there is no image
977 if(!module->dev || module->dev->image_storage.id == -1) return;
978
979 // enable this per default if raw or sraw if not real monochrome
980 module->default_enabled = enable(&module->dev->image_storage);
981 module->hide_enable_button = !enable(&module->dev->image_storage);
982 dt_iop_fmt_log(module, "reload_defaults: class=%s default_enabled=%d",
984 module->default_enabled);
985 // Stack visibility and the "guided laplacians" capability entry are set from default_enabled in
986 // gui_update() (which already does the stack), so reload_defaults() stays params-only.
987}
988
989static void _visualize_callback(GtkWidget *quad, gpointer user_data)
990{
991 if(dt_gui_widgets_suppressed()) return;
992 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
994
995 // if blend module is displaying mask do not display it here
998
999 g->show_visualize = dt_bauhaus_widget_get_quad_active(quad);
1000
1001 if(g->show_visualize) self->request_mask_display = DT_DEV_PIXELPIPE_DISPLAY_PASSTHRU;
1002
1003 dt_iop_set_cache_bypass(self, g->show_visualize);
1005}
1006
1007void gui_focus(struct dt_iop_module_t *self, gboolean in)
1008{
1010 if(!in)
1011 {
1012 const gboolean was_visualize = g->show_visualize;
1014 g->show_visualize = FALSE;
1015 if(was_visualize) dt_dev_pixelpipe_update_history_main(self->dev);
1016 }
1017}
1018
1019void gui_init(struct dt_iop_module_t *self)
1020{
1022 GtkWidget *box_raw = self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1023
1024 g->mode = dt_bauhaus_combobox_from_params(self, "mode");
1025 gtk_widget_set_tooltip_text(g->mode, _("highlight reconstruction method"));
1026
1027 g->clip = dt_bauhaus_slider_from_params(self, "clip");
1029 gtk_widget_set_tooltip_text(g->clip, _("manually adjust the clipping threshold against "
1030 "magenta highlights\nthe mask icon shows the clipped area\n"
1031 "(you shouldn't ever need to touch this)"));
1035 g_signal_connect(G_OBJECT(g->clip), "quad-pressed", G_CALLBACK(_visualize_callback), self);
1036
1037 g->noise_level = dt_bauhaus_slider_from_params(self, "noise_level");
1038 gtk_widget_set_tooltip_text(g->noise_level, _("add noise to visually blend the reconstructed areas\n"
1039 "into the rest of the noisy image. useful at high ISO."));
1040
1041 g->iterations = dt_bauhaus_slider_from_params(self, "iterations");
1042 dt_bauhaus_slider_set_soft_range(g->iterations, 1, 256);
1043 gtk_widget_set_tooltip_text(g->iterations, _("increase if magenta highlights don't get fully corrected\n"
1044 "each new iteration brings a performance penalty."));
1045
1046 g->solid_color = dt_bauhaus_slider_from_params(self, "solid_color");
1047 dt_bauhaus_slider_set_format(g->solid_color, "%");
1048 gtk_widget_set_tooltip_text(g->solid_color,
1049 _("increase if magenta highlights don't get fully corrected.\n"
1050 "this may produce non-smooth boundaries between valid and clipped regions."));
1051
1052 g->scales = dt_bauhaus_combobox_from_params(self, "scales");
1053 gtk_widget_set_tooltip_text(g->scales, _("increase to correct larger clipped areas.\n"
1054 "large values bring huge performance penalties"));
1055
1056 GtkWidget *monochromes = dt_ui_label_new(_("not applicable"));
1057 gtk_widget_set_tooltip_text(monochromes, _("no highlights reconstruction for monochrome images"));
1058
1059 // start building top level widget
1060 self->widget = gtk_stack_new();
1061 gtk_stack_set_homogeneous(GTK_STACK(self->widget), FALSE);
1062 gtk_stack_add_named(GTK_STACK(self->widget), monochromes, "monochrome");
1063 gtk_stack_add_named(GTK_STACK(self->widget), box_raw, "default");
1064}
1065
1066// clang-format off
1067// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1068// vim: shiftwidth=2 expandtab tabstop=2 cindent
1069// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1070// 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
#define MAX_NUM_SCALES
Definition atrous.c:90
#define m
Definition basecurve.c:278
void dt_bauhaus_slider_set_soft_range(GtkWidget *widget, float soft_min, float soft_max)
Definition bauhaus.c:1650
void dt_bauhaus_slider_set_digits(GtkWidget *widget, int val)
Definition bauhaus.c:3549
void dt_bauhaus_widget_set_quad_toggle(GtkWidget *widget, int toggle)
Definition bauhaus.c:1723
int dt_bauhaus_combobox_length(GtkWidget *widget)
Definition bauhaus.c:2158
void dt_bauhaus_widget_set_quad_active(GtkWidget *widget, int active)
Definition bauhaus.c:1729
void dt_bauhaus_combobox_add_full(GtkWidget *widget, const char *text, dt_bauhaus_combobox_alignment_t align, gpointer data, void(free_func)(void *data), gboolean sensitive)
Definition bauhaus.c:2041
int dt_bauhaus_widget_get_quad_active(GtkWidget *widget)
Definition bauhaus.c:1746
void dt_bauhaus_widget_set_quad_visibility(GtkWidget *widget, const gboolean visible)
Definition bauhaus.c:1739
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
Definition bauhaus.c:3613
void dt_bauhaus_widget_set_quad_paint(GtkWidget *widget, dt_bauhaus_quad_paint_f f, int paint_flags, void *paint_data)
Definition bauhaus.c:1705
@ DT_BAUHAUS_COMBOBOX_ALIGN_RIGHT
Definition bauhaus.h:125
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
__DT_CLONE_TARGETS__ void process_visualize(const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out, const uint32_t filters, dt_iop_highlights_data_t *data)
Definition clip.c:87
__DT_CLONE_TARGETS__ void process_clip(const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out, const float clip)
Definition clip.c:61
@ IOP_CS_RAW
@ IOP_CS_RGB
const float max
dt_image_pipe_class_t dt_image_pipe_class(const dt_image_t *img)
const char * dt_image_pipe_class_name(const dt_image_pipe_class_t klass)
gboolean dt_image_is_monochrome(const dt_image_t *img)
gboolean dt_image_needs_rawprepare(const dt_image_t *img)
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1600
#define dt_free_align(ptr)
Definition darktable.h:503
static void * dt_calloc_align(size_t size)
Definition darktable.h:510
@ DT_DEBUG_OPENCL
Definition darktable.h:744
#define dt_free(ptr)
Definition darktable.h:478
void void void gboolean dt_gui_widgets_suppressed(void)
Definition gtk.c:194
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
Definition darktable.h:151
#define __DT_CLONE_TARGETS__
Definition darktable.h:379
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:270
#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:293
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)
void dt_iop_params_t
Definition dev_history.h:42
#define dt_dev_pixelpipe_update_history_main(dev)
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
Definition develop.h:118
@ DT_DEV_PIXELPIPE_DISPLAY_PASSTHRU
Definition develop.h:136
@ DT_DEV_PIXELPIPE_DISPLAY_NONE
Definition develop.h:117
void dtgtk_cairo_paint_showmask(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
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)
Definition format.c:75
#define DT_GUI_BOX_SPACING
Definition gtk.h:109
static GtkWidget * dt_ui_label_new(const gchar *str)
Definition gtk.h:479
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)
Definition highlights.c:603
const char ** description(struct dt_iop_module_t *self)
Definition highlights.c:109
int default_group()
Definition highlights.c:116
__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 highlights.c:480
void reload_defaults(dt_iop_module_t *module)
Definition highlights.c:974
static gboolean enable(dt_image_t *image)
Definition highlights.c:653
void gui_focus(struct dt_iop_module_t *self, gboolean in)
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition highlights.c:915
const char * name()
Definition highlights.c:104
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 highlights.c:132
void gui_update(struct dt_iop_module_t *self)
Refresh GUI controls from current params and configuration.
Definition highlights.c:948
void gui_init(struct dt_iop_module_t *self)
void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
Definition highlights.c:927
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)
Definition highlights.c:403
void cleanup_global(dt_iop_module_so_t *module)
Definition highlights.c:798
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition highlights.c:126
int flags()
Definition highlights.c:121
void autoset(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, const struct dt_dev_pixelpipe_iop_t *piece, const void *input)
Definition highlights.c:138
gboolean force_enable(struct dt_iop_module_t *self, const gboolean current_state)
Definition highlights.c:660
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition highlights.c:921
static void _visualize_callback(GtkWidget *quad, gpointer user_data)
Definition highlights.c:989
void init_global(dt_iop_module_so_t *module)
Definition highlights.c:673
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)
Definition highlights.c:227
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 highlights.c:160
uint32_t dt_dev_get_roi_filters(const dt_dev_pixelpipe_iop_t *const piece, const dt_iop_roi_t *const roi_in)
Definition imageop.c:136
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:3220
float dt_dev_get_module_scale(const dt_dev_pixelpipe_t *const pipe, const dt_iop_roi_t *const roi_in)
Definition imageop.c:131
void dt_iop_set_cache_bypass(dt_iop_module_t *module, gboolean state)
Definition imageop.c:2989
#define dt_iop_fmt_log(module, fmt,...)
Debug helper to trace a module's input-format-driven decisions on the -d pipe channel (DT_DEBUG_PIPE)...
Definition imageop.h:490
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:197
@ IOP_FLAGS_ALLOW_TILING
Definition imageop.h:199
@ IOP_GROUP_REPAIR
Definition imageop.h:170
#define IOP_GUI_ALLOC(module)
Definition imageop.h:638
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
Definition imageop_gui.c:77
GtkWidget * dt_bauhaus_combobox_from_params(dt_iop_module_t *self, const char *param)
void *const ovoid
__DT_CLONE_TARGETS__ int process_laplacian_bayer(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const restrict ivoid, void *const restrict ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out, const dt_aligned_pixel_t clips)
Definition laplacian.c:426
cl_int process_laplacian_bayer_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, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out, const dt_aligned_pixel_t clips)
Definition laplacian.c:806
cl_int process_laplacian_xtrans_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, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out, const dt_aligned_pixel_t clips)
Definition laplacian.c:1073
__DT_CLONE_TARGETS__ int process_laplacian_xtrans(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const restrict ivoid, void *const restrict ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out, const dt_aligned_pixel_t clips)
Definition laplacian.c:527
__DT_CLONE_TARGETS__ void process_lch_bayer(dt_iop_module_t *self, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out, const float clip)
Definition lch.c:313
void interpolate_color(const void *const ivoid, void *const ovoid, const dt_iop_roi_t *const roi_out, int dim, int dir, int other, const float *clip, const uint32_t filters, const int pass)
Definition lch.c:203
__DT_CLONE_TARGETS__ void process_lch_xtrans(dt_iop_module_t *self, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out, const float clip)
Definition lch.c:410
void interpolate_color_xtrans(const void *const ivoid, void *const ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out, int dim, int dir, int other, const float *const clip, const uint8_t(*const xtrans)[6], const int pass)
Definition lch.c:87
float *const restrict const size_t k
float dt_aligned_pixel_t[4]
int dt_opencl_local_buffer_opt(const int devid, const int kernel, dt_opencl_local_buffer_t *factors)
Definition opencl.c:3286
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
Definition opencl.c:2164
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2058
void * dt_opencl_copy_host_to_device_constant(const int devid, const size_t size, void *host)
Definition opencl.c:2360
void dt_opencl_free_kernel(const int kernel)
Definition opencl.c:2101
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:2155
int dt_opencl_enqueue_kernel_2d_with_local(const int dev, const int kernel, const size_t *sizes, const size_t *local)
Definition opencl.c:2170
void dt_opencl_release_mem_object(cl_mem mem)
Definition opencl.c:2415
#define DT_OPENCL_DEFAULT_ERROR
Definition opencl.h:57
#define ROUNDUP(a, n)
Definition opencl.h:78
#define ROUNDUPDHT(a, b)
Definition opencl.h:82
#define ROUNDUPDWD(a, b)
Definition opencl.h:81
cl_int process_harmonic_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, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out, const dt_aligned_pixel_t clips)
Definition process.c:698
__DT_CLONE_TARGETS__ int process_harmonic_bayer(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const restrict ivoid, void *const restrict ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out, const dt_aligned_pixel_t clips)
Definition process.c:39
__DT_CLONE_TARGETS__ int process_harmonic_xtrans(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const restrict ivoid, void *const restrict ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out, const dt_aligned_pixel_t clips)
Definition process.c:201
struct _GtkWidget GtkWidget
Definition splash.h:29
const float uint32_t state[4]
@ DT_IOP_HIGHLIGHTS_HARMONIC
@ DT_IOP_HIGHLIGHTS_INPAINT
@ DT_IOP_HIGHLIGHTS_LAPLACIAN
dt_iop_highlights_params_t dt_iop_highlights_data_t
#define DS_FACTOR
dt_iop_buffer_dsc_t dsc_out
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
int32_t gui_attached
Definition develop.h:162
dt_image_t image_storage
Definition develop.h:259
struct dt_dev_pixelpipe_t * pipe
Definition develop.h:247
dt_iop_buffer_dsc_t dsc
Definition image.h:337
int32_t id
Definition image.h:319
uint32_t filters
Definition format.h:60
uint8_t xtrans[6][6]
Definition format.h:70
dt_aligned_pixel_t processed_maximum
Definition format.h:85
dt_iop_global_data_t * data
Definition imageop.h:263
int32_t hide_enable_button
Definition imageop.h:292
GtkWidget * widget
Definition imageop.h:374
struct dt_develop_t * dev
Definition imageop.h:333
dt_iop_gui_data_t * gui_data
Definition imageop.h:348
gboolean default_enabled
Definition imageop.h:340
dt_iop_global_data_t * global_data
Definition imageop.h:351
gboolean enabled
Definition imageop.h:335
int request_mask_display
Definition imageop.h:298
dt_iop_params_t * params
Definition imageop.h:344
Region of interest passed through the pixelpipe.
Definition imageop.h:72
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29