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