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/imagebuf.h" // dt_iop_image_copy_by_size(): the early-bypass passthrough
54#include "common/logging.h"
55#include "system/macros.h"
56#include "system/mem_alloc.h"
58#include "system/openmp.h"
59#include "system/simd.h"
61#include "config.h"
62#endif
63#include "widgets/bauhaus.h"
64#include "common/opencl.h"
66#include "develop/develop.h"
67#include "develop/imageop.h"
68#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 "iop/iop_api.h"
77// Highlights internals: harmonic mode is a set of compiled per-stage TUs; include only the
78// shared types (params + per-module OpenCL global data) and the high-level harmonic driver.
79#include "iop/highlights/clip.h"
83#include "iop/highlights/lch.h"
85
86#include <gtk/gtk.h>
87#include <inttypes.h>
88#include "widgets/label.h"
89
90// Downsampling factor for guided-laplacian
91
93
104
105const char *name()
106{
107 return _("_highlight reconstruction");
108}
109
110const char **description(struct dt_iop_module_t *self)
111{
112 return dt_iop_set_description(self, _("avoid magenta highlights and try to recover highlights colors"),
113 _("corrective"), _("linear, raw, scene-referred"), _("reconstruction, raw"),
114 _("linear, raw, scene-referred"));
115}
116
118{
119 return IOP_GROUP_REPAIR;
120}
121
126
128{
129 if(piece && piece->dsc_in.cst != IOP_CS_RAW) return IOP_CS_RGB;
130 return IOP_CS_RAW;
131}
132
135{
136 default_output_format(self, pipe, piece, dsc);
137}
138
139void autoset(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe,
140 const struct dt_dev_pixelpipe_iop_t *piece, const void *input)
141{
143 const dt_iop_roi_t *const roi_out = &piece->roi_out;
144 const float *const restrict in = (const float *)input;
145 float max_RGB[3] = { 0.0f };
146
147 __OMP_PARALLEL_FOR__(reduction(max : max_RGB[:3]) collapse(2))
148 for(size_t i = 0; i < roi_out->height; i++)
149 for(size_t j = 0; j < roi_out->width; j++)
150 {
151 const size_t channel = (piece->dsc_in.filters == 9u)
152 ? FCxtrans(i, j, roi_out, piece->dsc_in.xtrans)
153 : FC(i + roi_out->y, j + roi_out->x, piece->dsc_in.filters);
154 const float pixel_max = in[i * roi_out->width + j] / piece->dsc_in.processed_maximum[channel];
155 max_RGB[channel] = MAX(max_RGB[channel], pixel_max);
156 }
157
158 p->clip = MIN(MIN(max_RGB[0], max_RGB[1]), max_RGB[2]);
159}
160
161int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params,
162 const int new_version)
163{
164 if(old_version == 1 && new_version == 4)
165 {
166 /*
167 params of v2 :
168 float clip
169 + params of v3
170 + params of v4
171 */
172 memcpy(new_params, old_params,
173 sizeof(dt_iop_highlights_params_t) - 5 * sizeof(float) - 2 * sizeof(int)
176 n->clip = 1.0f;
177 n->noise_level = 0.0f;
178 n->reconstructing = 0.4f;
179 n->combine = 2.f;
180 n->debugmode = 0;
181 n->iterations = 1;
182 n->scales = 5;
183 n->solid_color = 0.f;
184 return 0;
185 }
186 if(old_version == 2 && new_version == 4)
187 {
188 /*
189 params of v3 :
190 float noise_level;
191 int iterations;
192 dt_atrous_wavelets_scales_t scales;
193 float reconstructing;
194 float combine;
195 int debugmode;
196 + params of v4
197 */
198 memcpy(new_params, old_params,
199 sizeof(dt_iop_highlights_params_t) - 4 * sizeof(float) - 2 * sizeof(int)
202 n->noise_level = 0.0f;
203 n->reconstructing = 0.4f;
204 n->combine = 2.f;
205 n->debugmode = 0;
206 n->iterations = 1;
207 n->scales = 5;
208 n->solid_color = 0.f;
209 return 0;
210 }
211 if(old_version == 3 && new_version == 4)
212 {
213 /*
214 params of v4 :
215 float solid_color;
216 */
217 memcpy(new_params, old_params, sizeof(dt_iop_highlights_params_t) - sizeof(float));
219 n->solid_color = 0.f;
220 return 0;
221 }
222
223 return 1;
224}
225
226// ===== early bypass: nothing worth reconstructing ============================================
227// The clipping threshold the ACTIVE MODE actually tests against, so the bypass counts exactly what
228// that mode would call clipped instead of a conservative envelope of all of them: colour inpainting
229// takes 0.987 of each channel's white point, the two reconstruction modes 0.995, and plain clip /
230// LCh the scalar clip (the smallest channel's white point, which is what they clamp to). Keep these
231// factors in step with the clips[] each case of the mode switch builds below.
232static inline void _hl_count_thresholds(const int mode, const float user_clip,
233 const dt_aligned_pixel_t pmax, const float clip,
234 dt_aligned_pixel_t thresholds)
235{
236 float factor = 0.f;
237 switch(mode)
238 {
240 factor = 0.987f;
241 break;
244 factor = 0.995f;
245 break;
248 default:
249 factor = 0.f; // no per-channel white point: these test the scalar clip
250 break;
251 }
252 for(int c = 0; c < 3; c++) thresholds[c] = (factor > 0.f) ? factor * user_clip * pmax[c] : clip;
253 thresholds[3] = clip;
254}
255
256// Count the input pixels the active mode would treat as clipped, against those thresholds. A
257// demosaiced pixel counts if any of R/G/B is over its own threshold; a mosaic pixel is one
258// photosite, whose colour would need a CFA lookup (FC / FCxtrans) to pick the matching threshold.
259// This module runs AFTER white balance, so processed_maximum carries the WB coefficients and the
260// three thresholds genuinely differ on raw (measured {2.28, 1.00, 1.60} on a Fuji X-Trans file).
261// The smallest is used instead of the per-photosite one: that OVER-counts -- an R photosite between
262// 0.995 and its own 2.28 threshold is counted as clipped -- so the bypass fires less often than it
263// strictly could, never more. Deliberate: this is a "nothing to do" guard, and a frame quiet enough
264// to bypass has every channel under every threshold anyway. Exactness here would cost a CFA lookup
265// per pixel on both the CPU and the device.
266static size_t _hl_count_clipped(const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
267 const dt_iop_roi_t *const roi_out, const dt_aligned_pixel_t thresholds)
268{
269 const float *const restrict in = (const float *const restrict)ivoid;
270 const size_t n_pixels = (size_t)roi_out->width * roi_out->height;
271 size_t clipped = 0;
272
273 if(piece->dsc_in.filters)
274 {
275 const float raw_threshold = fminf(fminf(thresholds[0], thresholds[1]), thresholds[2]);
276 __OMP_PARALLEL_FOR_SIMD__(reduction(+ : clipped))
277 for(size_t k = 0; k < n_pixels; k++) clipped += (in[k] > raw_threshold);
278 }
279 else
280 {
281 const size_t ch = piece->dsc_in.channels;
282 const size_t n_colours = MIN(ch, (size_t)3);
283 __OMP_PARALLEL_FOR__(reduction(+ : clipped))
284 for(size_t k = 0; k < n_pixels; k++)
285 {
286 int over = 0;
287 for(size_t c = 0; c < n_colours; c++) over |= (in[k * ch + c] > thresholds[c]);
288 clipped += (over != 0);
289 }
290 }
291 return clipped;
292}
293
294// Pure copy input -> output, the bypass result. roi_in == roi_out here (this module declares no
295// modify_roi_*), so the buffers have identical geometry and channel count.
296static void _hl_copy_input(const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid,
297 const dt_iop_roi_t *const roi_out)
298{
299 const size_t ch = piece->dsc_in.filters ? 1 : (size_t)piece->dsc_in.channels;
300 dt_iop_image_copy_by_size((float *const)ovoid, (const float *const)ivoid, roi_out->width, roi_out->height,
301 ch);
302}
303
304#ifdef HAVE_OPENCL
305
306// The per-mode OpenCL helpers below carry the kernel-argument boilerplate so process_cl() reads as a
307// clean mode switch, symmetric to process(). Each returns a cl_int (CL_SUCCESS or an error to bubble up).
308
309// Clip-visualization false-colour quad (raw only; the caller sets mask_display/bypass_blendif on success).
310static cl_int _hl_cl_visualize(dt_iop_highlights_global_data_t *gd, const int devid, cl_mem dev_in,
311 cl_mem dev_out, const int width, const int height,
312 const dt_iop_roi_t *const roi_out, const uint32_t filters,
313 const dt_aligned_pixel_t clips)
314{
315 cl_mem dev_clips = dt_opencl_copy_host_to_device_constant(devid, 4 * sizeof(float), (float *)clips);
316 if(IS_NULL_PTR(dev_clips)) return DT_OPENCL_DEFAULT_ERROR;
317 size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
318 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_false_color, 0, sizeof(cl_mem), (void *)&dev_in);
319 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_false_color, 1, sizeof(cl_mem), (void *)&dev_out);
320 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_false_color, 2, sizeof(int), (void *)&width);
321 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_false_color, 3, sizeof(int), (void *)&height);
322 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_false_color, 4, sizeof(int), (void *)&roi_out->x);
323 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_false_color, 5, sizeof(int), (void *)&roi_out->y);
324 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_false_color, 6, sizeof(int), (void *)&filters);
325 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_false_color, 7, sizeof(cl_mem), (void *)&dev_clips);
326 const cl_int err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_highlights_false_color, sizes);
328 return err;
329}
330
331// Device twin of _hl_count_clipped: one workgroup-local reduction per group, summed here.
332static cl_int _hl_cl_count_clipped(dt_iop_highlights_global_data_t *gd, const int devid, cl_mem dev_in,
333 const int width, const int height,
334 const dt_aligned_pixel_t thresholds, const int is_raw,
335 size_t *const clipped)
336{
337 const int local_size = 64, n_groups = 256;
338 *clipped = 0;
339 cl_mem partial = dt_opencl_alloc_device_buffer(devid, sizeof(uint32_t) * n_groups);
340 cl_mem dev_thresholds = dt_opencl_copy_host_to_device_constant(devid, 4 * sizeof(float),
341 (float *)thresholds);
342 if(IS_NULL_PTR(partial) || IS_NULL_PTR(dev_thresholds))
343 {
345 dt_opencl_release_mem_object(dev_thresholds);
347 }
348
350 size_t sizes[3] = { (size_t)n_groups * local_size, 1, 1 };
351 size_t local[3] = { local_size, 1, 1 };
352 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&dev_in);
353 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), (void *)&partial);
354 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), (void *)&width);
355 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), (void *)&height);
356 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), (void *)&dev_thresholds);
357 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), (void *)&is_raw);
358 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(uint32_t) * local_size, NULL);
359 cl_int err = dt_opencl_enqueue_kernel_2d_with_local(devid, kernel, sizes, local);
360
361 if(err == CL_SUCCESS)
362 {
363 uint32_t partial_host[256];
364 err = dt_opencl_read_buffer_from_device(devid, partial_host, partial, 0, sizeof(uint32_t) * n_groups,
365 CL_TRUE);
366 if(err == CL_SUCCESS)
367 for(int group = 0; group < n_groups; group++) *clipped += (size_t)partial_host[group];
368 }
370 dt_opencl_release_mem_object(dev_thresholds);
371 return err;
372}
373
374// Plain clip. Raw mosaic uses the single-channel kernel (roi-aware); non-raw uses the 4-channel kernel.
375// This is also the fallback for the raw-only reconstruction modes (LCh / colour inpainting) on non-raw.
376static cl_int _hl_cl_clip(dt_iop_highlights_global_data_t *gd, const int devid, cl_mem dev_in, cl_mem dev_out,
377 const int width, const int height, const dt_iop_roi_t *const roi_out,
378 const uint32_t filters, const int mode, const float clip)
379{
380 size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
381 if(filters)
382 {
383 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_clip, 0, sizeof(cl_mem), (void *)&dev_in);
384 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_clip, 1, sizeof(cl_mem), (void *)&dev_out);
385 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_clip, 2, sizeof(int), (void *)&width);
386 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_clip, 3, sizeof(int), (void *)&height);
387 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_clip, 4, sizeof(float), (void *)&clip);
388 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_clip, 5, sizeof(int), (void *)&roi_out->x);
389 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_clip, 6, sizeof(int), (void *)&roi_out->y);
390 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_clip, 7, sizeof(int), (void *)&filters);
392 }
393 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_4f_clip, 0, sizeof(cl_mem), (void *)&dev_in);
394 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_4f_clip, 1, sizeof(cl_mem), (void *)&dev_out);
395 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_4f_clip, 2, sizeof(int), (void *)&width);
396 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_4f_clip, 3, sizeof(int), (void *)&height);
397 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_4f_clip, 4, sizeof(int), (void *)&mode);
398 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_4f_clip, 5, sizeof(float), (void *)&clip);
400}
401
402// LCh reconstruction, Bayer.
403static cl_int _hl_cl_lch_bayer(dt_iop_highlights_global_data_t *gd, const int devid, cl_mem dev_in,
404 cl_mem dev_out, const int width, const int height,
405 const dt_iop_roi_t *const roi_out, const uint32_t filters, const float clip)
406{
407 size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
408 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_bayer, 0, sizeof(cl_mem), (void *)&dev_in);
409 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_bayer, 1, sizeof(cl_mem), (void *)&dev_out);
410 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_bayer, 2, sizeof(int), (void *)&width);
411 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_bayer, 3, sizeof(int), (void *)&height);
412 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_bayer, 4, sizeof(float), (void *)&clip);
413 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_bayer, 5, sizeof(int), (void *)&roi_out->x);
414 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_bayer, 6, sizeof(int), (void *)&roi_out->y);
415 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_bayer, 7, sizeof(int), (void *)&filters);
417}
418
419// LCh reconstruction, X-Trans (needs the local-memory tile plus a device copy of the xtrans matrix).
420static cl_int _hl_cl_lch_xtrans(dt_iop_highlights_global_data_t *gd, const int devid, cl_mem dev_in,
421 cl_mem dev_out, const int width, const int height,
422 const dt_iop_roi_t *const roi_out, const dt_dev_pixelpipe_iop_t *piece,
423 const float clip)
424{
425 int blocksizex, blocksizey;
427 .xfactor = 1,
428 .yoffset = 2 * 2,
429 .yfactor = 1,
430 .cellsize = sizeof(float),
431 .overhead = 0,
432 .sizex = 1 << 8,
433 .sizey = 1 << 8 };
435 {
436 blocksizex = locopt.sizex;
437 blocksizey = locopt.sizey;
438 }
439 else
440 blocksizex = blocksizey = 1;
441
442 cl_mem dev_xtrans = dt_opencl_copy_host_to_device_constant(devid, sizeof(piece->dsc_in.xtrans),
443 (void *)piece->dsc_in.xtrans);
444 if(IS_NULL_PTR(dev_xtrans)) return DT_OPENCL_DEFAULT_ERROR;
445
446 size_t sizes[] = { ROUNDUP(width, blocksizex), ROUNDUP(height, blocksizey), 1 };
447 size_t local[] = { blocksizex, blocksizey, 1 };
448 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_xtrans, 0, sizeof(cl_mem), (void *)&dev_in);
449 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_xtrans, 1, sizeof(cl_mem), (void *)&dev_out);
450 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_xtrans, 2, sizeof(int), (void *)&width);
451 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_xtrans, 3, sizeof(int), (void *)&height);
452 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_xtrans, 4, sizeof(float), (void *)&clip);
453 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_xtrans, 5, sizeof(int), (void *)&roi_out->x);
454 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_xtrans, 6, sizeof(int), (void *)&roi_out->y);
455 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_1f_lch_xtrans, 7, sizeof(cl_mem), (void *)&dev_xtrans);
457 sizeof(float) * (blocksizex + 4) * (blocksizey + 4), NULL);
459 local);
461 return err;
462}
463
464int process_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece,
465 cl_mem dev_in, cl_mem dev_out)
466{
467 const dt_iop_roi_t *const roi_in = &piece->roi_in;
468 const dt_iop_roi_t *const roi_out = &piece->roi_out;
472
473 const uint32_t filters = piece->dsc_in.filters;
474 const int devid = pipe->devid;
475 const int width = roi_in->width;
476 const int height = roi_in->height;
477
478 /* This transient preview belongs to the central darkroom view. Do not infer
479 * its owner from ROI geometry: at zoom-to-fit the main and navigation pipes
480 * can produce identical dimensions while both still need distinct outputs. */
481 // The clip-visualization quad reads the buffer as a raw mosaic (FC / false-colour kernel), so it is
482 // raw-only; the toggle is also hidden on non-raw images in gui_update().
483 const gboolean visualizing = !IS_NULL_PTR(g) && g->show_visualize && filters && self->dev->gui_attached
484 && pipe == self->dev->pipe;
485
486 cl_int err = DT_OPENCL_DEFAULT_ERROR;
487
488 if(visualizing)
489 {
490 const dt_aligned_pixel_t clips = { 0.995f * d->clip * piece->dsc_in.processed_maximum[0],
491 0.995f * d->clip * piece->dsc_in.processed_maximum[1],
492 0.995f * d->clip * piece->dsc_in.processed_maximum[2], d->clip };
493 err = _hl_cl_visualize(gd, devid, dev_in, dev_out, width, height, roi_out, filters, clips);
494 if(err != CL_SUCCESS) goto error;
495 /* The clipping preview is the final output of this module. Blending would interpret PASSTHRU as a
496 * channel-display request and replace RAW output with zeroes before the downstream demosaic stage
497 * can display it. */
499 ((dt_dev_pixelpipe_t *)pipe)->bypass_blendif = 1;
500 return TRUE;
501 }
502
503 // Clip white point -- default non-positive channels (non-raw, where rawprepare never set it) to 1.0,
504 // mirroring process(). Without this the non-raw clip threshold collapses to 0 and the reconstruction
505 // treats the whole frame as clipped. Raw is unchanged (rawprepare already made every channel 1.0).
507 for(int c = 0; c < 4; c++)
508 pmax[c] = (piece->dsc_in.processed_maximum[c] > 0.f) ? piece->dsc_in.processed_maximum[c] : 1.0f;
509 const float clip = d->clip * fminf(pmax[0], fminf(pmax[1], pmax[2]));
510 const dt_aligned_pixel_t clips
511 = { 0.995f * d->clip * pmax[0], 0.995f * d->clip * pmax[1], 0.995f * d->clip * pmax[2], clip };
512
513 // Early bypass: with a negligible clipped area every mode below would pay a full-frame pass (the
514 // reconstruction modes several, plus region segmentation and sparse solves) to move a couple of
515 // dozen pixels. Count first and copy the input through instead. Mirrors process().
516 {
517 dt_aligned_pixel_t count_thresholds;
518 _hl_count_thresholds(d->mode, d->clip, pmax, clip, count_thresholds);
519 size_t n_clipped = 0;
520 err = _hl_cl_count_clipped(gd, devid, dev_in, width, height, count_thresholds, filters != 0, &n_clipped);
521 if(err != CL_SUCCESS) goto error;
522 if(n_clipped < DT_HL_MIN_CLIPPED_PIXELS)
523 {
524 size_t origin[] = { 0, 0, 0 };
525 size_t region[] = { width, height, 1 };
526 err = dt_opencl_enqueue_copy_image(devid, dev_in, dev_out, origin, origin, region);
527 if(err != CL_SUCCESS) goto error;
528 return TRUE;
529 }
530 }
531
532 // Mode switch, symmetric to process(). The reconstruction modes run on the GPU for raw and non-raw
533 // alike -- the non-raw passthrough drivers use their own device gather/remosaic kernels, no CPU
534 // roundtrip. The raw-only modes (LCh / colour inpainting) fall back to a device clip on non-raw input.
535 switch(d->mode)
536 {
538 if(filters == 9u)
539 err = _hl_cl_lch_xtrans(gd, devid, dev_in, dev_out, width, height, roi_out, piece, clip);
540 else if(filters)
541 err = _hl_cl_lch_bayer(gd, devid, dev_in, dev_out, width, height, roi_out, filters, clip);
542 else
543 err = _hl_cl_clip(gd, devid, dev_in, dev_out, width, height, roi_out, filters, d->mode, clip);
544 break;
546 if(filters == 9u)
547 err = process_laplacian_xtrans_cl(self, pipe, piece, dev_in, dev_out, roi_in, roi_out, clips);
548 else if(filters)
549 err = process_laplacian_bayer_cl(self, pipe, piece, dev_in, dev_out, roi_in, roi_out, clips);
550 else
551 err = process_laplacian_passthrough_cl(self, pipe, piece, dev_in, dev_out, roi_in, roi_out, clips);
552 break;
554 // process_harmonic_cl handles Bayer, X-Trans and non-raw passthrough (selected internally); its
555 // reconstruction middle is CFA-agnostic and reused for all three.
556 err = process_harmonic_cl(self, pipe, piece, dev_in, dev_out, roi_in, roi_out, clips);
557 break;
559 // Colour inpainting has no device kernel; commit_params() clears process_cl_ready so this is not
560 // normally reached. Signal a CPU fallback defensively.
561 return FALSE;
563 default:
564 err = _hl_cl_clip(gd, devid, dev_in, dev_out, width, height, roi_out, filters, d->mode, clip);
565 break;
566 }
567
568 if(err != CL_SUCCESS) goto error;
569 return TRUE;
570
571error:
572 dt_print(DT_DEBUG_OPENCL, "[opencl_highlights] couldn't enqueue kernel! %i\n", err);
573 return FALSE;
574}
575#endif
576
577void tiling_callback(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe,
578 const struct dt_dev_pixelpipe_iop_t *piece, struct dt_develop_tiling_t *tiling)
579{
580 const dt_iop_roi_t *const roi_in = &piece->roi_in;
582 const uint32_t filters = piece->dsc_in.filters;
583
584 if((d->mode == DT_IOP_HIGHLIGHTS_LAPLACIAN || d->mode == DT_IOP_HIGHLIGHTS_HARMONIC) && filters)
585 {
586 // Mosaic CFA and guided laplacian method: prepare for wavelets decomposition.
587 // Harmonic transposition reuses the same conservative budget (it disables tiling in
588 // commit_params and allocates region buffers well under this envelope).
589 const float scale = DS_FACTOR * dt_dev_get_module_scale(pipe, roi_in);
590 const float final_radius = (float)((int)(1 << d->scales)) / scale;
591 const int scales = CLAMP((int)ceilf(log2f(final_radius)), 1, MAX_NUM_SCALES);
592 const int max_filter_radius = (1 << scales);
593
594 // Warning: in and out are single-channel in RAW mode.
595 // in + out + interpolated + ds_interpolated + ds_tmp + 2 * ds_LF + ds_HF + mask + ds_mask
596 tiling->factor = 2.f + 2.f * 4 + 6.f * 4 / DS_FACTOR;
597 // OpenCL adds a downsampled scratch accumulator to keep the guided-laplacian read/write images distinct.
598 // in + out + interpolated + temp + mask + ds_interpolated + reconstructed_scratch + 2 * ds_LF + ds_HF + ds_mask
599 tiling->factor_cl = 2.f + 3.f * 4 + 6.f * 4 / DS_FACTOR;
600
601 // The wavelets decomposition uses a temp buffer of size 4 x ds_width
602 tiling->maxbuf = 1.f / roi_in->height * 4.f / DS_FACTOR;
603
604 // No temp buffer on GPU
605 tiling->maxbuf_cl = 1.0f;
606 tiling->overhead = 0;
607
608 // Note : if we were not doing anything iterative,
609 // max_filter_radius would not need to be factored more.
610 // Since we are iterating within tiles, we need more padding.
611 // The clean way of doing it would be an internal tiling mechanism
612 // where we restitch the tiles between each new iteration.
613 tiling->overlap = max_filter_radius * 1.5f / DS_FACTOR;
614 tiling->xalign = (filters == 9u) ? 6 : 2;
615 tiling->yalign = (filters == 9u) ? 6 : 2;
616
617 return;
618 }
619
620 tiling->factor = 2.0f; // in + out
621 tiling->maxbuf = 1.0f;
622 tiling->overhead = 0;
623
624 if(filters == 9u)
625 {
626 // xtrans
627 tiling->xalign = 6;
628 tiling->yalign = 6;
629 tiling->overlap = (d->mode == DT_IOP_HIGHLIGHTS_LCH) ? 2 : 0;
630 }
631 else if(filters)
632 {
633 // bayer
634 tiling->xalign = 2;
635 tiling->yalign = 2;
636 tiling->overlap = (d->mode == DT_IOP_HIGHLIGHTS_LCH) ? 1 : 0;
637 }
638 else
639 {
640 // non-raw
641 tiling->xalign = 1;
642 tiling->yalign = 1;
643 tiling->overlap = 0;
644 }
645}
646
647#undef SQRT3
648#undef SQRT12
649
650
651// Human-readable mode label (matches the $DESCRIPTION strings on dt_iop_highlights_mode_t), for the
652// non-raw fallback warning. Wrap in _() at the call site for translation.
654{
655 switch(mode)
656 {
657 case DT_IOP_HIGHLIGHTS_CLIP: return N_("clip highlights");
658 case DT_IOP_HIGHLIGHTS_LCH: return N_("reconstruct in LCh");
659 case DT_IOP_HIGHLIGHTS_INPAINT: return N_("reconstruct color");
660 case DT_IOP_HIGHLIGHTS_LAPLACIAN: return N_("guided laplacians");
661 case DT_IOP_HIGHLIGHTS_HARMONIC: return N_("harmonic transposition");
662 }
663 return N_("unknown");
664}
665
666// Returns TRUE when `mode` has a real path for the given (roi-shifted) CFA descriptor. Any raw mosaic
667// (filters != 0) supports every mode. On already-demosaiced input (filters == 0) CLIP thresholds each
668// channel and LAPLACIAN/HARMONIC reconstruct via their passthrough gather, but LCh and colour inpainting
669// are raw-mosaic-only and silently fall back to clip. The channels!=4 mono-raw case never reaches here:
670// it is excluded at history level by enable()/force_enable() (monochrome images are rejected). Keep in
671// sync with the process() mode switch.
672static gboolean _highlights_mode_supported(const dt_iop_highlights_mode_t mode, const uint32_t filters)
673{
674 if(filters) return TRUE; // any raw mosaic: every mode has a path
675 return (mode == DT_IOP_HIGHLIGHTS_CLIP || mode == DT_IOP_HIGHLIGHTS_LAPLACIAN
676 || mode == DT_IOP_HIGHLIGHTS_HARMONIC);
677}
678
680int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece,
681 const void *const ivoid, void *const ovoid)
682{
683 const dt_iop_roi_t *const roi_in = &piece->roi_in;
684 const dt_iop_roi_t *const roi_out = &piece->roi_out;
685 // process_visualize and interpolate_color (INPAINT mode) below read FC(row, col, filters)
686 // with tile-local row/col, so filters must be pre-shifted for roi_in's crop position here.
687 // The laplacian dispatch below passes roi_in/roi_out, not this filters value, and derives its
688 // own (correctly shifted) copy internally -- shifting this local doesn't affect it. The
689 // filters==9u / !filters checks are shift-invariant.
690 const uint32_t filters = dt_dev_get_roi_filters(piece, roi_in);
693
694 /* This transient preview belongs to the central darkroom view. Do not infer
695 * its owner from ROI geometry: at zoom-to-fit the main and navigation pipes
696 * can produce identical dimensions while both still need distinct outputs. */
697 // The clip-visualization quad reads the buffer as a raw mosaic (FC / false-colour kernel), so it is
698 // raw-only; the toggle is also hidden on non-raw images in gui_update().
699 const gboolean visualizing = !IS_NULL_PTR(g) && g->show_visualize && filters && self->dev->gui_attached
700 && pipe == self->dev->pipe;
701
702 if(visualizing)
703 {
704 process_visualize(piece, ivoid, ovoid, roi_in, roi_out, filters, data);
705 /* The clipping preview is the final output of this module. Blending would
706 * interpret PASSTHRU as a channel-display request and replace RAW output
707 * with zeroes before the downstream demosaic stage can display it. */
709 ((dt_dev_pixelpipe_t *)pipe)->bypass_blendif = 1;
710 return 0;
711 }
712
713 // Clip white point. rawprepare sets processed_maximum to 1.0 for raw; on non-raw it is never set and
714 // stays 0, which would collapse the clip threshold to 0 -- marking the whole image as clipped (a black
715 // "clip" and full-frame over-reconstruction / halos in laplacian & harmonic). Default any non-positive
716 // channel to 1.0 (the white point of normalized RGB) so non-raw input clips only genuinely blown
717 // highlights. Raw is unchanged: rawprepare already made every channel 1.0.
719 for(int c = 0; c < 4; c++)
720 pmax[c] = (piece->dsc_in.processed_maximum[c] > 0.f) ? piece->dsc_in.processed_maximum[c] : 1.0f;
721 const float clip = data->clip * fminf(pmax[0], fminf(pmax[1], pmax[2]));
722
723 // Non-raw input is no longer nuked to a plain clip here: the mode switch below dispatches it too
724 // (LAPLACIAN/HARMONIC reconstruct already-demosaiced RGB via their passthrough gather, CLIP thresholds
725 // per channel, LCh/INPAINT have no non-raw path and fall back to _highlights_copy_input).
726
727 // Early bypass, twin of process_cl()'s: nothing worth reconstructing -> copy the input through.
728 dt_aligned_pixel_t count_thresholds;
729 _hl_count_thresholds(data->mode, data->clip, pmax, clip, count_thresholds);
730 if(_hl_count_clipped(piece, ivoid, roi_out, count_thresholds) < DT_HL_MIN_CLIPPED_PIXELS)
731 {
732 _hl_copy_input(piece, ivoid, ovoid, roi_out);
733 return 0;
734 }
735
736 switch(data->mode)
737 {
738 case DT_IOP_HIGHLIGHTS_INPAINT: // a1ex's (magiclantern) idea of color inpainting:
739 {
740 const float clips[4] = { 0.987f * data->clip * pmax[0], 0.987f * data->clip * pmax[1],
741 0.987f * data->clip * pmax[2], clip };
742 if(filters == 9u)
743 process_inpaint_xtrans(ivoid, ovoid, roi_in, roi_out, clips,
744 (const uint8_t(*const)[6])piece->dsc_in.xtrans);
745 else if(filters)
746 process_inpaint_bayer(ivoid, ovoid, roi_out, clips, filters);
747 else
748 process_clip(piece, ivoid, ovoid, roi_in, roi_out, clip); // colour inpainting is raw-mosaic only
749 break;
750 }
752 if(filters == 9u)
753 process_lch_xtrans(self, piece, ivoid, ovoid, roi_in, roi_out, clip);
754 else if(filters)
755 process_lch_bayer(self, piece, ivoid, ovoid, roi_in, roi_out, clip);
756 else
757 process_clip(piece, ivoid, ovoid, roi_in, roi_out, clip); // LCh is raw-mosaic only
758 break;
760 {
761 // process_laplacian reconstructs Bayer, X-Trans and already-demosaiced RGB (passthrough gather,
762 // selected internally from the CFA descriptor). Non-raw input reaching here is guaranteed 4-channel:
763 // the channels!=4 mono-raw case is an image-type decision made once in force_enable()/commit_params(),
764 // not per frame -- the module never runs on such images.
765 const dt_aligned_pixel_t clips = { 0.995f * data->clip * pmax[0], 0.995f * data->clip * pmax[1],
766 0.995f * data->clip * pmax[2], clip };
767 if(process_laplacian(self, pipe, piece, ivoid, ovoid, roi_in, roi_out, clips))
768 return 1;
769 break;
770 }
772 {
773 const dt_aligned_pixel_t clips = { 0.995f * data->clip * pmax[0], 0.995f * data->clip * pmax[1],
774 0.995f * data->clip * pmax[2], clip };
775 if(process_harmonic(self, pipe, piece, ivoid, ovoid, roi_in, roi_out, clips))
776 return 1;
777 break;
778 }
779 default:
781 process_clip(piece, ivoid, ovoid, roi_in, roi_out, clip); // handles raw + non-raw (per channel)
782 break;
783 }
784
785 // TODO: this should be handled in the pipeline recursion directly
787 dt_iop_alpha_copy(ivoid, ovoid, roi_out->width, roi_out->height);
788 return 0;
789}
790
793{
796
797 memcpy(d, p, sizeof(*p));
798
799 // Image-type eligibility (raw colorimetry, not monochrome) is decided once at history level by
800 // enable()/force_enable()/reload_defaults(); nothing type-related is decided per frame. process()'s
801 // mode switch dispatches non-raw input too: LAPLACIAN/HARMONIC reconstruct already-demosaiced RGB
802 // (sRAW / linear DNG) via their passthrough gather, CLIP thresholds per channel, and the raw-only
803 // modes (LCh / colour inpainting) fall back to clip.
804 const dt_image_t *const img = &self->dev->image_storage;
805 dt_iop_fmt_log(self, "commit: class=%s filters=%u mode=%d -> enabled=%d",
807 piece->enabled);
808
809 // no OpenCL for DT_IOP_HIGHLIGHTS_INPAINT. Every other mode keeps its CL path, including the non-raw
810 // reconstruction paths (LAPLACIAN/HARMONIC on already-demosaiced RGB), which run on the GPU through
811 // their dedicated passthrough gather/remosaic kernels -- no CPU roundtrip.
812 piece->process_cl_ready = (d->mode == DT_IOP_HIGHLIGHTS_INPAINT) ? 0 : 1;
813
814 // Warn once per pipe setup (not per frame) when a raw-only reconstruction mode (LCh / colour
815 // inpainting) has no path for this non-raw image and silently falls back to clipping.
816 if(!_highlights_mode_supported(d->mode, piece->dsc_in.filters))
817 {
819 "[highlights] mode \"%s\" has no reconstruction path for this image; falling back to clipping\n",
820 _highlights_mode_name(d->mode));
821 if(self->dev->gui_attached && pipe == self->dev->pipe)
822 dt_control_log(_("highlight reconstruction: \"%s\" is not available for this image type; clipping instead"),
823 _(_highlights_mode_name(d->mode)));
824 }
825
827 piece->cache_output_on_ram = TRUE;
828
829 if(d->mode == DT_IOP_HIGHLIGHTS_HARMONIC)
830 {
831 // The segmented full-resolution reconstruction needs each clipped region intact: tiling
832 // would split a large blown highlight (e.g. the sun) across tile borders, and the per-region
833 // biharmonic core dome would then be solved on each half against a fake tile edge (-> a
834 // half-recovered, dark-cored disc). Process the whole frame instead.
835 piece->process_tiling_ready = 0;
836 }
837
838 // Non-raw reconstruction (LAPLACIAN/HARMONIC passthrough) runs whole-frame too: the tiling budget in
839 // tiling_callback() is written for the single-channel raw layout, and the reconstruction downsamples
840 // internally -- keep the frame intact rather than mis-tiling it.
841 if(!piece->dsc_in.filters
843 piece->process_tiling_ready = 0;
844
846 {
847 if(!piece->dsc_in.filters)
848 {
849 // Non-raw: processed_maximum is left at 0 upstream (no rawprepare). Default it to the normalized-RGB
850 // white point (1.0) rather than propagating 0 downstream (which collapses any later clip logic).
851 const float m0 = fminf(piece->dsc_in.processed_maximum[0],
852 fminf(piece->dsc_in.processed_maximum[1], piece->dsc_in.processed_maximum[2]));
853 const float m = (m0 > 0.f) ? m0 : 1.0f;
854 for(int k = 0; k < 3; k++) piece->dsc_out.processed_maximum[k] = m;
855 }
856 else
857 {
858 const float m = fmaxf(piece->dsc_in.processed_maximum[0],
859 fmaxf(piece->dsc_in.processed_maximum[1], piece->dsc_in.processed_maximum[2]));
860 for(int k = 0; k < 3; k++) piece->dsc_out.processed_maximum[k] = m;
861 }
862 }
863}
864
865// Whether the module can run on this image at all (eligibility, NOT auto-enable). The module
866// self-disables only on a non-mosaiced image that is not 4-channel -- a mono-raw / greyscale
867// (filters==0, channels==1) has no colour to clip or reconstruct. Everything else is eligible: a
868// mosaiced raw, an already-demosaiced sRAW/linear-DNG, and a rendered RGB (all 4-channel or mosaic).
869// Monochrome is the image-level proxy for "not 4-channel"; mosaiced is filters!=0. Shared by
870// force_enable()/reload_defaults()/gui_update() so the self-disable rule lives in exactly one place.
871static gboolean _highlights_image_supported(const dt_image_t *image)
872{
873 return dt_image_is_mosaiced(image) || !dt_image_is_monochrome(image);
874}
875
876// Whether the module should be enabled BY DEFAULT (auto-on). Only raw colorimetry (mosaiced raw or
877// sRAW/linear-DNG), not monochrome: on already-rendered RGB the module is available but opt-in, never
878// auto-enabled. Must match the commit_params() gate above.
879static gboolean enable(const dt_image_t *image)
880{
881 return dt_image_needs_rawprepare(image) && !dt_image_is_monochrome(image);
882}
883
884gboolean force_enable(struct dt_iop_module_t *self, const gboolean current_state)
885{
886 // History sanitization: clamp against the eligibility rule (self-disable only on non-mosaiced &&
887 // not-4-channel). This lets an opt-in enable on a rendered RGB / sRAW survive, while still stripping
888 // a highlights entry pasted onto a mono-raw. Auto-enable (default_enabled) stays raw-only in
889 // reload_defaults()/gui_update(); this only decides whether an already-set enable may stand.
890 const gboolean active = _highlights_image_supported(&self->dev->image_storage);
891 const gboolean state = current_state && active;
892 dt_iop_fmt_log(self, "force_enable: class=%s supported=%d current=%d -> %d",
893 dt_image_pipe_class_name(dt_image_pipe_class(&self->dev->image_storage)), active, current_state,
894 state);
895 return state;
896}
897
899{
900 const int program = 2; // basic.cl, from programs.conf
903 module->data = gd;
904 gd->kernel_highlights_1f_clip = dt_opencl_create_kernel(program, "highlights_1f_clip");
905 gd->kernel_highlights_count_clipped = dt_opencl_create_kernel(program, "highlights_count_clipped");
906 const int harmonic_program = 38; // highlights_harmonic.cl (harmonic transposition, fp32)
907 const int sparse_program = 37; // highlights_sparse.cl (fp64 sparse solvers)
908 gd->kernel_sparse_chol_update_level = dt_opencl_create_kernel(sparse_program, "sparse_chol_update_level");
909 gd->kernel_sparse_chol_final_level = dt_opencl_create_kernel(sparse_program, "sparse_chol_final_level");
910 gd->kernel_sparse_chol_fwd_level = dt_opencl_create_kernel(sparse_program, "sparse_chol_fwd_level");
911 gd->kernel_sparse_chol_bwd_level = dt_opencl_create_kernel(sparse_program, "sparse_chol_bwd_level");
912 gd->kernel_hl_cg_fold = dt_opencl_create_kernel(sparse_program, "hl_cg_fold");
913 gd->kernel_hl_cg_alpha_step = dt_opencl_create_kernel(sparse_program, "hl_cg_alpha_step");
914 gd->kernel_hl_cg_beta_step = dt_opencl_create_kernel(sparse_program, "hl_cg_beta_step");
915 gd->kernel_hl_pde_rhs = dt_opencl_create_kernel(sparse_program, "hl_pde_rhs");
916 gd->kernel_hl_pde_scatter = dt_opencl_create_kernel(sparse_program, "hl_pde_scatter");
917 gd->kernel_hl_aniso_rhs = dt_opencl_create_kernel(sparse_program, "hl_aniso_rhs");
918 gd->kernel_hl_aniso_scatter = dt_opencl_create_kernel(sparse_program, "hl_aniso_scatter");
919 gd->kernel_hl_cg_r1 = dt_opencl_create_kernel(sparse_program, "hl_cg_r1");
920 gd->kernel_hl_cg_ap = dt_opencl_create_kernel(sparse_program, "hl_cg_ap");
921 gd->kernel_hl_cg_update = dt_opencl_create_kernel(sparse_program, "hl_cg_update");
922 gd->kernel_hl_cfa_steer = dt_opencl_create_kernel(harmonic_program, "hl_cfa_steer");
923 gd->kernel_hl_cfa_down = dt_opencl_create_kernel(harmonic_program, "hl_cfa_down");
924 gd->kernel_hl_cfa_box = dt_opencl_create_kernel(harmonic_program, "hl_cfa_box");
925 gd->kernel_hl_cfa_grad = dt_opencl_create_kernel(harmonic_program, "hl_cfa_grad");
926 gd->kernel_hl_cfa_tensor = dt_opencl_create_kernel(harmonic_program, "hl_cfa_tensor");
927 gd->kernel_hl_cfa_gnorm = dt_opencl_create_kernel(harmonic_program, "hl_cfa_gnorm");
928 gd->kernel_hl_cfa_weights = dt_opencl_create_kernel(harmonic_program, "hl_cfa_weights");
929 gd->kernel_hl_cfa_jacobi = dt_opencl_create_kernel(harmonic_program, "hl_cfa_jacobi");
930 gd->kernel_hl_cfa_jacobi_block = dt_opencl_create_kernel(harmonic_program, "hl_cfa_jacobi_block");
931 gd->kernel_hl_fill_down = dt_opencl_create_kernel(harmonic_program, "hl_fill_down");
932 gd->kernel_hl_fill_seed = dt_opencl_create_kernel(harmonic_program, "hl_fill_seed");
933 gd->kernel_hl_fill_seed_up = dt_opencl_create_kernel(harmonic_program, "hl_fill_seed_up");
934 gd->kernel_hl_fill_jacobi = dt_opencl_create_kernel(harmonic_program, "hl_fill_jacobi");
935 gd->kernel_hl_fill_jacobi_block = dt_opencl_create_kernel(harmonic_program, "hl_fill_jacobi_block");
936 gd->kernel_hl_fill_up = dt_opencl_create_kernel(harmonic_program, "hl_fill_up");
937 gd->kernel_hl_cf_lref_partials = dt_opencl_create_kernel(harmonic_program, "hl_cf_lref_partials");
938 gd->kernel_hl_cf_pack_joint = dt_opencl_create_kernel(harmonic_program, "hl_cf_pack_joint");
939 gd->kernel_hl_cf_fit_joint = dt_opencl_create_kernel(harmonic_program, "hl_cf_fit_joint");
940 gd->kernel_hl_cf_eval_joint = dt_opencl_create_kernel(harmonic_program, "hl_cf_eval_joint");
941 gd->kernel_hl_cf_pack_pair = dt_opencl_create_kernel(harmonic_program, "hl_cf_pack_pair");
942 gd->kernel_hl_cf_fit_pair = dt_opencl_create_kernel(harmonic_program, "hl_cf_fit_pair");
943 gd->kernel_hl_cf_eval_pair = dt_opencl_create_kernel(harmonic_program, "hl_cf_eval_pair");
944 gd->kernel_hl_cf_pack_deepmask = dt_opencl_create_kernel(harmonic_program, "hl_cf_pack_deepmask");
945 gd->kernel_hl_cf_eval_deep = dt_opencl_create_kernel(harmonic_program, "hl_cf_eval_deep");
946 gd->kernel_hl_buf_to_img = dt_opencl_create_kernel(harmonic_program, "hl_buf_to_img");
947 gd->kernel_hl_hf_pack = dt_opencl_create_kernel(harmonic_program, "hl_hf_pack");
948 gd->kernel_hl_hf_fit = dt_opencl_create_kernel(harmonic_program, "hl_hf_fit");
949 gd->kernel_hl_hf_energy = dt_opencl_create_kernel(harmonic_program, "hl_hf_energy");
950 gd->kernel_hl_hf_eval = dt_opencl_create_kernel(harmonic_program, "hl_hf_eval");
951 gd->kernel_hl_hf_damp = dt_opencl_create_kernel(harmonic_program, "hl_hf_damp");
952 gd->kernel_hl_soft_floor = dt_opencl_create_kernel(harmonic_program, "hl_soft_floor");
953 gd->kernel_hl_hard_floor = dt_opencl_create_kernel(harmonic_program, "hl_hard_floor");
954 gd->kernel_hl_lsb_hole = dt_opencl_create_kernel(harmonic_program, "hl_lsb_hole");
955 gd->kernel_hl_ratio_plane = dt_opencl_create_kernel(harmonic_program, "hl_ratio_plane");
956 gd->kernel_hl_dome_down = dt_opencl_create_kernel(harmonic_program, "hl_dome_down");
957 gd->kernel_hl_dome_blend = dt_opencl_create_kernel(harmonic_program, "hl_dome_blend");
958 gd->kernel_hl_core_floor = dt_opencl_create_kernel(harmonic_program, "hl_core_floor");
959 gd->kernel_hl_cmean_reduce = dt_opencl_create_kernel(harmonic_program, "hl_cmean_reduce");
960 gd->kernel_hl_ratio_cmean_blend = dt_opencl_create_kernel(harmonic_program, "hl_ratio_cmean_blend");
961 gd->kernel_hl_clip0_rehue = dt_opencl_create_kernel(harmonic_program, "hl_clip0_rehue");
962 gd->kernel_hl_ring_vote = dt_opencl_create_kernel(harmonic_program, "hl_ring_vote");
963 gd->kernel_hl_cgrad_plateau = dt_opencl_create_kernel(harmonic_program, "hl_cgrad_plateau");
964 gd->kernel_hl_cmean_finalize = dt_opencl_create_kernel(harmonic_program, "hl_cmean_finalize");
965 gd->kernel_hl_ring_vote_finalize = dt_opencl_create_kernel(harmonic_program, "hl_ring_vote_finalize");
966 gd->kernel_hl_region_benefit = dt_opencl_create_kernel(harmonic_program, "hl_region_benefit");
967 gd->kernel_hl_region_worth_finalize = dt_opencl_create_kernel(harmonic_program, "hl_region_worth_finalize");
968 gd->kernel_hl_reduce_finalize = dt_opencl_create_kernel(harmonic_program, "hl_reduce_finalize");
969 gd->kernel_hl_set_scalar = dt_opencl_create_kernel(harmonic_program, "hl_set_scalar");
970 gd->kernel_hl_vote_reduce = dt_opencl_create_kernel(harmonic_program, "hl_vote_reduce");
971 gd->kernel_hl_cgrad_guard = dt_opencl_create_kernel(harmonic_program, "hl_cgrad_guard");
972 gd->kernel_hl_cgrad_anchor = dt_opencl_create_kernel(harmonic_program, "hl_cgrad_anchor");
973 gd->kernel_hl_cgrad_share = dt_opencl_create_kernel(harmonic_program, "hl_cgrad_share");
974 gd->kernel_hl_cgrad_store = dt_opencl_create_kernel(harmonic_program, "hl_cgrad_store");
975 gd->kernel_hl_cgrad_gate = dt_opencl_create_kernel(harmonic_program, "hl_cgrad_gate");
976 gd->kernel_hl_cgrad_reproject = dt_opencl_create_kernel(harmonic_program, "hl_cgrad_reproject");
977 gd->kernel_hl_cgrad_hole1c = dt_opencl_create_kernel(harmonic_program, "hl_cgrad_hole1c");
978 gd->kernel_hl_cgrad_write1c = dt_opencl_create_kernel(harmonic_program, "hl_cgrad_write1c");
979 gd->kernel_hl_pde_init = dt_opencl_create_kernel(harmonic_program, "hl_pde_init");
980 gd->kernel_hl_mask_to_img1 = dt_opencl_create_kernel(harmonic_program, "hl_mask_to_img1");
981 gd->kernel_hl_core_blend = dt_opencl_create_kernel(harmonic_program, "hl_core_blend");
982 gd->kernel_hl_aniso_prep = dt_opencl_create_kernel(harmonic_program, "hl_aniso_prep");
983 gd->kernel_hl_box3 = dt_opencl_create_kernel(harmonic_program, "hl_box3");
984 gd->kernel_hl_grad_reduce = dt_opencl_create_kernel(harmonic_program, "hl_grad_reduce");
985 gd->kernel_hl_aniso_tensor = dt_opencl_create_kernel(harmonic_program, "hl_aniso_tensor");
986 gd->kernel_hl_aniso_weights = dt_opencl_create_kernel(harmonic_program, "hl_aniso_weights");
987 gd->kernel_hl_aniso_reassemble = dt_opencl_create_kernel(harmonic_program, "hl_aniso_reassemble");
988 gd->kernel_hl_knee_bin = dt_opencl_create_kernel(harmonic_program, "hl_knee_bin");
989 gd->kernel_hl_knee_jmom = dt_opencl_create_kernel(harmonic_program, "hl_knee_jmom");
990 gd->kernel_hl_knee_pmom = dt_opencl_create_kernel(harmonic_program, "hl_knee_pmom");
991 gd->kernel_hl_knee_joint_reg = dt_opencl_create_kernel(harmonic_program, "hl_knee_joint_reg");
992 gd->kernel_hl_knee_pair_reg = dt_opencl_create_kernel(harmonic_program, "hl_knee_pair_reg");
993 gd->kernel_hl_knee_apply = dt_opencl_create_kernel(harmonic_program, "hl_knee_apply");
994 gd->kernel_hl_mask_pack = dt_opencl_create_kernel(harmonic_program, "hl_mask_pack");
995 gd->kernel_hl_region_gather = dt_opencl_create_kernel(harmonic_program, "hl_region_gather");
996 gd->kernel_hl_region_scatter = dt_opencl_create_kernel(harmonic_program, "hl_region_scatter");
997 gd->kernel_hl_region_stats = dt_opencl_create_kernel(harmonic_program, "hl_region_stats");
998 gd->kernel_hl_need_self = dt_opencl_create_kernel(harmonic_program, "hl_need_self");
999 gd->kernel_hl_knee_apply_interp = dt_opencl_create_kernel(harmonic_program, "hl_knee_apply_interp");
1000 gd->kernel_hl_cg_embed = dt_opencl_create_kernel(harmonic_program, "hl_cg_embed");
1001 gd->kernel_hl_cg_op = dt_opencl_create_kernel(harmonic_program, "hl_cg_op");
1002 gd->kernel_hl_cg_r0 = dt_opencl_create_kernel(harmonic_program, "hl_cg_r0");
1003 gd->kernel_hl_cg_beta = dt_opencl_create_kernel(harmonic_program, "hl_cg_beta");
1004 gd->kernel_hl_relu = dt_opencl_create_kernel(harmonic_program, "hl_relu");
1005 gd->kernel_hl_aniso_pyr_down = dt_opencl_create_kernel(harmonic_program, "hl_aniso_pyr_down");
1006 gd->kernel_hl_pyr_getc = dt_opencl_create_kernel(harmonic_program, "hl_pyr_getc");
1007 gd->kernel_hl_pyr_getc4 = dt_opencl_create_kernel(harmonic_program, "hl_pyr_getc4");
1008 gd->kernel_hl_pyr_putc4 = dt_opencl_create_kernel(harmonic_program, "hl_pyr_putc4");
1009 gd->kernel_hl_pyr_project = dt_opencl_create_kernel(harmonic_program, "hl_pyr_project");
1010 gd->kernel_hl_aniso_obs_full = dt_opencl_create_kernel(harmonic_program, "hl_aniso_obs_full");
1011 gd->kernel_hl_aniso_obs_flags = dt_opencl_create_kernel(harmonic_program, "hl_aniso_obs_flags");
1012 gd->kernel_hl_window_pack = dt_opencl_create_kernel(harmonic_program, "hl_window_pack");
1013 gd->kernel_hl_window_unpack = dt_opencl_create_kernel(harmonic_program, "hl_window_unpack");
1014 gd->kernel_hl_pyr_putc = dt_opencl_create_kernel(harmonic_program, "hl_pyr_putc");
1015 gd->kernel_hl_aniso_iter = dt_opencl_create_kernel(harmonic_program, "hl_aniso_iter");
1016 gd->kernel_hl_aniso_iter_block = dt_opencl_create_kernel(harmonic_program, "hl_aniso_iter_block");
1017 gd->kernel_hl_dt_warp = dt_opencl_create_kernel(harmonic_program, "hl_dt_warp");
1018 gd->kernel_hl_dt_rows = dt_opencl_create_kernel(harmonic_program, "hl_dt_rows");
1019 gd->kernel_hl_dt_cols = dt_opencl_create_kernel(harmonic_program, "hl_dt_cols");
1020 gd->kernel_hl_aniso_splat = dt_opencl_create_kernel(harmonic_program, "hl_aniso_splat");
1021 gd->kernel_highlights_1f_lch_bayer = dt_opencl_create_kernel(program, "highlights_1f_lch_bayer");
1022 gd->kernel_highlights_1f_lch_xtrans = dt_opencl_create_kernel(program, "highlights_1f_lch_xtrans");
1023 gd->kernel_highlights_4f_clip = dt_opencl_create_kernel(program, "highlights_4f_clip");
1024 gd->kernel_highlights_bilinear_and_mask = dt_opencl_create_kernel(program, "interpolate_and_mask");
1025 gd->kernel_highlights_bilinear_and_mask_xtrans = dt_opencl_create_kernel(program, "interpolate_and_mask_xtrans");
1027 = dt_opencl_create_kernel(program, "interpolate_and_mask_passthrough");
1029 = dt_opencl_create_kernel(program, "highlights_normalize_reduce_first");
1031 = dt_opencl_create_kernel(program, "highlights_normalize_reduce_first_xtrans");
1033 = dt_opencl_create_kernel(program, "highlights_normalize_reduce_first_passthrough");
1035 = dt_opencl_create_kernel(program, "highlights_normalize_reduce_second");
1036 gd->kernel_highlights_remosaic_and_replace = dt_opencl_create_kernel(program, "remosaic_and_replace");
1038 = dt_opencl_create_kernel(program, "remosaic_and_replace_xtrans");
1040 = dt_opencl_create_kernel(program, "remosaic_and_replace_passthrough");
1041 gd->kernel_highlights_box_blur = dt_opencl_create_kernel(program, "box_blur_5x5");
1042 gd->kernel_highlights_guide_laplacians = dt_opencl_create_kernel(program, "guide_laplacians");
1043 gd->kernel_highlights_diffuse_color = dt_opencl_create_kernel(program, "diffuse_color");
1044 gd->kernel_highlights_false_color = dt_opencl_create_kernel(program, "highlights_false_color");
1045 gd->kernel_interpolate_bilinear = dt_opencl_create_kernel(program, "interpolate_bilinear");
1046
1047 const int wavelets = 35; // bspline.cl, from programs.conf
1048 gd->kernel_filmic_bspline_horizontal = dt_opencl_create_kernel(wavelets, "blur_2D_Bspline_horizontal");
1049 gd->kernel_filmic_bspline_vertical = dt_opencl_create_kernel(wavelets, "blur_2D_Bspline_vertical");
1051 = dt_opencl_create_kernel(wavelets, "blur_2D_Bspline_horizontal_local");
1052 gd->kernel_filmic_bspline_vertical_local = dt_opencl_create_kernel(wavelets, "blur_2D_Bspline_vertical_local");
1053}
1054
1056{
1190
1195
1197
1198 dt_free(module->data);
1199}
1200
1202{
1204 piece->data_size = sizeof(dt_iop_highlights_data_t);
1205}
1206
1208{
1209 dt_free_align(piece->data);
1210 piece->data = NULL;
1211}
1212
1213void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
1214{
1217
1218 const gboolean raw = (self->dev->image_storage.dsc.filters != 0);
1219 const gboolean israw = (self->dev->image_storage.dsc.filters != 0);
1220 dt_iop_highlights_mode_t mode = p->mode;
1221
1222 // harmonic transposition reads noise_level (regrain) and solid_color (all-clip core reaction);
1223 // iterations and scales belong to the a-trous guided laplacians only
1224 gtk_widget_set_visible(g->noise_level,
1225 raw && (mode == DT_IOP_HIGHLIGHTS_LAPLACIAN || mode == DT_IOP_HIGHLIGHTS_HARMONIC));
1226 gtk_widget_set_visible(g->iterations, raw && mode == DT_IOP_HIGHLIGHTS_LAPLACIAN);
1227 gtk_widget_set_visible(g->scales, raw && mode == DT_IOP_HIGHLIGHTS_LAPLACIAN);
1228 gtk_widget_set_visible(g->solid_color,
1229 raw && (mode == DT_IOP_HIGHLIGHTS_LAPLACIAN || mode == DT_IOP_HIGHLIGHTS_HARMONIC));
1230
1232}
1233
1234void gui_update(struct dt_iop_module_t *self)
1235{
1237 const dt_image_t *const image = &self->dev->image_storage;
1238 const gboolean supported = _highlights_image_supported(image);
1239 // Auto-enable only on raw colorimetry (raw / sRAW, not monochrome); on rendered RGB it is opt-in.
1240 self->default_enabled = enable(image);
1241
1242 // Show the on/off button for any eligible image (opt-in on rendered RGB / sRAW). Neuter it only where
1243 // the module self-disables (mono-raw / greyscale), and even then keep it if already enabled (history
1244 // copy & paste from a RAW image) so the user can turn it back off.
1245 self->hide_enable_button = !supported && !self->enabled;
1246 gtk_stack_set_visible_child_name(GTK_STACK(self->gui->widget), supported ? "default" : "monochrome");
1247
1248 // capability entries, added once (moved here from reload_defaults so it never touches widgets off
1249 // the GUI thread / on a widget-less export dev)
1251 dt_bauhaus_combobox_add_full(g->mode, _("guided laplacians"), DT_BAUHAUS_COMBOBOX_ALIGN_RIGHT,
1252 GINT_TO_POINTER(DT_IOP_HIGHLIGHTS_LAPLACIAN), NULL, TRUE);
1254 dt_bauhaus_combobox_add_full(g->mode, _("harmonic transposition"), DT_BAUHAUS_COMBOBOX_ALIGN_RIGHT,
1255 GINT_TO_POINTER(DT_IOP_HIGHLIGHTS_HARMONIC), NULL, TRUE);
1256
1258 g->show_visualize = FALSE;
1259 gui_changed(self, NULL, NULL);
1260}
1261
1263{
1264 // we might be called from presets update infrastructure => there is no image
1265 if(!module->dev || module->dev->image_storage.id == -1) return;
1266
1267 // Auto-enable only on raw colorimetry (raw / sRAW, not monochrome). Availability is broader: the
1268 // button is shown (opt-in) for any eligible image and hidden only on a non-mosaiced non-4-channel
1269 // image (mono-raw / greyscale), where the module self-disables -- there is nothing to reconstruct.
1270 module->default_enabled = enable(&module->dev->image_storage);
1271 module->hide_enable_button = !_highlights_image_supported(&module->dev->image_storage);
1272 dt_iop_fmt_log(module, "reload_defaults: class=%s default_enabled=%d hidden=%d",
1274 module->default_enabled, module->hide_enable_button);
1275 // Stack visibility and the "guided laplacians" capability entry are set from default_enabled in
1276 // gui_update() (which already does the stack), so reload_defaults() stays params-only.
1277}
1278
1279static void _visualize_callback(GtkWidget *quad, gpointer user_data)
1280{
1281 if(dt_gui_widgets_suppressed()) return;
1282 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
1284
1285 // if blend module is displaying mask do not display it here
1288
1289 g->show_visualize = dt_bauhaus_widget_get_quad_active(quad);
1290
1291 if(g->show_visualize) self->request_mask_display = DT_DEV_PIXELPIPE_DISPLAY_PASSTHRU;
1292
1293 dt_iop_set_cache_bypass(self, g->show_visualize);
1295}
1296
1297void gui_focus(struct dt_iop_module_t *self, gboolean in)
1298{
1300 if(!in)
1301 {
1302 const gboolean was_visualize = g->show_visualize;
1304 g->show_visualize = FALSE;
1305 if(was_visualize) dt_dev_pixelpipe_update_history_main(self->dev);
1306 }
1307}
1308
1309void gui_init(struct dt_iop_module_t *self)
1310{
1312 GtkWidget *box_raw = self->gui->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1313
1314 g->mode = dt_bauhaus_combobox_from_params(self, "mode");
1315 gtk_widget_set_tooltip_text(g->mode, _("highlight reconstruction method"));
1316
1317 g->clip = dt_bauhaus_slider_from_params(self, "clip");
1319 gtk_widget_set_tooltip_text(g->clip, _("manually adjust the clipping threshold against "
1320 "magenta highlights\nthe mask icon shows the clipped area\n"
1321 "(you shouldn't ever need to touch this)"));
1325 g_signal_connect(G_OBJECT(g->clip), "quad-pressed", G_CALLBACK(_visualize_callback), self);
1326
1327 g->noise_level = dt_bauhaus_slider_from_params(self, "noise_level");
1328 gtk_widget_set_tooltip_text(g->noise_level, _("add noise to visually blend the reconstructed areas\n"
1329 "into the rest of the noisy image. useful at high ISO."));
1330
1331 g->iterations = dt_bauhaus_slider_from_params(self, "iterations");
1332 dt_bauhaus_slider_set_soft_range(g->iterations, 1, 256);
1333 gtk_widget_set_tooltip_text(g->iterations, _("increase if magenta highlights don't get fully corrected\n"
1334 "each new iteration brings a performance penalty."));
1335
1336 g->solid_color = dt_bauhaus_slider_from_params(self, "solid_color");
1337 dt_bauhaus_slider_set_format(g->solid_color, "%");
1338 gtk_widget_set_tooltip_text(g->solid_color,
1339 _("increase if magenta highlights don't get fully corrected.\n"
1340 "this may produce non-smooth boundaries between valid and clipped regions."));
1341
1342 g->scales = dt_bauhaus_combobox_from_params(self, "scales");
1343 gtk_widget_set_tooltip_text(g->scales, _("increase to correct larger clipped areas.\n"
1344 "large values bring huge performance penalties"));
1345
1346 GtkWidget *monochromes = dt_ui_label_new(_("not applicable"));
1347 gtk_widget_set_tooltip_text(monochromes, _("no highlights reconstruction for monochrome images"));
1348
1349 // start building top level widget
1350 self->gui->widget = gtk_stack_new();
1351 gtk_stack_set_homogeneous(GTK_STACK(self->gui->widget), FALSE);
1352 gtk_stack_add_named(GTK_STACK(self->gui->widget), monochromes, "monochrome");
1353 gtk_stack_add_named(GTK_STACK(self->gui->widget), box_raw, "default");
1354}
1355
1356// clang-format off
1357// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1358// vim: shiftwidth=2 expandtab tabstop=2 cindent
1359// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1360// 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:97
#define m
Definition basecurve.c:283
void dt_bauhaus_slider_set_soft_range(GtkWidget *widget, float soft_min, float soft_max)
Definition bauhaus.c:1498
void dt_bauhaus_slider_set_digits(GtkWidget *widget, int val)
Definition bauhaus.c:3343
void dt_bauhaus_widget_set_quad_toggle(GtkWidget *widget, int toggle)
Definition bauhaus.c:1572
int dt_bauhaus_combobox_length(GtkWidget *widget)
Definition bauhaus.c:1963
void dt_bauhaus_widget_set_quad_active(GtkWidget *widget, int active)
Definition bauhaus.c:1578
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:1846
int dt_bauhaus_widget_get_quad_active(GtkWidget *widget)
Definition bauhaus.c:1595
void dt_bauhaus_widget_set_quad_visibility(GtkWidget *widget, const gboolean visible)
Definition bauhaus.c:1588
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
Definition bauhaus.c:3407
void dt_bauhaus_widget_set_quad_paint(GtkWidget *widget, dt_bauhaus_quad_paint_f f, int paint_flags, void *paint_data)
Definition bauhaus.c:1554
@ DT_BAUHAUS_COMBOBOX_ALIGN_RIGHT
Definition bauhaus.h:127
__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:88
__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:62
@ IOP_CS_RAW
@ IOP_CS_RGB
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
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)
gboolean dt_image_is_mosaiced(const dt_image_t *img)
void dt_control_log(const char *msg,...)
Definition control.c:824
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:43
#define dt_dev_pixelpipe_update_history_main(dev)
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
@ DT_DEV_PIXELPIPE_DISPLAY_PASSTHRU
Definition develop.h:141
@ DT_DEV_PIXELPIPE_DISPLAY_NONE
Definition develop.h:122
int supported(struct dt_imageio_module_storage_t *storage, struct dt_imageio_module_format_t *format)
Definition example.c:273
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:791
const char ** description(struct dt_iop_module_t *self)
Definition highlights.c:110
int default_group()
Definition highlights.c:117
__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:680
static void _hl_copy_input(const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid, const dt_iop_roi_t *const roi_out)
Definition highlights.c:296
static cl_int _hl_cl_visualize(dt_iop_highlights_global_data_t *gd, const int devid, cl_mem dev_in, cl_mem dev_out, const int width, const int height, const dt_iop_roi_t *const roi_out, const uint32_t filters, const dt_aligned_pixel_t clips)
Definition highlights.c:310
static gboolean _highlights_image_supported(const dt_image_t *image)
Definition highlights.c:871
void reload_defaults(dt_iop_module_t *module)
static void _hl_count_thresholds(const int mode, const float user_clip, const dt_aligned_pixel_t pmax, const float clip, dt_aligned_pixel_t thresholds)
Definition highlights.c:232
static cl_int _hl_cl_lch_xtrans(dt_iop_highlights_global_data_t *gd, const int devid, cl_mem dev_in, cl_mem dev_out, const int width, const int height, const dt_iop_roi_t *const roi_out, const dt_dev_pixelpipe_iop_t *piece, const float clip)
Definition highlights.c:420
static gboolean enable(const dt_image_t *image)
Definition highlights.c:879
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)
static cl_int _hl_cl_lch_bayer(dt_iop_highlights_global_data_t *gd, const int devid, cl_mem dev_in, cl_mem dev_out, const int width, const int height, const dt_iop_roi_t *const roi_out, const uint32_t filters, const float clip)
Definition highlights.c:403
const char * name()
Definition highlights.c:105
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:133
void gui_update(struct dt_iop_module_t *self)
Refresh GUI controls from current params and configuration.
static cl_int _hl_cl_clip(dt_iop_highlights_global_data_t *gd, const int devid, cl_mem dev_in, cl_mem dev_out, const int width, const int height, const dt_iop_roi_t *const roi_out, const uint32_t filters, const int mode, const float clip)
Definition highlights.c:376
void gui_init(struct dt_iop_module_t *self)
void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
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:577
static gboolean _highlights_mode_supported(const dt_iop_highlights_mode_t mode, const uint32_t filters)
Definition highlights.c:672
void cleanup_global(dt_iop_module_so_t *module)
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition highlights.c:127
int flags()
Definition highlights.c:122
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:139
gboolean force_enable(struct dt_iop_module_t *self, const gboolean current_state)
Definition highlights.c:884
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static void _visualize_callback(GtkWidget *quad, gpointer user_data)
static cl_int _hl_cl_count_clipped(dt_iop_highlights_global_data_t *gd, const int devid, cl_mem dev_in, const int width, const int height, const dt_aligned_pixel_t thresholds, const int is_raw, size_t *const clipped)
Definition highlights.c:332
void init_global(dt_iop_module_so_t *module)
Definition highlights.c:898
static size_t _hl_count_clipped(const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, const dt_iop_roi_t *const roi_out, const dt_aligned_pixel_t thresholds)
Definition highlights.c:266
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:464
static const char * _highlights_mode_name(const dt_iop_highlights_mode_t mode)
Definition highlights.c:653
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:161
static void dt_iop_image_copy_by_size(float *const __restrict__ out, const float *const __restrict__ in, const size_t width, const size_t height, const size_t ch)
Definition imagebuf.h:91
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:139
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
float dt_dev_get_module_scale(const dt_dev_pixelpipe_t *const pipe, const dt_iop_roi_t *const roi_in)
Definition imageop.c:134
void dt_iop_set_cache_bypass(dt_iop_module_t *module, gboolean state)
Definition imageop.c:1669
#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:535
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:186
@ IOP_FLAGS_ALLOW_TILING
Definition imageop.h:188
@ IOP_GROUP_REPAIR
Definition imageop.h:159
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
GtkWidget * dt_bauhaus_combobox_from_params(dt_iop_module_t *self, const char *param)
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
__DT_CLONE_TARGETS__ void process_inpaint_bayer(const void *const ivoid, void *const ovoid, const dt_iop_roi_t *const roi_out, const float clips[4], const uint32_t filters)
Definition inpaint.c:64
__DT_CLONE_TARGETS__ void process_inpaint_xtrans(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 clips[4], const uint8_t(*const xtrans)[6])
Definition inpaint.c:85
static float kernel(const float *x, const float *y)
GtkWidget * dt_ui_label_new(const gchar *str)
Definition label.c:125
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:764
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:1031
cl_int process_laplacian_passthrough_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:1309
__DT_CLONE_TARGETS__ int process_laplacian(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:433
__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:316
__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:413
@ DT_DEBUG_OPENCL
Definition logging.h:57
@ DT_DEBUG_ALWAYS
Definition logging.h:49
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 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_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
Definition mem_alloc.h:214
static void * dt_calloc_align(size_t size)
dt_alloc_align() followed by a zero fill.
Definition mem_alloc.h:225
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
DT_MODULE() for a module whose params struct is introspected.
int dt_opencl_local_buffer_opt(const int devid, const int kernel, dt_opencl_local_buffer_t *factors)
Definition opencl.c:3713
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
Definition opencl.c:2554
void * dt_opencl_alloc_device_buffer(const int devid, const size_t size)
Definition opencl.c:2970
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2448
void * dt_opencl_copy_host_to_device_constant(const int devid, const size_t size, void *host)
Definition opencl.c:2750
int dt_opencl_enqueue_copy_image(const int devid, cl_mem src, cl_mem dst, size_t *orig_src, size_t *orig_dst, size_t *region)
Definition opencl.c:2679
int dt_opencl_read_buffer_from_device(const int devid, void *host, void *device, const size_t offset, const size_t size, const int blocking)
Definition opencl.c:2727
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
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:2560
void dt_opencl_release_mem_object(cl_mem mem)
Definition opencl.c:2805
#define DT_OPENCL_DEFAULT_ERROR
Definition opencl.h:61
#define ROUNDUP(a, n)
Definition opencl.h:82
#define ROUNDUPDHT(a, b)
Definition opencl.h:86
#define ROUNDUPDWD(a, b)
Definition opencl.h:85
#define __OMP_PARALLEL_FOR__(...)
Definition openmp.h:95
#define __OMP_PARALLEL_FOR_SIMD__(...)
Definition openmp.h:96
const float factor
Definition pdf.h:91
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:619
__DT_CLONE_TARGETS__ int process_harmonic(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:42
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
Definition simd.h:53
@ DT_IOP_HIGHLIGHTS_HARMONIC
@ DT_IOP_HIGHLIGHTS_INPAINT
@ DT_IOP_HIGHLIGHTS_LAPLACIAN
#define DT_HL_MIN_CLIPPED_PIXELS
dt_iop_highlights_params_t dt_iop_highlights_data_t
#define DS_FACTOR
const float uint32_t state[4]
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:167
dt_image_t image_storage
Definition develop.h:225
struct dt_dev_pixelpipe_t * pipe
Definition develop.h:214
dt_iop_buffer_dsc_t dsc
Definition image.h:419
int32_t id
Definition image.h:401
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
GtkWidget * widget
Definition imageop_gui.h:47
dt_iop_global_data_t * data
Definition imageop.h:238
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
gboolean default_enabled
Definition imageop.h:318
dt_iop_global_data_t * global_data
Definition imageop.h:337
gboolean enabled
Definition imageop.h:313
int request_mask_display
Definition imageop.h:276
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__
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29
Telling the user something happened.
gboolean dt_gui_widgets_suppressed(void)
#define DT_GUI_BOX_SPACING
void dtgtk_cairo_paint_showmask(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)