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