Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
atrous.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-2014, 2016 johannes hanika.
5 Copyright (C) 2011 Antony Dovgal.
6 Copyright (C) 2011 Brian Teague.
7 Copyright (C) 2011 Edouard Gomez.
8 Copyright (C) 2011-2012 Henrik Andersson.
9 Copyright (C) 2011 Jochen Schroeder.
10 Copyright (C) 2011 Jérémy Rosen.
11 Copyright (C) 2011 Olivier Tribout.
12 Copyright (C) 2011-2014 Pascal de Bruijn.
13 Copyright (C) 2011 Robert Bieber.
14 Copyright (C) 2011 Rostyslav Pidgornyi.
15 Copyright (C) 2011-2014, 2016, 2019 Tobias Ellinghaus.
16 Copyright (C) 2011-2014, 2016-2017, 2019-2020 Ulrich Pegelow.
17 Copyright (C) 2012 Richard Wonka.
18 Copyright (C) 2013-2016 Roman Lebedev.
19 Copyright (C) 2013 Simon Spannagel.
20 Copyright (C) 2014 parafin.
21 Copyright (C) 2014 Robert William Hutton.
22 Copyright (C) 2015 Pedro Côrte-Real.
23 Copyright (C) 2016 Asma.
24 Copyright (C) 2017-2018, 2021 Dan Torop.
25 Copyright (C) 2017-2018 Heiko Bauke.
26 Copyright (C) 2018-2020, 2022-2023, 2025-2026 Aurélien PIERRE.
27 Copyright (C) 2018 Edgardo Hoszowski.
28 Copyright (C) 2018 Maurizio Paglia.
29 Copyright (C) 2018, 2020-2022 Pascal Obry.
30 Copyright (C) 2018 rawfiner.
31 Copyright (C) 2019 Andreas Schneider.
32 Copyright (C) 2019-2022 Diederik Ter Rahe.
33 Copyright (C) 2019 emeikei.
34 Copyright (C) 2020 Aldric Renaudin.
35 Copyright (C) 2020-2021 Hubert Kowalski.
36 Copyright (C) 2020-2021 Ralf Brown.
37 Copyright (C) 2021 Chris Elston.
38 Copyright (C) 2021 Martin Straeten.
39 Copyright (C) 2021 Sakari Kapanen.
40 Copyright (C) 2022 Hanno Schwalm.
41 Copyright (C) 2022 Martin Bařinka.
42 Copyright (C) 2022 Philipp Lutz.
43 Copyright (C) 2022 Sebatian Glasl.
44 Copyright (C) 2026 Guillaume Stutin.
45
46 darktable is free software: you can redistribute it and/or modify
47 it under the terms of the GNU General Public License as published by
48 the Free Software Foundation, either version 3 of the License, or
49 (at your option) any later version.
50
51 darktable is distributed in the hope that it will be useful,
52 but WITHOUT ANY WARRANTY; without even the implied warranty of
53 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
54 GNU General Public License for more details.
55
56 You should have received a copy of the GNU General Public License
57 along with darktable. If not, see <http://www.gnu.org/licenses/>.
58*/
59
60#include "system/macros.h"
62#include "system/openmp.h"
64#include "system/mem_alloc.h"
65#include "system/simd.h"
66#include "common/logging.h"
68#include "database/database.h"
70#include "widgets/bauhaus.h"
71#include "pixel/eaw.h"
72#include "common/imagebuf.h"
73#include "common/opencl.h"
74#include "common/conf.h"
75#include "develop/imageop.h"
76#include "develop/imageop_gui.h"
78#include "develop/tiling.h"
79
80#include "widgets/draw.h"
81#include "gui/presets.h"
82#include "iop/iop_api.h"
83
84#include <math.h>
85#include <stdlib.h>
86#include "widgets/notebook.h"
87#include "widgets/scroll_wrap.h"
88#include "gui/screen_metrics.h"
89//#define USE_NEW_CL //uncomment to use the new, more memory-efficient OpenCL code (not yet finished)
90
91#define INSET DT_PIXEL_APPLY_DPI(5)
92#define INFL .3f
93
94
96
97#define BANDS 6
98#define MAX_NUM_SCALES 8 // 2*2^(i+1) + 1 = 1025px support for i = 8
99#define RES 64
100
101#define dt_atrous_show_upper_label(cr, text, layout, ink) \
102 pango_layout_set_text(layout, text, -1); \
103 pango_layout_get_pixel_extents(layout, &ink, NULL); \
104 cairo_move_to(cr, .5 * (width - ink.width), (.08 * height) - ink.height); \
105 pango_cairo_show_layout(cr, layout);
106
107
108#define dt_atrous_show_lower_label(cr, text, layout, ink) \
109 pango_layout_set_text(layout, text, -1); \
110 pango_layout_get_pixel_extents(layout, &ink, NULL); \
111 cairo_move_to(cr, .5 * (width - ink.width), (.98 * height) - ink.height); \
112 pango_cairo_show_layout(cr, layout);
113
114
116{
117 atrous_L = 0, // luminance boost
118 atrous_c = 1, // chrominance boost
119 atrous_s = 2, // edge sharpness
120 atrous_Lt = 3, // luminance noise threshold
121 atrous_ct = 4, // chrominance noise threshold
122 atrous_none = 5
124
126{
127 int32_t octaves; // $DEFAULT: 3
129 float y[atrous_none][BANDS]; // $DEFAULT: 0.5
130 float mix; // $DEFAULT: 1.0 $MIN: -2.0 $MAX: 2.0
132
154
162
164{
165 // demosaic pattern
166 int32_t octaves;
169
170
171const char *name()
172{
173 return _("contrast equalizer");
174}
175
176const char *aliases()
177{
178 return _("sharpness|acutance|local contrast");
179}
180
181const char **description(struct dt_iop_module_t *self)
182{
183 return dt_iop_set_description(self, _("add or remove local contrast, sharpness, acutance"),
184 _("corrective and creative"),
185 _("linear, Lab, scene-referred"),
186 _("frequential, RGB"),
187 _("linear, Lab, scene-referred"));
188}
189
191{
192 return IOP_GROUP_SHARPNESS;
193}
194
199
204
205int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version,
206 void *new_params, const int new_version)
207{
208 if(old_version == 1 && new_version == 2)
209 {
210 typedef struct dt_iop_atrous_params_v1_t
211 {
212 int32_t octaves; // $DEFAULT: 3
213 float x[atrous_none][BANDS];
214 float y[atrous_none][BANDS]; // $DEFAULT: 0.5
215 } dt_iop_atrous_params_v1_t;
216
217 dt_iop_atrous_params_v1_t *o = (dt_iop_atrous_params_v1_t *)old_params;
220
221 *n = *d; // start with a fresh copy of default parameters
222
223 memcpy(n, o, sizeof(dt_iop_atrous_params_v1_t));
224 n->mix = 1.0f;
225 return 0;
226 }
227
228 return 1;
229}
230
232static int get_samples(float *t, const dt_iop_atrous_data_t *const d, const dt_iop_roi_t *roi_in,
233 const dt_dev_pixelpipe_iop_t *const piece)
234{
235 const float scale = roi_in->scale;
236 const float supp0
237 = MIN(2 * (2 << (MAX_NUM_SCALES - 1)) + 1, MAX(piece->buf_in.height, piece->buf_in.width) * 0.2f);
238 const float i0 = dt_log2f((supp0 - 1.0f) * .5f);
239 int i = 0;
240 for(; i < MAX_NUM_SCALES; i++)
241 {
242 // actual filter support on scaled buffer
243 const float supp = 2 * (2 << i) + 1;
244 // approximates this filter size on unscaled input image:
245 const float supp_in = supp * (1.0f / scale);
246 const float i_in = dt_log2f((supp_in - 1) * .5f) - 1.0f;
247 t[i] = 1.0f - (i_in + .5f) / i0;
248 if(t[i] < 0.0f) break;
249 }
250 return i;
251}
252
254static int get_scales(float (*thrs)[4], float (*boost)[4], float *sharp, const dt_iop_atrous_data_t *const d,
255 const dt_iop_roi_t *roi_in, const dt_dev_pixelpipe_iop_t *const piece)
256{
257 // we want coeffs to span max 20% of the image
258 // finest is 5x5 filter
259 //
260 // 1:1 : w=20% buf_in.width w=5x5
261 // : ^ ... .... .... ^
262 // buf : 17x17 9x9 5x5 2*2^k+1
263 // .....
264 // . . . . .
265 // . . . . .
266 // cut off too fine ones, if image is not detailed enough (due to roi_in->scale)
267 const float scale = roi_in->scale;
268 // largest desired filter on input buffer (20% of input dim)
269 const float supp0
270 = MIN(2 * (2 << (MAX_NUM_SCALES - 1)) + 1,
271 MAX(piece->buf_in.height, piece->buf_in.width) * 0.2f);
272 const float i0 = dt_log2f((supp0 - 1.0f) * .5f);
273 int i = 0;
274 for(; i < MAX_NUM_SCALES; i++)
275 {
276 // actual filter support on scaled buffer
277 const float supp = 2 * (2 << i) + 1;
278 // approximates this filter size on unscaled input image:
279 const float supp_in = supp * (1.0f / scale);
280 const float i_in = dt_log2f((supp_in - 1) * .5f) - 1.0f;
281 // i_in = max_scale .. .. .. 0
282 const float t = 1.0f - (i_in + .5f) / i0;
283 boost[i][3] = boost[i][0] = 2.0f * dt_draw_curve_calc_value(d->curve[atrous_L], t);
284 boost[i][1] = boost[i][2] = 2.0f * dt_draw_curve_calc_value(d->curve[atrous_c], t);
285 for(int k = 0; k < 4; k++) boost[i][k] *= boost[i][k];
286 thrs[i][0] = thrs[i][3] = powf(2.0f, -7.0f * (1.0f - t)) * 10.0f
288 thrs[i][1] = thrs[i][2] = powf(2.0f, -7.0f * (1.0f - t)) * 20.0f
290 sharp[i] = 0.0025f * dt_draw_curve_calc_value(d->curve[atrous_s], t);
291 // printf("scale %d boost %f %f thrs %f %f sharpen %f\n", i, boost[i][0], boost[i][2], thrs[i][0],
292 // thrs[i][1], sharp[i]);
293 if(t < 0.0f) break;
294 }
295 // ensure that return value max_scale is such that
296 // 2 * 2 *(1 << max_scale) <= min(width, height)
297 const int max_scale_roi = (int)floorf(dt_log2f((float)MIN(roi_in->width, roi_in->height))) - 2;
298 return MIN(max_scale_roi, i);
299}
300
301/* just process the supplied image buffer, upstream default_process_tiling() does the rest */
303static int process_wavelets(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe,
304 const struct dt_dev_pixelpipe_iop_t *piece, const void *const i, void *const o,
305 const dt_iop_roi_t *const roi_in,
306 const dt_iop_roi_t *const roi_out, const eaw_decompose_t decompose,
307 const eaw_synthesize_t synthesize)
308{
312 float sharp[MAX_NUM_SCALES];
313 const int max_scale = get_scales(thrs, boost, sharp, d, roi_in, piece);
314
315 const int width = roi_out->width;
316 const int height = roi_out->height;
317
318 // get_scales() returns MIN(floor(log2(min(w,h))) - 2, i), which is <= 0 once the ROI's
319 // smaller side is 4 px or under, and negative below that. `1u << (max_scale - 1)` is
320 // then a shift by a negative count: undefined behaviour. Such a ROI is below the
321 // wavelet's minimum size anyway, which is exactly what the guard further down bails
322 // on, so treat it as the same corner case instead of computing the shift at all.
323 if(max_scale <= 0)
324 {
326 return 0;
327 }
328
329 const int max_mult = 1u << (max_scale - 1);
330
331 if(self->dev->gui_attached && !dt_dev_pixelpipe_has_preview_output(self->dev, pipe, roi_out))
332 {
334 if(!IS_NULL_PTR(g)) g->num_samples = get_samples(g->sample, d, roi_in, piece);
335 // tries to acquire gdk lock and this prone to deadlock:
336 // dt_control_queue_draw(GTK_WIDGET(g->area));
337 }
338
339 // corner case of extremely small image. this is not really likely to happen but would
340 // lead to out of bounds memory access
341 if(width < 2 * max_mult || height < 2 * max_mult)
342 {
344 return 0;
345 }
346
347 float *const restrict out = (float*)o;
348 float *restrict detail = NULL;
349 float *restrict tmp = NULL;
350 float *restrict tmp2 = NULL;
351
352 if (dt_iop_alloc_image_buffers(self, roi_in, roi_out, 4, &tmp, 4, &tmp2, 4, &detail, 0))
353 {
354 return 1;
355 }
356
357 float *buf1 = (float *)i;
358 float *buf2 = tmp;
359
360 // clear the output buffer, which will be accumulating all of the detail scales
361 memset(out, 0, sizeof(float) * 4 * width * height);
362
363 // now do the wavelet decomposition, immediately synthesizing the detail scale into the final output so
364 // that we don't need to store it past the current scale's iteration
365 for(int scale = 0; scale < max_scale; scale++)
366 {
367 // The pipe abandoned this run: stop between two scales, each a full-image edge-aware pass.
369 {
373 return 1;
374 }
375 decompose(buf2, buf1, detail, scale, sharp[scale], width, height);
376 synthesize(out, out, detail, thrs[scale], boost[scale], width, height);
377 if(scale == 0) buf1 = (float *)tmp2; // now switch to second scratch for buffer ping-pong between buf1 and buf2
378 float *buf3 = buf2;
379 buf2 = buf1;
380 buf1 = buf3;
381 }
382
383 // add in the final residue
384 __OMP_SIMD__(aligned(buf1, out : 64))
385 for (size_t k = 0; k < (size_t)4 * width * height; k++)
386 out[k] += buf1[k];
387
389 dt_iop_alpha_copy(i, o, width, height);
390
394 return 0;
395}
396
397int process(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe,
398 const struct dt_dev_pixelpipe_iop_t *piece, const void *const i, void *const o)
399{
400 const dt_iop_roi_t *const roi_in = &piece->roi_in;
401 const dt_iop_roi_t *const roi_out = &piece->roi_out;
402 return process_wavelets(self, pipe, piece, i, o, roi_in, roi_out, eaw_decompose, eaw_synthesize);
403}
404
405#ifdef HAVE_OPENCL
406
407#ifdef USE_NEW_CL
408/* this version is adapted to the new global tiling mechanism. it no longer does tiling by itself. */
409int 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)
410{
411 const dt_iop_roi_t *const roi_in = &piece->roi_in;
412 const dt_iop_roi_t *const roi_out = &piece->roi_out;
416 float sharp[MAX_NUM_SCALES];
417 const int max_scale = get_scales(thrs, boost, sharp, d, roi_in, piece);
418
419 if(self->dev->gui_attached && !dt_dev_pixelpipe_has_preview_output(self->dev, pipe, roi_out))
420 {
422 if(!IS_NULL_PTR(g)) g->num_samples = get_samples(g->sample, d, roi_in, piece);
423 // dt_control_queue_redraw_widget(GTK_WIDGET(g->area));
424 // tries to acquire gdk lock and this prone to deadlock:
425 // dt_control_queue_draw(GTK_WIDGET(g->area));
426 }
427
429
430 const int devid = pipe->devid;
431 cl_int err = -999;
432 cl_mem dev_filter = NULL;
433 cl_mem dev_tmp = NULL;
434 cl_mem dev_tmp2 = NULL;
435 cl_mem dev_detail = NULL;
436
437 float m[] = { 0.0625f, 0.25f, 0.375f, 0.25f, 0.0625f }; // 1/16, 4/16, 6/16, 4/16, 1/16
438 float mm[5][5];
439 for(int j = 0; j < 5; j++)
440 for(int i = 0; i < 5; i++) mm[j][i] = m[i] * m[j];
441
442 dev_filter = dt_opencl_copy_host_to_device_constant(devid, sizeof(float) * 25, mm);
443 if(IS_NULL_PTR(dev_filter)) goto error;
444
445 /* allocate space for two temporary buffer to participate_in in the buffer ping-pong below. We need dev_out
446 to accumulate the result and dev_in needs to stay unchanged for blendops */
447 dev_tmp = dt_opencl_alloc_device(devid, roi_out->width, roi_out->height, sizeof(float) * 4);
448 if(IS_NULL_PTR(dev_tmp)) goto error;
449 dev_tmp2 = dt_opencl_alloc_device(devid, roi_out->width, roi_out->height, sizeof(float) * 4);
450 if(IS_NULL_PTR(dev_tmp2)) goto error;
451
452 /* allocate a buffer for storing the detail information. */
453 dev_detail = dt_opencl_alloc_device(devid, roi_out->width, roi_out->height, sizeof(float) * 4);
454 if(IS_NULL_PTR(dev_detail)) goto error;
455
456 const int width = roi_out->width;
457 const int height = roi_out->height;
458 size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
459
460 // clear dev_out to zeros, as we will be incrementally accumulating results there
461 dt_opencl_set_kernel_arg(devid, gd->kernel_zero, 0, sizeof(cl_mem), (void *)&dev_out);
462 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_zero, sizes);
463 if(err != CL_SUCCESS) goto error;
464
465 // the buffers for the buffer ping-pong. We start with dev_in as the input half for the first
466 // scale, then switch to using dev_tmp and dev_tmp2 as the two scratch buffers
467 void* dev_buf1 = &dev_in;
468 void* dev_buf2 = &dev_tmp;
469
470 /* decompose image into detail scales and coarse (the latter is left in dev_tmp or dev_out) */
471 for(int s = 0; s < max_scale; s++)
472 {
473 const int scale = s;
474
475 // run the decomposition
476 dt_opencl_set_kernel_arg(devid, gd->kernel_decompose, 0, sizeof(cl_mem), (void *)&dev_buf2); //this scale's output
477 dt_opencl_set_kernel_arg(devid, gd->kernel_decompose, 1, sizeof(cl_mem), (void *)&dev_buf1); //this scale's input
478 dt_opencl_set_kernel_arg(devid, gd->kernel_decompose, 2, sizeof(cl_mem), (void *)&dev_detail);
479 dt_opencl_set_kernel_arg(devid, gd->kernel_decompose, 3, sizeof(int), (void *)&width);
480 dt_opencl_set_kernel_arg(devid, gd->kernel_decompose, 4, sizeof(int), (void *)&height);
481 dt_opencl_set_kernel_arg(devid, gd->kernel_decompose, 5, sizeof(unsigned int), (void *)&scale);
482 dt_opencl_set_kernel_arg(devid, gd->kernel_decompose, 6, sizeof(float), (void *)&sharp[s]);
483 dt_opencl_set_kernel_arg(devid, gd->kernel_decompose, 7, sizeof(cl_mem), (void *)&dev_filter);
484
485 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_decompose, sizes);
486 if(err != CL_SUCCESS) goto error;
487
488 // now immediately run the synthesis for the current scale, accumulating the details into dev_out
489 dt_opencl_set_kernel_arg(devid, gd->kernel_synthesize, 0, sizeof(cl_mem), (void *)&dev_out);
490 dt_opencl_set_kernel_arg(devid, gd->kernel_synthesize, 1, sizeof(cl_mem), (void *)&dev_out);
491 dt_opencl_set_kernel_arg(devid, gd->kernel_synthesize, 2, sizeof(cl_mem), (void *)&dev_detail);
492 dt_opencl_set_kernel_arg(devid, gd->kernel_synthesize, 3, sizeof(int), (void *)&width);
493 dt_opencl_set_kernel_arg(devid, gd->kernel_synthesize, 4, sizeof(int), (void *)&height);
494 dt_opencl_set_kernel_arg(devid, gd->kernel_synthesize, 5, sizeof(float), (void *)&thrs[scale][0]);
495 dt_opencl_set_kernel_arg(devid, gd->kernel_synthesize, 6, sizeof(float), (void *)&thrs[scale][1]);
496 dt_opencl_set_kernel_arg(devid, gd->kernel_synthesize, 7, sizeof(float), (void *)&thrs[scale][2]);
497 dt_opencl_set_kernel_arg(devid, gd->kernel_synthesize, 8, sizeof(float), (void *)&thrs[scale][3]);
498 dt_opencl_set_kernel_arg(devid, gd->kernel_synthesize, 9, sizeof(float), (void *)&boost[scale][0]);
499 dt_opencl_set_kernel_arg(devid, gd->kernel_synthesize, 10, sizeof(float), (void *)&boost[scale][1]);
500 dt_opencl_set_kernel_arg(devid, gd->kernel_synthesize, 11, sizeof(float), (void *)&boost[scale][2]);
501 dt_opencl_set_kernel_arg(devid, gd->kernel_synthesize, 12, sizeof(float), (void *)&boost[scale][3]);
502
503 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_synthesize, sizes);
504 if(err != CL_SUCCESS) goto error;
505
506 // swap scratch buffers
507 if (scale == 0) dev_buf1 = dev_tmp2;
508 void* tmp = dev_buf2;
509 dev_buf2 = dev_buf1;
510 dev_buf1 = tmp;
511 }
512
513 // add the residue (the coarse scale from the final decomposition) to the accumulated details
514 dt_opencl_set_kernel_arg(devid, gd->kernel_addbuffers, 0, sizeof(cl_mem), (void*)&dev_out);
515 dt_opencl_set_kernel_arg(devid, gd->kernel_addbuffers, 1, sizeof(cl_mem), (void*)&dev_buf1);
516
517 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_addbuffers, sizes);
518 if(err != CL_SUCCESS) goto error;
519
524 return TRUE;
525
526error:
531 dt_print(DT_DEBUG_OPENCL, "[opencl_atrous] couldn't enqueue kernel! %d\n", err);
532 return FALSE;
533}
534
535#else // ======== old, memory-hungry implementation ========================================================
536
537/* this version is adapted to the new global tiling mechanism. it no longer does tiling by itself. */
538int 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)
539{
540 const dt_iop_roi_t *const roi_in = &piece->roi_in;
541 const dt_iop_roi_t *const roi_out = &piece->roi_out;
545 float sharp[MAX_NUM_SCALES];
546 const int max_scale = get_scales(thrs, boost, sharp, d, roi_in, piece);
547
548 if(self->dev->gui_attached && !dt_dev_pixelpipe_has_preview_output(self->dev, pipe, roi_out))
549 {
551 if(!IS_NULL_PTR(g)) g->num_samples = get_samples(g->sample, d, roi_in, piece);
552 // dt_control_queue_redraw_widget(GTK_WIDGET(g->area));
553 // tries to acquire gdk lock and this prone to deadlock:
554 // dt_control_queue_draw(GTK_WIDGET(g->area));
555 }
556
558
559 const int devid = pipe->devid;
560 cl_int err = -999;
561 cl_mem dev_filter = NULL;
562 cl_mem dev_tmp = NULL;
563 cl_mem *dev_detail = calloc(max_scale, sizeof(cl_mem));
564
565 float m[] = { 0.0625f, 0.25f, 0.375f, 0.25f, 0.0625f }; // 1/16, 4/16, 6/16, 4/16, 1/16
566 float mm[5][5];
567 for(int j = 0; j < 5; j++)
568 for(int i = 0; i < 5; i++)
569 mm[j][i] = m[i] * m[j];
570
571 dev_filter = dt_opencl_copy_host_to_device_constant(devid, sizeof(float) * 25, mm);
572 if(IS_NULL_PTR(dev_filter)) goto error;
573
574 /* allocate space for a temporary buffer. we don't want to use dev_in in the buffer ping-pong below, as we
575 need to keep it for blendops */
576 dev_tmp = dt_opencl_alloc_device(devid, roi_out->width, roi_out->height, sizeof(float) * 4);
577 if(IS_NULL_PTR(dev_tmp)) goto error;
578
579 /* allocate space to store detail information. Requires a number of additional buffers, each with full image
580 * size */
581 for(int k = 0; k < max_scale; k++)
582 {
583 dev_detail[k] = dt_opencl_alloc_device(devid, roi_out->width, roi_out->height, sizeof(float) * 4);
584 if(dev_detail[k] == NULL) goto error;
585 }
586
587 const int width = roi_out->width;
588 const int height = roi_out->height;
589 size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
590 size_t origin[] = { 0, 0, 0 };
591 size_t region[] = { width, height, 1 };
592
593 // copy original input from dev_in -> dev_out as starting point
594 err = dt_opencl_enqueue_copy_image(devid, dev_in, dev_out, origin, origin, region);
595 if(err != CL_SUCCESS) goto error;
596
597 /* decompose image into detail scales and coarse (the latter is left in dev_tmp or dev_out) */
598 for(int s = 0; s < max_scale; s++)
599 {
600 const int scale = s;
601
602 if(s & 1)
603 {
604 dt_opencl_set_kernel_arg(devid, gd->kernel_decompose, 0, sizeof(cl_mem), (void *)&dev_tmp);
605 dt_opencl_set_kernel_arg(devid, gd->kernel_decompose, 1, sizeof(cl_mem), (void *)&dev_out);
606 }
607 else
608 {
609 dt_opencl_set_kernel_arg(devid, gd->kernel_decompose, 0, sizeof(cl_mem), (void *)&dev_out);
610 dt_opencl_set_kernel_arg(devid, gd->kernel_decompose, 1, sizeof(cl_mem), (void *)&dev_tmp);
611 }
612 dt_opencl_set_kernel_arg(devid, gd->kernel_decompose, 2, sizeof(cl_mem), (void *)&dev_detail[s]);
613 dt_opencl_set_kernel_arg(devid, gd->kernel_decompose, 3, sizeof(int), (void *)&width);
614 dt_opencl_set_kernel_arg(devid, gd->kernel_decompose, 4, sizeof(int), (void *)&height);
615 dt_opencl_set_kernel_arg(devid, gd->kernel_decompose, 5, sizeof(unsigned int), (void *)&scale);
616 dt_opencl_set_kernel_arg(devid, gd->kernel_decompose, 6, sizeof(float), (void *)&sharp[s]);
617 dt_opencl_set_kernel_arg(devid, gd->kernel_decompose, 7, sizeof(cl_mem), (void *)&dev_filter);
618
619 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_decompose, sizes);
620 if(err != CL_SUCCESS) goto error;
621 }
622
623 /* now synthesize again */
624 for(int scale = max_scale - 1; scale >= 0; scale--)
625 {
626 if(scale & 1)
627 {
628 dt_opencl_set_kernel_arg(devid, gd->kernel_synthesize, 0, sizeof(cl_mem), (void *)&dev_tmp);
629 dt_opencl_set_kernel_arg(devid, gd->kernel_synthesize, 1, sizeof(cl_mem), (void *)&dev_out);
630 }
631 else
632 {
633 dt_opencl_set_kernel_arg(devid, gd->kernel_synthesize, 0, sizeof(cl_mem), (void *)&dev_out);
634 dt_opencl_set_kernel_arg(devid, gd->kernel_synthesize, 1, sizeof(cl_mem), (void *)&dev_tmp);
635 }
636
637 dt_opencl_set_kernel_arg(devid, gd->kernel_synthesize, 2, sizeof(cl_mem), (void *)&dev_detail[scale]);
638 dt_opencl_set_kernel_arg(devid, gd->kernel_synthesize, 3, sizeof(int), (void *)&width);
639 dt_opencl_set_kernel_arg(devid, gd->kernel_synthesize, 4, sizeof(int), (void *)&height);
640 dt_opencl_set_kernel_arg(devid, gd->kernel_synthesize, 5, sizeof(float), (void *)&thrs[scale][0]);
641 dt_opencl_set_kernel_arg(devid, gd->kernel_synthesize, 6, sizeof(float), (void *)&thrs[scale][1]);
642 dt_opencl_set_kernel_arg(devid, gd->kernel_synthesize, 7, sizeof(float), (void *)&thrs[scale][2]);
643 dt_opencl_set_kernel_arg(devid, gd->kernel_synthesize, 8, sizeof(float), (void *)&thrs[scale][3]);
644 dt_opencl_set_kernel_arg(devid, gd->kernel_synthesize, 9, sizeof(float), (void *)&boost[scale][0]);
645 dt_opencl_set_kernel_arg(devid, gd->kernel_synthesize, 10, sizeof(float), (void *)&boost[scale][1]);
646 dt_opencl_set_kernel_arg(devid, gd->kernel_synthesize, 11, sizeof(float), (void *)&boost[scale][2]);
647 dt_opencl_set_kernel_arg(devid, gd->kernel_synthesize, 12, sizeof(float), (void *)&boost[scale][3]);
648
649 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_synthesize, sizes);
650 if(err != CL_SUCCESS) goto error;
651 }
652
655 for(int k = 0; k < max_scale; k++)
656 dt_opencl_release_mem_object(dev_detail[k]);
657 dt_free(dev_detail);
658 return TRUE;
659
660error:
663 for(int k = 0; k < max_scale; k++)
664 dt_opencl_release_mem_object(dev_detail[k]);
665 dt_free(dev_detail);
666 dt_print(DT_DEBUG_OPENCL, "[opencl_atrous] couldn't enqueue kernel! %d\n", err);
667 return FALSE;
668}
669#endif // USE_NEW_CL
670
671#endif // HAVE_OPENCL
672
674{
675 const dt_iop_roi_t *const roi_in = &piece->roi_in;
679 float sharp[MAX_NUM_SCALES];
680 const int max_scale = get_scales(thrs, boost, sharp, d, roi_in, piece);
681 const int max_filter_radius = 2 * (1 << max_scale); // 2 * 2^max_scale
682
683 tiling->factor = 5.0f; // in + out + 2*tmp + details
684 tiling->factor_cl = 3.0f + max_scale; // in + out + tmp + scale buffers
685 tiling->maxbuf = 1.0f;
686 tiling->maxbuf_cl = 1.0f;
687 tiling->overhead = 0;
688 tiling->overlap = max_filter_radius;
689 tiling->xalign = 1;
690 tiling->yalign = 1;
691 return;
692}
693
695{
696 dt_iop_default_init(module);
697
698 dt_iop_atrous_params_t *d = module->default_params;
699
700 for(int k = 0; k < BANDS; k++)
701 {
702 d->y[atrous_Lt][k] = d->y[atrous_ct][k] = 0.0f;
703 for(int c = atrous_L; c <= atrous_ct; c++)
704 d->x[c][k] = k / (BANDS - 1.0f);
705 }
706}
707
709{
710 const int program = 1; // from programs.conf
713 module->data = gd;
714 gd->kernel_decompose = dt_opencl_create_kernel(program, "eaw_decompose");
715 gd->kernel_synthesize = dt_opencl_create_kernel(program, "eaw_synthesize");
716#ifdef USE_NEW_CL
717 gd->kernel_zero = dt_opencl_create_kernel(program, "eaw_zero");
718 gd->kernel_addbuffers = dt_opencl_create_kernel(program, "eaw_addbuffers");
719#endif
720}
721
733
734static inline void _apply_mix(dt_iop_module_t *self,
735 const int ch, const int k,
736 const float mix,
737 const float px, const float py, float *x, float *y)
738{
740 *x = fminf(1.0f, fmaxf(0.0f, px + (mix - 1.0f) * (px - dp->x[ch][k])));
741 *y = fminf(1.0f, fmaxf(0.0f, py + (mix - 1.0f) * (py - dp->y[ch][k])));
742}
743
746{
749
750#if 0
751 printf("---------- atrous preset begin\n");
752 printf("p.octaves = %d; p.mix = %.2f\n", p->octaves, p->mix);
753 for(int ch=0; ch<atrous_none; ch++) for(int k=0; k<BANDS; k++)
754 {
755 printf("p.x[%d][%d] = %f;\n", ch, k, p->x[ch][k]);
756 printf("p.y[%d][%d] = %f;\n", ch, k, p->y[ch][k]);
757 }
758 printf("---------- atrous preset end\n");
759#endif
760 d->octaves = p->octaves;
761 for(int ch = 0; ch < atrous_none; ch++)
762 for(int k = 0; k < BANDS; k++)
763 {
764 float x, y;
765 _apply_mix(self, ch, k, p->mix, p->x[ch][k], p->y[ch][k], &x, &y);
766 dt_draw_curve_set_point(d->curve[ch], k, x, y);
767 }
768 if(pipe)
769 {
770 int l = 0;
771 for(int k = (int)MIN(pipe->iwidth, pipe->iheight); k; k >>= 1) l++;
772 d->octaves = MIN(BANDS, l);
773 }
774
775 piece->cache_output_on_ram = TRUE;
776}
777
779{
782 piece->data = (void *)d;
783 piece->data_size = sizeof(dt_iop_atrous_data_t);
784 // Checked AFTER both piece->data and piece->data_size are set, deliberately.
785 // dt_iop_init_pipe() disables the node for us when it sees data_size > 0 with a NULL
786 // data -- returning before data_size is assigned skips that and leaves the node enabled
787 // with no storage. dt_calloc_align() returns NULL on failure and pipe nodes are built
788 // exactly when memory is tightest (Sentry 134134395 faulted in atrous's init_pipe).
789 if(IS_NULL_PTR(piece->data)) return;
790 for(int ch = 0; ch < atrous_none; ch++)
791 {
792 d->curve[ch] = dt_draw_curve_new(0.0, 1.0, CATMULL_ROM);
793 for(int k = 0; k < BANDS; k++)
794 (void)dt_draw_curve_add_point(d->curve[ch], default_params->x[ch][k], default_params->y[ch][k]);
795 }
796 if(pipe)
797 {
798 int l = 0;
799 for(int k = (int)MIN(pipe->iwidth, pipe->iheight); k; k >>= 1) l++;
800 d->octaves = MIN(BANDS, l);
801 }
802 else
803 {
804 d->octaves = BANDS;
805 }
806}
807
809{
810 /* init_pipe() may have failed to allocate, and cleanup runs regardless. */
811 if(IS_NULL_PTR(piece->data)) return;
813 for(int ch = 0; ch < atrous_none; ch++)
814 dt_draw_curve_destroy(d->curve[ch]);
815 dt_free_align(piece->data);
816 piece->data = NULL;
817}
818
819#define GAUSS(x, sigma) expf( -(1.0f - x) * (1.0f - x) / (sigma * sigma)) / (2.0 * sigma * powf(M_PI, 0.5f))
820
822{
825 p.octaves = 7;
826 p.mix = 1.0f;
827
828 for(int k = 0; k < BANDS; k++)
829 {
830 p.x[atrous_L][k] = k / (BANDS - 1.0);
831 p.x[atrous_c][k] = k / (BANDS - 1.0);
832 p.x[atrous_s][k] = k / (BANDS - 1.0);
833 p.y[atrous_L][k] = fmaxf(.5f, .75f - .5f * k / (BANDS - 1.0));
834 p.y[atrous_c][k] = fmaxf(.5f, .55f - .5f * k / (BANDS - 1.0));
835 p.y[atrous_s][k] = fminf(.5f, .2f + .35f * k / (BANDS - 1.0));
836 p.x[atrous_Lt][k] = k / (BANDS - 1.0);
837 p.x[atrous_ct][k] = k / (BANDS - 1.0);
838 p.y[atrous_Lt][k] = 0.0f;
839 p.y[atrous_ct][k] = 0.0f;
840 }
841 dt_gui_presets_add_generic(C_("eq_preset", "coarse"), self->op,
842 self->version(), &p, sizeof(p), 1);
843 for(int k = 0; k < BANDS; k++)
844 {
845 p.x[atrous_L][k] = k / (BANDS - 1.0);
846 p.x[atrous_c][k] = k / (BANDS - 1.0);
847 p.x[atrous_s][k] = k / (BANDS - 1.0);
848 p.y[atrous_L][k] = .5f + .25f * k / (float)BANDS;
849 p.y[atrous_c][k] = .5f;
850 p.y[atrous_s][k] = .5f;
851 p.x[atrous_Lt][k] = k / (BANDS - 1.0);
852 p.x[atrous_ct][k] = k / (BANDS - 1.0);
853 p.y[atrous_Lt][k] = .2f * k / (float)BANDS;
854 p.y[atrous_ct][k] = .3f * k / (float)BANDS;
855 }
856 dt_gui_presets_add_generic(_("denoise & sharpen"), self->op,
857 self->version(), &p, sizeof(p), 1);
858 for(int k = 0; k < BANDS; k++)
859 {
860 p.x[atrous_L][k] = k / (BANDS - 1.0);
861 p.x[atrous_c][k] = k / (BANDS - 1.0);
862 p.x[atrous_s][k] = k / (BANDS - 1.0);
863 p.y[atrous_L][k] = .5f + .25f * k / (float)BANDS;
864 p.y[atrous_c][k] = .5f;
865 p.y[atrous_s][k] = .5f;
866 p.x[atrous_Lt][k] = k / (BANDS - 1.0);
867 p.x[atrous_ct][k] = k / (BANDS - 1.0);
868 p.y[atrous_Lt][k] = 0.0f;
869 p.y[atrous_ct][k] = 0.0f;
870 }
871 dt_gui_presets_add_generic(C_("atrous", "sharpen"), self->op,
872 self->version(), &p, sizeof(p), 1);
873 for(int k = 0; k < BANDS; k++)
874 {
875 p.x[atrous_L][k] = k / (BANDS - 1.0);
876 p.x[atrous_c][k] = k / (BANDS - 1.0);
877 p.x[atrous_s][k] = k / (BANDS - 1.0);
878 p.y[atrous_L][k] = .5f;
879 p.y[atrous_c][k] = .5f;
880 p.y[atrous_s][k] = .0f;
881 p.x[atrous_Lt][k] = k / (BANDS - 1.0);
882 p.x[atrous_ct][k] = k / (BANDS - 1.0);
883 p.y[atrous_Lt][k] = .0f;
884 p.y[atrous_ct][k] = fmaxf(0.0f, (.60f * k / (float)BANDS) - 0.30f);
885 }
886 dt_gui_presets_add_generic(_("denoise chroma"), self->op,
887 self->version(), &p, sizeof(p), 1);
888 for(int k = 0; k < BANDS; k++)
889 {
890 p.x[atrous_L][k] = k / (BANDS - 1.0);
891 p.x[atrous_c][k] = k / (BANDS - 1.0);
892 p.x[atrous_s][k] = k / (BANDS - 1.0);
893 p.y[atrous_L][k] = .5f; //-.2f*k/(float)BANDS;
894 p.y[atrous_c][k] = .5f; // fmaxf(0.0f, .5f-.3f*k/(float)BANDS);
895 p.y[atrous_s][k] = .5f;
896 p.x[atrous_Lt][k] = k / (BANDS - 1.0);
897 p.x[atrous_ct][k] = k / (BANDS - 1.0);
898 p.y[atrous_Lt][k] = .2f * k / (float)BANDS;
899 p.y[atrous_ct][k] = .3f * k / (float)BANDS;
900 }
901 dt_gui_presets_add_generic(_("denoise"), self->op,
902 self->version(), &p, sizeof(p), 1);
903 for(int k = 0; k < BANDS; k++)
904 {
905 p.x[atrous_L][k] = k / (BANDS - 1.0);
906 p.x[atrous_c][k] = k / (BANDS - 1.0);
907 p.x[atrous_s][k] = k / (BANDS - 1.0);
908 p.y[atrous_L][k] = fminf(.5f, .3f + .35f * k / (BANDS - 1.0));
909 p.y[atrous_c][k] = .5f;
910 p.y[atrous_s][k] = .0f;
911 p.x[atrous_Lt][k] = k / (BANDS - 1.0);
912 p.x[atrous_ct][k] = k / (BANDS - 1.0);
913 p.y[atrous_Lt][k] = 0.0f;
914 p.y[atrous_ct][k] = 0.0f;
915 }
916 p.y[atrous_L][0] = .5f;
917 dt_gui_presets_add_generic(_("bloom"), self->op,
918 self->version(), &p, sizeof(p), 1);
919 for(int k = 0; k < BANDS; k++)
920 {
921 p.x[atrous_L][k] = k / (BANDS - 1.0);
922 p.x[atrous_c][k] = k / (BANDS - 1.0);
923 p.x[atrous_s][k] = k / (BANDS - 1.0);
924 p.y[atrous_L][k] = 0.6f;
925 p.y[atrous_c][k] = .55f;
926 p.y[atrous_s][k] = .0f;
927 p.x[atrous_Lt][k] = k / (BANDS - 1.0);
928 p.x[atrous_ct][k] = k / (BANDS - 1.0);
929 p.y[atrous_Lt][k] = 0.0f;
930 p.y[atrous_ct][k] = 0.0f;
931 }
932 dt_gui_presets_add_generic(_("clarity"), self->op,
933 self->version(), &p, sizeof(p), 1);
934
935 float sigma = 3.f / (float)(BANDS - 1);
936
937 for(int k = 0; k < BANDS; k++)
938 {
939 const float x = k / (float)(BANDS - 1);
940 const float fine = GAUSS(x, 0.5 * sigma);
941 const float medium = GAUSS(x, sigma);
942 const float coarse = GAUSS(x, 2 * sigma);
943 const float coeff = 0.5f + (coarse + medium + fine) / 16.0f;
944 const float noise = (coarse + medium + fine) / 128.f;
945
946 p.x[atrous_L][k] = p.x[atrous_c][k] = p.x[atrous_s][k] = x;
947 p.y[atrous_L][k] = p.y[atrous_s][k] = coeff;
948 p.y[atrous_c][k] = 0.5f;
949 p.x[atrous_Lt][k] = p.x[atrous_ct][k] = x;
950 p.y[atrous_Lt][k] = p.y[atrous_ct][k] = noise;
951 }
952 dt_gui_presets_add_generic(_("deblur: large blur, strength 3"), self->op,
953 self->version(), &p, sizeof(p), 1);
954
955 for(int k = 0; k < BANDS; k++)
956 {
957 const float x = k / (float)(BANDS - 1);
958 const float fine = GAUSS(x, 0.5 * sigma);
959 const float medium = GAUSS(x, sigma);
960 const float coeff = 0.5f + (medium + fine) / 16.0f;
961 const float noise = (medium + fine) / 128.f;
962
963 p.x[atrous_L][k] = p.x[atrous_c][k] = p.x[atrous_s][k] = x;
964 p.y[atrous_L][k] = p.y[atrous_s][k] = coeff;
965 p.y[atrous_c][k] = 0.5f;
966 p.x[atrous_Lt][k] = p.x[atrous_ct][k] = x;
967 p.y[atrous_Lt][k] = p.y[atrous_ct][k] = noise;
968 }
969 dt_gui_presets_add_generic(_("deblur: medium blur, strength 3"), self->op,
970 self->version(), &p, sizeof(p), 1);
971
972 for(int k = 0; k < BANDS; k++)
973 {
974 const float x = k / (float)(BANDS - 1);
975 const float fine = GAUSS(x, 0.5 * sigma);
976 const float coeff = 0.5f + fine / 16.f;
977 const float noise = fine / 128.f;
978
979 p.x[atrous_L][k] = p.x[atrous_c][k] = p.x[atrous_s][k] = x;
980 p.y[atrous_L][k] = p.y[atrous_s][k] = coeff;
981 p.y[atrous_c][k] = 0.5f;
982 p.x[atrous_Lt][k] = p.x[atrous_ct][k] = x;
983 p.y[atrous_Lt][k] = p.y[atrous_ct][k] = noise;
984 }
985 dt_gui_presets_add_generic(_("deblur: fine blur, strength 3"), self->op,
986 self->version(), &p, sizeof(p), 1);
987
988 for(int k = 0; k < BANDS; k++)
989 {
990 const float x = k / (float)(BANDS - 1);
991 const float fine = GAUSS(x, 0.5 * sigma);
992 const float medium = GAUSS(x, sigma);
993 const float coarse = GAUSS(x, 2 * sigma);
994 const float coeff = 0.5f + (coarse + medium + fine) / 24.0f;
995 const float noise = (coarse + medium + fine) / 192.f;
996
997 p.x[atrous_L][k] = p.x[atrous_c][k] = p.x[atrous_s][k] = x;
998 p.y[atrous_L][k] = p.y[atrous_s][k] = coeff;
999 p.y[atrous_c][k] = 0.5f;
1000 p.x[atrous_Lt][k] = p.x[atrous_ct][k] = x;
1001 p.y[atrous_Lt][k] = p.y[atrous_ct][k] = noise;
1002 }
1003 dt_gui_presets_add_generic(_("deblur: large blur, strength 2"), self->op,
1004 self->version(), &p, sizeof(p), 1);
1005
1006 for(int k = 0; k < BANDS; k++)
1007 {
1008 const float x = k / (float)(BANDS - 1);
1009 const float fine = GAUSS(x, 0.5 * sigma);
1010 const float medium = GAUSS(x, sigma);
1011 const float coeff = 0.5f + (medium + fine) / 24.0f;
1012 const float noise = (medium + fine) / 192.f;
1013
1014 p.x[atrous_L][k] = p.x[atrous_c][k] = p.x[atrous_s][k] = x;
1015 p.y[atrous_L][k] = p.y[atrous_s][k] = coeff;
1016 p.y[atrous_c][k] = 0.5f;
1017 p.x[atrous_Lt][k] = p.x[atrous_ct][k] = x;
1018 p.y[atrous_Lt][k] = p.y[atrous_ct][k] = noise;
1019 }
1020 dt_gui_presets_add_generic(_("deblur: medium blur, strength 2"), self->op,
1021 self->version(), &p, sizeof(p), 1);
1022
1023 for(int k = 0; k < BANDS; k++)
1024 {
1025 const float x = k / (float)(BANDS - 1);
1026 const float fine = GAUSS(x, 0.5 * sigma);
1027 const float coeff = 0.5f + fine / 24.0f;
1028 const float noise = fine / 192.f;
1029
1030 p.x[atrous_L][k] = p.x[atrous_c][k] = p.x[atrous_s][k] = x;
1031 p.y[atrous_L][k] = p.y[atrous_s][k] = coeff;
1032 p.y[atrous_c][k] = 0.5f;
1033 p.x[atrous_Lt][k] = p.x[atrous_ct][k] = x;
1034 p.y[atrous_Lt][k] = p.y[atrous_ct][k] = noise;
1035 }
1036 dt_gui_presets_add_generic(_("deblur: fine blur, strength 2"), self->op,
1037 self->version(), &p, sizeof(p), 1);
1038
1039 for(int k = 0; k < BANDS; k++)
1040 {
1041 const float x = k / (float)(BANDS - 1);
1042 const float fine = GAUSS(x, 0.5 * sigma);
1043 const float medium = GAUSS(x, sigma);
1044 const float coarse = GAUSS(x, 2 * sigma);
1045 const float coeff = 0.5f + (coarse + medium + fine) / 32.0f;
1046 const float noise = (coarse + medium + fine) / 128.f;
1047
1048 p.x[atrous_L][k] = p.x[atrous_c][k] = p.x[atrous_s][k] = x;
1049 p.y[atrous_L][k] = p.y[atrous_s][k] = coeff;
1050 p.y[atrous_c][k] = 0.5f;
1051 p.x[atrous_Lt][k] = p.x[atrous_ct][k] = x;
1052 p.y[atrous_Lt][k] = p.y[atrous_ct][k] = noise;
1053 }
1054 dt_gui_presets_add_generic(_("deblur: large blur, strength 1"), self->op,
1055 self->version(), &p, sizeof(p), 1);
1056
1057 for(int k = 0; k < BANDS; k++)
1058 {
1059 const float x = k / (float)(BANDS - 1);
1060 const float fine = GAUSS(x, 0.5 * sigma);
1061 const float medium = GAUSS(x, sigma);
1062 const float coeff = 0.5f + (medium + fine) / 32.0f;
1063 const float noise = (medium + fine) / 128.f;
1064
1065 p.x[atrous_L][k] = p.x[atrous_c][k] = p.x[atrous_s][k] = x;
1066 p.y[atrous_L][k] = p.y[atrous_s][k] = coeff;
1067 p.y[atrous_c][k] = 0.5f;
1068 p.x[atrous_Lt][k] = p.x[atrous_ct][k] = x;
1069 p.y[atrous_Lt][k] = p.y[atrous_ct][k] = noise;
1070 }
1071 dt_gui_presets_add_generic(_("deblur: medium blur, strength 1"), self->op,
1072 self->version(), &p, sizeof(p), 1);
1073
1074 for(int k = 0; k < BANDS; k++)
1075 {
1076 const float x = k / (float)(BANDS - 1);
1077 const float fine = GAUSS(x, 0.5 * sigma);
1078 const float coeff = 0.5f + fine / 32.f;
1079 const float noise = fine / 128.f;
1080
1081 p.x[atrous_L][k] = p.x[atrous_c][k] = p.x[atrous_s][k] = x;
1082 p.y[atrous_L][k] = p.y[atrous_s][k] = coeff;
1083 p.y[atrous_c][k] = 0.5f;
1084 p.x[atrous_Lt][k] = p.x[atrous_ct][k] = x;
1085 p.y[atrous_Lt][k] = p.y[atrous_ct][k] = noise;
1086 }
1087 dt_gui_presets_add_generic(_("deblur: fine blur, strength 1"), self->op,
1088 self->version(), &p, sizeof(p), 1);
1089
1091}
1092
1093static void reset_mix(dt_iop_module_t *self)
1094{
1097 c->drag_params = *p;
1099 dt_bauhaus_slider_set(c->mix, p->mix);
1101}
1102
1103void gui_update(struct dt_iop_module_t *self)
1104{
1105 reset_mix(self);
1106 gtk_widget_queue_draw(self->gui->widget);
1107}
1108
1109
1110// gui stuff:
1111
1112static gboolean area_enter_notify(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
1113{
1114 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
1116 if(!c->dragging) c->mouse_y = fabs(c->mouse_y);
1117 c->in_curve = TRUE;
1118 gtk_widget_queue_draw(widget);
1119 return TRUE;
1120}
1121
1122static gboolean area_leave_notify(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
1123{
1124 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
1126 if(!c->dragging) c->mouse_y = -fabs(c->mouse_y);
1127 c->in_curve = FALSE;
1128 gtk_widget_queue_draw(widget);
1129 return TRUE;
1130}
1131
1132// fills in new parameters based on mouse position (in 0,1)
1133static void get_params(dt_iop_atrous_params_t *p, const int ch, const double mouse_x, const double mouse_y,
1134 const float rad)
1135{
1136 for(int k = 0; k < BANDS; k++)
1137 {
1138 const float f = expf(-(mouse_x - p->x[ch][k]) * (mouse_x - p->x[ch][k]) / (rad * rad));
1139 p->y[ch][k] = MAX(0.0f, MIN(1.0f, (1 - f) * p->y[ch][k] + f * mouse_y));
1140 }
1141}
1142
1143static gboolean area_draw(GtkWidget *widget, cairo_t *crf, gpointer user_data)
1144{
1145 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
1148
1149 const float mix = c->in_curve ? 1.0f : p.mix;
1150
1151 for(int k = 0; k < BANDS; k++)
1152 {
1153 const int ch2 = (int)c->channel2;
1154 float x, y;
1155 _apply_mix(self, ch2, k, mix, p.x[ch2][k], p.y[ch2][k], &x, &y);
1156 dt_draw_curve_set_point(c->minmax_curve, k, x, y);
1157 }
1158
1159 const int inset = INSET;
1160 GtkAllocation allocation;
1161 gtk_widget_get_allocation(widget, &allocation);
1162 int width = allocation.width, height = allocation.height;
1163 cairo_surface_t *cst = dt_cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
1164 cairo_t *cr = cairo_create(cst);
1165 // clear bg, match color of the notebook tabs:
1166 GdkRGBA bright_bg_color, graph_bg;
1167 GtkStyleContext *context = gtk_widget_get_style_context(self->gui->expander);
1168 gboolean color_found = gtk_style_context_lookup_color (context, "graph_overlay", &bright_bg_color);
1169 if(!color_found)
1170 {
1171 bright_bg_color.red = 1.0;
1172 bright_bg_color.green = 0.0;
1173 bright_bg_color.blue = 0.0;
1174 bright_bg_color.alpha = 1.0;
1175 }
1176
1177 color_found = gtk_style_context_lookup_color (context, "graph_bg", &graph_bg);
1178 if(!color_found)
1179 {
1180 graph_bg.red = 1.0;
1181 graph_bg.green = 0.0;
1182 graph_bg.blue = 0.0;
1183 graph_bg.alpha = 1.0;
1184 }
1185
1186 gdk_cairo_set_source_rgba(cr, &bright_bg_color);
1187 cairo_paint(cr);
1188
1189 cairo_translate(cr, inset, inset);
1190 width -= 2 * inset;
1191 height -= 2 * inset;
1192
1193 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.0));
1194 gdk_cairo_set_source_rgba(cr, &graph_bg);
1195 cairo_rectangle(cr, 0, 0, width, height);
1196 cairo_stroke(cr);
1197
1198 gdk_cairo_set_source_rgba(cr, &bright_bg_color);
1199 cairo_rectangle(cr, 0, 0, width, height);
1200 cairo_fill(cr);
1201
1202 if(c->mouse_y > 0 || c->dragging)
1203 {
1204 const int ch2 = (int)c->channel2;
1205
1206 // draw min/max curves:
1207 get_params(&p, ch2, c->mouse_x, 1., c->mouse_radius);
1208 for(int k = 0; k < BANDS; k++)
1209 dt_draw_curve_set_point(c->minmax_curve, k, p.x[ch2][k], p.y[ch2][k]);
1210 dt_draw_curve_calc_values(c->minmax_curve, 0.0, 1.0, RES, c->draw_min_xs, c->draw_min_ys);
1211
1212 p = *(dt_iop_atrous_params_t *)self->params;
1213 get_params(&p, ch2, c->mouse_x, .0, c->mouse_radius);
1214 for(int k = 0; k < BANDS; k++)
1215 dt_draw_curve_set_point(c->minmax_curve, k, p.x[ch2][k], p.y[ch2][k]);
1216 dt_draw_curve_calc_values(c->minmax_curve, 0.0, 1.0, RES, c->draw_max_xs, c->draw_max_ys);
1217 }
1218
1219 // draw grid
1220 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(.4));
1221 gdk_cairo_set_source_rgba(cr, &graph_bg);
1222 dt_draw_grid(cr, 8, 0, 0, width, height);
1223
1224 cairo_save(cr);
1225
1226 // draw selected cursor
1227 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.));
1228 cairo_translate(cr, 0, height);
1229
1230// draw frequency histogram in bg.
1231#if 1
1232 if(c->num_samples > 0)
1233 {
1234 cairo_save(cr);
1235 for(int k = 1; k < c->num_samples; k += 2)
1236 {
1237 cairo_set_source_rgba(cr, graph_bg.red, graph_bg.green, graph_bg.blue, .3);
1238 cairo_move_to(cr, width * c->sample[k - 1], 0.0f);
1239 cairo_line_to(cr, width * c->sample[k - 1], -height);
1240 cairo_line_to(cr, width * c->sample[k], -height);
1241 cairo_line_to(cr, width * c->sample[k], 0.0f);
1242 cairo_fill(cr);
1243 }
1244 if(c->num_samples & 1)
1245 {
1246 cairo_move_to(cr, width * c->sample[c->num_samples - 1], 0.0f);
1247 cairo_line_to(cr, width * c->sample[c->num_samples - 1], -height);
1248 cairo_line_to(cr, 0.0f, -height);
1249 cairo_line_to(cr, 0.0f, 0.0f);
1250 cairo_fill(cr);
1251 }
1252 cairo_restore(cr);
1253 }
1254 if(c->band_max > 0)
1255 {
1256 cairo_save(cr);
1257 cairo_scale(cr, width / (BANDS - 1.0), -(height - DT_PIXEL_APPLY_DPI(5)) / c->band_max);
1258 cairo_set_source_rgba(cr, graph_bg.red, graph_bg.green, graph_bg.blue, .3);
1259 cairo_move_to(cr, 0, 0);
1260 for(int k = 0; k < BANDS; k++) cairo_line_to(cr, k, c->band_hist[k]);
1261 cairo_line_to(cr, BANDS - 1.0, 0.);
1262 cairo_close_path(cr);
1263 cairo_fill(cr);
1264 cairo_restore(cr);
1265 }
1266#endif
1267
1268 // cairo_set_operator(cr, CAIRO_OPERATOR_ADD);
1269 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1270 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(2.));
1271 for(int i = 0; i <= atrous_s; i++)
1272 {
1273 // draw curves, selected last.
1274 int ch = ((int)c->channel + i + 1) % (atrous_s + 1);
1275 int ch2 = -1;
1276 const float bgmul = i < atrous_s ? 0.5f : 1.0f;
1277 switch(ch)
1278 {
1279 case atrous_L:
1280 cairo_set_source_rgba(cr, .6, .6, .6, .3 * bgmul);
1281 ch2 = atrous_Lt;
1282 break;
1283 case atrous_c:
1284 cairo_set_source_rgba(cr, .4, .2, .0, .4 * bgmul);
1285 ch2 = atrous_ct;
1286 break;
1287 default: // case atrous_s:
1288 cairo_set_source_rgba(cr, .1, .2, .3, .4 * bgmul);
1289 break;
1290 }
1291 p = *(dt_iop_atrous_params_t *)self->params;
1292
1293 // reverse order if bottom is active (to end up with correct values in minmax_curve):
1294 if(c->channel2 == ch2)
1295 {
1296 ch2 = ch;
1297 ch = c->channel2;
1298 }
1299
1300 if(ch2 >= 0)
1301 {
1302 for(int k = 0; k < BANDS; k++)
1303 {
1304 float x, y;
1305 _apply_mix(self, ch2, k, mix, p.x[ch2][k], p.y[ch2][k], &x, &y);
1306 dt_draw_curve_set_point(c->minmax_curve, k, x, y);
1307 }
1308 dt_draw_curve_calc_values(c->minmax_curve, 0.0, 1.0, RES, c->draw_xs, c->draw_ys);
1309 cairo_move_to(cr, width, -height * p.y[ch2][BANDS - 1]);
1310 for(int k = RES - 2; k >= 0; k--)
1311 cairo_line_to(cr, k * width / (float)(RES - 1), -height * c->draw_ys[k]);
1312 }
1313 else
1314 cairo_move_to(cr, 0, 0);
1315 for(int k = 0; k < BANDS; k++)
1316 {
1317 float x, y;
1318 _apply_mix(self, ch, k, mix, p.x[ch][k], p.y[ch][k], &x, &y);
1319 dt_draw_curve_set_point(c->minmax_curve, k, x, y);
1320 }
1321 dt_draw_curve_calc_values(c->minmax_curve, 0.0, 1.0, RES, c->draw_xs, c->draw_ys);
1322 for(int k = 0; k < RES; k++)
1323 cairo_line_to(cr, k * width / (float)(RES - 1), -height * c->draw_ys[k]);
1324 if(ch2 < 0)
1325 cairo_line_to(cr, width, 0);
1326 cairo_close_path(cr);
1327 cairo_stroke_preserve(cr);
1328 cairo_fill(cr);
1329 }
1330
1331 if(c->mouse_y > 0 || c->dragging)
1332 {
1333 const int ch = (int)c->channel;
1334 const int ch2 = (int)c->channel2;
1335
1336 // draw dots on knots
1337 cairo_save(cr);
1338 if(ch != ch2)
1339 cairo_set_source_rgb(cr, 0.1, 0.1, 0.1);
1340 else
1341 cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
1342 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.));
1343 for(int k = 0; k < BANDS; k++)
1344 {
1345 float x, y;
1346 _apply_mix(self, ch, k, mix, p.x[ch2][k], p.y[ch2][k], &x, &y);
1347 cairo_arc(cr, width * x, -height * y, DT_PIXEL_APPLY_DPI(3.0), 0.0, 2.0 * M_PI);
1348 if(c->x_move == k)
1349 cairo_fill(cr);
1350 else
1351 cairo_stroke(cr);
1352 }
1353 cairo_restore(cr);
1354 }
1355
1356 if(c->mouse_y > 0 || c->dragging)
1357 {
1358 // draw min/max, if selected
1359 // cairo_set_source_rgba(cr, .6, .6, .6, .5);
1360 cairo_move_to(cr, 0, -height * c->draw_min_ys[0]);
1361 for(int k = 1; k < RES; k++)
1362 cairo_line_to(cr, k * width / (float)(RES - 1), -height * c->draw_min_ys[k]);
1363 for(int k = RES - 1; k >= 0; k--)
1364 cairo_line_to(cr, k * width / (float)(RES - 1), -height * c->draw_max_ys[k]);
1365 cairo_close_path(cr);
1366 cairo_fill(cr);
1367 // draw mouse focus circle
1368 cairo_set_source_rgba(cr, .9, .9, .9, .5);
1369 const float pos = RES * c->mouse_x;
1370 int k = (int)pos;
1371 const float f = k - pos;
1372 if(k >= RES - 1) k = RES - 2;
1373 const float ht = -height * (f * c->draw_ys[k] + (1 - f) * c->draw_ys[k + 1]);
1374 cairo_arc(cr, c->mouse_x * width, ht, c->mouse_radius * width, 0, 2. * M_PI);
1375 cairo_stroke(cr);
1376 }
1377
1378 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1379
1380 // draw x positions
1381 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.));
1382 cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
1383 const float arrw = DT_PIXEL_APPLY_DPI(7.0f);
1384 for(int k = 1; k < BANDS - 1; k++)
1385 {
1386 cairo_move_to(cr, width * p.x[(int)c->channel][k], inset - DT_PIXEL_APPLY_DPI(1));
1387 cairo_rel_line_to(cr, -arrw * .5f, 0);
1388 cairo_rel_line_to(cr, arrw * .5f, -arrw);
1389 cairo_rel_line_to(cr, arrw * .5f, arrw);
1390 cairo_close_path(cr);
1391 if(c->x_move == k)
1392 cairo_fill(cr);
1393 else
1394 cairo_stroke(cr);
1395 }
1396
1397 cairo_restore(cr);
1398
1399 if(c->mouse_y > 0 || c->dragging)
1400 {
1401 // draw labels:
1402 PangoLayout *layout;
1403 PangoRectangle ink;
1404 PangoFontDescription *desc = pango_font_description_copy_static(dt_bauhaus_get_global()->pango_font_desc);
1405 pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD);
1406 pango_font_description_set_absolute_size(desc, (.06 * height) * PANGO_SCALE);
1407 layout = pango_cairo_create_layout(cr);
1408 pango_layout_set_font_description(layout, desc);
1409 gdk_cairo_set_source_rgba(cr, &graph_bg);
1410 cairo_set_font_size(cr, .06 * height);
1411 pango_layout_set_text(layout, _("coarse"), -1);
1412 pango_layout_get_pixel_extents(layout, &ink, NULL);
1413 cairo_move_to(cr, .02 * width - ink.y, .14 * height + ink.width);
1414 cairo_save(cr);
1415 cairo_rotate(cr, -M_PI * .5f);
1416 pango_cairo_show_layout(cr, layout);
1417 cairo_restore(cr);
1418 pango_layout_set_text(layout, _("fine"), -1);
1419 pango_layout_get_pixel_extents(layout, &ink, NULL);
1420 cairo_move_to(cr, .98 * width - ink.height, .14 * height + ink.width);
1421 cairo_save(cr);
1422 cairo_rotate(cr, -M_PI * .5f);
1423 pango_cairo_show_layout(cr, layout);
1424 cairo_restore(cr);
1425
1426 switch(c->channel2)
1427 {
1428 case atrous_L:
1429 case atrous_c:
1430 dt_atrous_show_upper_label(cr, _("contrasty"), layout, ink);
1431 dt_atrous_show_lower_label(cr, _("smooth"), layout, ink);
1432 break;
1433 case atrous_Lt:
1434 case atrous_ct:
1435 dt_atrous_show_upper_label(cr, _("smooth"), layout, ink);
1436 dt_atrous_show_lower_label(cr, _("noisy"), layout, ink);
1437 break;
1438 default: // case atrous_s:
1439 dt_atrous_show_upper_label(cr, _("bold"), layout, ink);
1440 dt_atrous_show_lower_label(cr, _("dull"), layout, ink);
1441 break;
1442 }
1443 pango_font_description_free(desc);
1444 g_object_unref(layout);
1445 }
1446
1447
1448 cairo_destroy(cr);
1449 cairo_set_source_surface(crf, cst, 0, 0);
1450 cairo_paint(crf);
1451 cairo_surface_destroy(cst);
1452 return TRUE;
1453}
1454
1455static gboolean area_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
1456{
1457 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
1460 const int inset = INSET;
1461 GtkAllocation allocation;
1462 gtk_widget_get_allocation(widget, &allocation);
1463 const int height = allocation.height - 2 * inset;
1464 const int width = allocation.width - 2 * inset;
1465 if(!c->dragging) c->mouse_x = CLAMP(event->x - inset, 0, width) / (float)width;
1466 c->mouse_y = 1.0 - CLAMP(event->y - inset, 0, height) / (float)height;
1467
1468 int ch2 = c->channel;
1469 if(c->channel == atrous_L) ch2 = atrous_Lt;
1470 if(c->channel == atrous_c) ch2 = atrous_ct;
1471
1472 if(c->dragging)
1473 {
1474 // drag y-positions
1475 *p = c->drag_params;
1476 if(c->x_move >= 0)
1477 {
1478 const float mx = CLAMP(event->x - inset, 0, width) / (float)width;
1479 if(c->x_move > 0 && c->x_move < BANDS - 1)
1480 {
1481 const float minx = p->x[c->channel][c->x_move - 1] + 0.001f;
1482 const float maxx = p->x[c->channel][c->x_move + 1] - 0.001f;
1483 p->x[ch2][c->x_move] = p->x[c->channel][c->x_move] = fminf(maxx, fmaxf(minx, mx));
1484 }
1485 }
1486 else
1487 {
1488 get_params(p, c->channel2, c->mouse_x, c->mouse_y + c->mouse_pick, c->mouse_radius);
1489 }
1490 gtk_widget_queue_draw(widget);
1491 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
1492 }
1493 else if(event->y > height)
1494 {
1495 // move x-positions
1496 c->x_move = 0;
1497 float dist = fabs(p->x[c->channel][0] - c->mouse_x);
1498 for(int k = 1; k < BANDS; k++)
1499 {
1500 const float d2 = fabs(p->x[c->channel][k] - c->mouse_x);
1501 if(d2 < dist)
1502 {
1503 c->x_move = k;
1504 dist = d2;
1505 }
1506 }
1507 gtk_widget_queue_draw(widget);
1508 }
1509 else
1510 {
1511 // choose between bottom and top curve:
1512 const int ch = c->channel;
1513 float dist = 1000000.0f;
1514 for(int k = 0; k < BANDS; k++)
1515 {
1516 float d2 = fabs(p->x[c->channel][k] - c->mouse_x);
1517 if(d2 < dist)
1518 {
1519 if(fabs(c->mouse_y - p->y[ch][k]) < fabs(c->mouse_y - p->y[ch2][k]))
1520 c->channel2 = ch;
1521 else
1522 c->channel2 = ch2;
1523 dist = d2;
1524 }
1525 }
1526 // don't move x-positions:
1527 c->x_move = -1;
1528 gtk_widget_queue_draw(widget);
1529 }
1530 return TRUE;
1531}
1532
1533static gboolean area_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
1534{
1535 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
1536 if(event->button == 1 && event->type == GDK_2BUTTON_PRESS)
1537 {
1538 // reset current curve
1542 reset_mix(self);
1543 for(int k = 0; k < BANDS; k++)
1544 {
1545 p->x[c->channel2][k] = d->x[c->channel2][k];
1546 p->y[c->channel2][k] = d->y[c->channel2][k];
1547 }
1548 gtk_widget_queue_draw(self->gui->widget);
1549 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
1550 }
1551 else if(event->button == 1)
1552 {
1553 // set active point
1555 reset_mix(self);
1556 const int inset = INSET;
1557 GtkAllocation allocation;
1558 gtk_widget_get_allocation(widget, &allocation);
1559 const int height = allocation.height - 2 * inset;
1560 const int width = allocation.width - 2 * inset;
1561 c->mouse_pick
1562 = dt_draw_curve_calc_value(c->minmax_curve, CLAMP(event->x - inset, 0, width) / (float)width);
1563 c->mouse_pick -= 1.0 - CLAMP(event->y - inset, 0, height) / (float)height;
1564 c->dragging = 1;
1565 return TRUE;
1566 }
1567 return FALSE;
1568}
1569
1570static gboolean area_button_release(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
1571{
1572 if(event->button == 1)
1573 {
1574 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
1576 c->dragging = 0;
1577 reset_mix(self);
1578 return TRUE;
1579 }
1580 return FALSE;
1581}
1582
1583static gboolean area_scrolled(GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
1584{
1585 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
1587
1588 int delta_y;
1589 if(dt_gui_get_scroll_unit_deltas(event, NULL, &delta_y))
1590 {
1591 c->mouse_radius = CLAMP(c->mouse_radius * (1.0 + 0.1 * delta_y), 0.25 / BANDS, 1.0);
1592 gtk_widget_queue_draw(widget);
1593 }
1594 return TRUE;
1595}
1596
1597static void tab_switch(GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer user_data)
1598{
1599 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
1601 if(dt_gui_widgets_suppressed()) return;
1602 c->channel = c->channel2 = (atrous_channel_t)page_num;
1603 gtk_widget_queue_draw(self->gui->widget);
1604}
1605
1606static void mix_callback(GtkWidget *slider, gpointer user_data)
1607{
1608 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
1609 if(dt_gui_widgets_suppressed()) return;
1611 p->mix = dt_bauhaus_slider_get(slider);
1612 gtk_widget_queue_draw(self->gui->widget);
1613 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
1614}
1615
1616void gui_init(struct dt_iop_module_t *self)
1617{
1620
1621 c->num_samples = 0;
1622 c->band_max = 0;
1623 c->channel = c->channel2 = dt_conf_get_int("plugins/darkroom/atrous/gui_channel");
1624 int ch = (int)c->channel;
1625 c->minmax_curve = dt_draw_curve_new(0.0, 1.0, CATMULL_ROM);
1626 for(int k = 0; k < BANDS; k++)
1627 (void)dt_draw_curve_add_point(c->minmax_curve, p->x[ch][k], p->y[ch][k]);
1628 c->mouse_x = c->mouse_y = c->mouse_pick = -1.0;
1629 c->dragging = 0;
1630 self->gui->timeout_handle = 0;
1631 c->x_move = -1;
1632 c->mouse_radius = 1.0 / BANDS;
1633 c->in_curve = FALSE;
1634
1635 self->gui->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1636
1637 c->channel_tabs = dt_ui_notebook_new();
1638 dt_ui_notebook_page(c->channel_tabs, N_("luma"), _("change lightness at each feature size"));
1639 dt_ui_notebook_page(c->channel_tabs, N_("chroma"), _("change color saturation at each feature size"));
1640 dt_ui_notebook_page(c->channel_tabs, N_("edges"), _("change edge halos at each feature size\nonly changes results of luma and chroma tabs"));
1641 gtk_widget_show(gtk_notebook_get_nth_page(c->channel_tabs, c->channel));
1642 gtk_notebook_set_current_page(c->channel_tabs, c->channel);
1643 g_signal_connect(G_OBJECT(c->channel_tabs), "switch_page", G_CALLBACK(tab_switch), self);
1644 gtk_box_pack_start(GTK_BOX(self->gui->widget), GTK_WIDGET(c->channel_tabs), FALSE, FALSE, 0);
1645
1646 // graph
1647 c->area = GTK_DRAWING_AREA(gtk_drawing_area_new());
1648 gtk_widget_set_hexpand(GTK_WIDGET(c->area), TRUE);
1649 gtk_box_pack_start(GTK_BOX(self->gui->widget),
1650 dt_ui_resizable_drawing_area(GTK_WIDGET(c->area),
1651 "plugins/darkroom/atrous/graphheight", 280, 100),
1652 FALSE, FALSE, 0);
1653
1654 gtk_widget_add_events(GTK_WIDGET(c->area),
1655 GDK_POINTER_MOTION_MASK
1656 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
1657 | GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK
1659 g_object_set_data(G_OBJECT(c->area), "iop-instance", self);
1660 g_signal_connect(G_OBJECT(c->area), "draw", G_CALLBACK(area_draw), self);
1661 g_signal_connect(G_OBJECT(c->area), "button-press-event", G_CALLBACK(area_button_press), self);
1662 g_signal_connect(G_OBJECT(c->area), "button-release-event", G_CALLBACK(area_button_release), self);
1663 g_signal_connect(G_OBJECT(c->area), "motion-notify-event", G_CALLBACK(area_motion_notify), self);
1664 g_signal_connect(G_OBJECT(c->area), "leave-notify-event", G_CALLBACK(area_leave_notify), self);
1665 g_signal_connect(G_OBJECT(c->area), "enter-notify-event", G_CALLBACK(area_enter_notify), self);
1666 g_signal_connect(G_OBJECT(c->area), "scroll-event", G_CALLBACK(area_scrolled), self);
1667
1668 // mix slider
1669 c->mix = dt_bauhaus_slider_from_params(self, N_("mix"));
1670 gtk_widget_set_tooltip_text(c->mix, _("make effect stronger or weaker"));
1671 g_signal_connect(G_OBJECT(c->mix), "value-changed", G_CALLBACK(mix_callback), self);
1672}
1673
1675{
1677 dt_conf_set_int("plugins/darkroom/atrous/gui_channel", c->channel);
1678 dt_draw_curve_destroy(c->minmax_curve);
1679
1681}
1682
1683// clang-format off
1684// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1685// vim: shiftwidth=2 expandtab tabstop=2 cindent
1686// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1687// clang-format on
const double thrs
static double dist(double x1, double y1, double x2, double y2)
Definition ashift_lsd.c:250
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void init(dt_iop_module_t *module)
Definition atrous.c:694
static gboolean area_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
Definition atrous.c:1533
#define GAUSS(x, sigma)
Definition atrous.c:819
const char ** description(struct dt_iop_module_t *self)
Definition atrous.c:181
static __DT_CLONE_TARGETS__ int get_scales(float(*thrs)[4], float(*boost)[4], float *sharp, const dt_iop_atrous_data_t *const d, const dt_iop_roi_t *roi_in, const dt_dev_pixelpipe_iop_t *const piece)
Definition atrous.c:254
int default_group()
Definition atrous.c:190
static gboolean area_leave_notify(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
Definition atrous.c:1122
#define BANDS
Definition atrous.c:97
static void reset_mix(dt_iop_module_t *self)
Definition atrous.c:1093
void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *params, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition atrous.c:744
atrous_channel_t
Definition atrous.c:116
@ atrous_s
Definition atrous.c:119
@ atrous_ct
Definition atrous.c:121
@ atrous_L
Definition atrous.c:117
@ atrous_none
Definition atrous.c:122
@ atrous_c
Definition atrous.c:118
@ atrous_Lt
Definition atrous.c:120
const char * aliases()
Definition atrous.c:176
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition atrous.c:778
#define RES
Definition atrous.c:99
const char * name()
Definition atrous.c:171
void gui_update(struct dt_iop_module_t *self)
Definition atrous.c:1103
void gui_init(struct dt_iop_module_t *self)
Definition atrous.c:1616
static void mix_callback(GtkWidget *slider, gpointer user_data)
Definition atrous.c:1606
static void _apply_mix(dt_iop_module_t *self, const int ch, const int k, const float mix, const float px, const float py, float *x, float *y)
Definition atrous.c:734
#define dt_atrous_show_lower_label(cr, text, layout, ink)
Definition atrous.c:108
static __DT_CLONE_TARGETS__ int process_wavelets(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, const struct dt_dev_pixelpipe_iop_t *piece, const void *const i, void *const o, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out, const eaw_decompose_t decompose, const eaw_synthesize_t synthesize)
Definition atrous.c:303
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 atrous.c:673
void cleanup_global(dt_iop_module_so_t *module)
Definition atrous.c:722
static void get_params(dt_iop_atrous_params_t *p, const int ch, const double mouse_x, const double mouse_y, const float rad)
Definition atrous.c:1133
static gboolean area_enter_notify(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
Definition atrous.c:1112
static gboolean area_button_release(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
Definition atrous.c:1570
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition atrous.c:200
static gboolean area_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
Definition atrous.c:1455
int flags()
Definition atrous.c:195
static gboolean area_draw(GtkWidget *widget, cairo_t *crf, gpointer user_data)
Definition atrous.c:1143
void gui_cleanup(struct dt_iop_module_t *self)
Definition atrous.c:1674
void init_presets(dt_iop_module_so_t *self)
Definition atrous.c:821
static __DT_CLONE_TARGETS__ int get_samples(float *t, const dt_iop_atrous_data_t *const d, const dt_iop_roi_t *roi_in, const dt_dev_pixelpipe_iop_t *const piece)
Definition atrous.c:232
#define INSET
Definition atrous.c:91
#define MAX_NUM_SCALES
Definition atrous.c:98
static gboolean area_scrolled(GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
Definition atrous.c:1583
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition atrous.c:808
int process(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, const struct dt_dev_pixelpipe_iop_t *piece, const void *const i, void *const o)
Definition atrous.c:397
void init_global(dt_iop_module_so_t *module)
Definition atrous.c:708
#define dt_atrous_show_upper_label(cr, text, layout, ink)
Definition atrous.c:101
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 atrous.c:538
static void tab_switch(GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer user_data)
Definition atrous.c:1597
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 atrous.c:205
#define m
Definition basecurve.c:283
float dt_bauhaus_slider_get(GtkWidget *widget)
Definition bauhaus.c:3283
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3334
@ IOP_CS_LAB
static const float x
const float f
const float *const const float coeff[3]
const int t
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
const dt_colormatrix_t dt_aligned_pixel_t out
void dt_conf_set_int(const char *name, int val)
int dt_conf_get_int(const char *name)
Integer for name, clamped to the bounds declared in the XML.
#define CATMULL_ROM
Definition curve_tools.h:35
struct dt_bauhaus_t * dt_bauhaus_get_global(void)
Definition darktable.c:649
#define dt_database_start_transaction()
Definition database.h:290
#define dt_database_release_transaction()
Definition database.h:291
#define dt_dev_add_history_item(dev, module, enable, redraw)
void dt_iop_params_t
Definition dev_history.h:43
gboolean dt_dev_pixelpipe_has_preview_output(const dt_develop_t *dev, const dt_dev_pixelpipe_t *pipe, const dt_iop_roi_t *roi)
Definition develop.c:405
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
Definition develop.h:123
static void dt_draw_curve_calc_values(dt_draw_curve_t *c, const float min, const float max, const int res, float *x, float *y)
Definition draw.h:324
static void dt_draw_grid(cairo_t *cr, const int num, const int left, const int top, const int right, const int bottom)
Definition draw.h:158
static float dt_draw_curve_calc_value(dt_draw_curve_t *c, const float x)
Definition draw.h:360
static void dt_draw_curve_destroy(dt_draw_curve_t *c)
Definition draw.h:297
static void dt_draw_curve_set_point(dt_draw_curve_t *c, const int num, const float x, const float y)
Definition draw.h:303
static int dt_draw_curve_add_point(dt_draw_curve_t *c, const float x, const float y)
Definition draw.h:379
static dt_draw_curve_t * dt_draw_curve_new(const float min, const float max, unsigned int type)
Definition draw.h:281
void eaw_decompose(float *const restrict out, const float *const restrict in, float *const restrict detail, const int scale, const float sharpen, const int32_t width, const int32_t height)
Definition eaw.c:79
void eaw_synthesize(float *const out, const float *const in, const float *const restrict detail, const float *const restrict threshold, const float *const restrict boost, const int32_t width, const int32_t height)
Definition eaw.c:157
void dt_gui_presets_add_generic(const char *name, dt_dev_operation_t op, const int32_t version, const void *params, const int32_t params_size, const int32_t enabled)
int dt_iop_alloc_image_buffers(struct dt_iop_module_t *const module, const struct dt_iop_roi_t *const roi_in, const struct dt_iop_roi_t *const roi_out,...)
Definition imagebuf.c:35
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
void dt_iop_default_init(dt_iop_module_t *module)
Definition imageop.c:308
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:1909
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:186
@ IOP_FLAGS_ALLOW_TILING
Definition imageop.h:188
@ IOP_GROUP_SHARPNESS
Definition imageop.h:160
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
#define IOP_GUI_FREE
Definition imageop_gui.h:96
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
static const dt_dev_pixelpipe_t * pipe
Definition layers.h:49
static const dt_dev_pixelpipe_t const dt_dev_pixelpipe_iop_t * piece
Definition layers.h:50
static const dt_iop_drawlayer_params_t * params
Definition layers.h:80
static float mix(const float a, const float b, const float t)
Definition liquify.c:751
@ DT_DEBUG_OPENCL
Definition logging.h:57
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
float *const restrict 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 M_PI
Definition math.h:47
#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 o
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
DT_MODULE() for a module whose params struct is introspected.
GtkWidget * dt_ui_notebook_page(GtkNotebook *notebook, const char *text, const char *tooltip)
Definition notebook.c:88
GtkNotebook * dt_ui_notebook_new()
Definition notebook.c:83
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
Definition opencl.c:2612
void * dt_opencl_alloc_device(const int devid, const int width, const int height, const int bpp)
Definition opencl.c:2952
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2506
void * dt_opencl_copy_host_to_device_constant(const int devid, const size_t size, void *host)
Definition opencl.c:2808
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:2737
void dt_opencl_free_kernel(const int kernel)
Definition opencl.c:2549
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:2603
void dt_opencl_release_mem_object(cl_mem mem)
Definition opencl.c:2863
#define ROUNDUPDHT(a, b)
Definition opencl.h:86
#define ROUNDUPDWD(a, b)
Definition opencl.h:85
#define __OMP_SIMD__(...)
Definition openmp.h:99
#define dt_pixelpipe_cache_free_align(mem)
static gboolean dt_dev_pixelpipe_has_shutdown(const dt_dev_pixelpipe_t *pipe)
static cairo_surface_t * dt_cairo_image_surface_create(cairo_format_t format, int width, int height)
GtkWidget * dt_ui_resizable_drawing_area(GtkWidget *area, char *config_str, int default_height, int min_height)
Make a self-drawing widget (typically a GtkDrawingArea graph or scope) vertically resizable.
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
Definition simd.h:53
const float sigma
const float noise
int32_t gui_attached
Definition develop.h:167
dt_draw_curve_t * curve[atrous_none]
Definition atrous.c:167
GtkDrawingArea * area
Definition atrous.c:136
atrous_channel_t channel2
Definition atrous.c:144
dt_iop_atrous_params_t drag_params
Definition atrous.c:140
GtkNotebook * channel_tabs
Definition atrous.c:137
atrous_channel_t channel
Definition atrous.c:144
dt_draw_curve_t * minmax_curve
Definition atrous.c:143
float y[atrous_none][6]
Definition atrous.c:129
float x[atrous_none][6]
Definition atrous.c:128
GtkWidget * expander
Definition imageop_gui.h:57
GtkWidget * widget
Definition imageop_gui.h:47
dt_dev_operation_t op
Definition imageop.h:235
dt_iop_global_data_t * data
Definition imageop.h:238
dt_iop_params_t * default_params
Definition imageop.h:333
struct dt_iop_module_gui_t * gui
Definition imageop.h:346
struct dt_develop_t * dev
Definition imageop.h:311
dt_iop_global_data_t * global_data
Definition imageop.h:337
dt_iop_params_t * params
Definition imageop.h:333
Region of interest passed through the pixelpipe.
Definition format.h:49
double scale
Definition format.h:51
int width
Definition format.h:50
int height
Definition format.h:50
GtkWidget * notebook
#define __DT_CLONE_TARGETS__
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29
gboolean dt_gui_get_scroll_unit_deltas(const GdkEventScroll *event, int *delta_x, int *delta_y)
GdkEventMask dt_widget_scroll_mask(void)
gboolean dt_gui_widgets_suppressed(void)
#define dt_gui_freeze_begin()
#define dt_gui_freeze_end()
#define DT_GUI_BOX_SPACING
#define DT_PIXEL_APPLY_DPI(value)