Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
rawdenoise.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2011 Bruce Guenter.
4 Copyright (C) 2011-2012 Henrik Andersson.
5 Copyright (C) 2011-2013, 2016 johannes hanika.
6 Copyright (C) 2011 Jérémy Rosen.
7 Copyright (C) 2011 Robert Bieber.
8 Copyright (C) 2011 Rostyslav Pidgornyi.
9 Copyright (C) 2011-2014, 2016, 2019 Tobias Ellinghaus.
10 Copyright (C) 2012 parafin.
11 Copyright (C) 2012 Pascal de Bruijn.
12 Copyright (C) 2012 Richard Wonka.
13 Copyright (C) 2014, 2021 Dan Torop.
14 Copyright (C) 2014-2016 Roman Lebedev.
15 Copyright (C) 2014, 2019 Ulrich Pegelow.
16 Copyright (C) 2015-2016 Pedro Côrte-Real.
17 Copyright (C) 2017 Heiko Bauke.
18 Copyright (C) 2018, 2020, 2022-2023, 2025-2026 Aurélien PIERRE.
19 Copyright (C) 2018 Edgardo Hoszowski.
20 Copyright (C) 2018 Maurizio Paglia.
21 Copyright (C) 2018, 2020-2022 Pascal Obry.
22 Copyright (C) 2018-2019 rawfiner.
23 Copyright (C) 2019 Andreas Schneider.
24 Copyright (C) 2019 emeikei.
25 Copyright (C) 2019 Hanno Schwalm.
26 Copyright (C) 2020 Aldric Renaudin.
27 Copyright (C) 2020-2022 Diederik Ter Rahe.
28 Copyright (C) 2020-2021 Hubert Kowalski.
29 Copyright (C) 2020 Ralf Brown.
30 Copyright (C) 2021 Chris Elston.
31 Copyright (C) 2022 Martin Bařinka.
32 Copyright (C) 2022 Philipp Lutz.
33
34 darktable is free software: you can redistribute it and/or modify
35 it under the terms of the GNU General Public License as published by
36 the Free Software Foundation, either version 3 of the License, or
37 (at your option) any later version.
38
39 darktable is distributed in the hope that it will be useful,
40 but WITHOUT ANY WARRANTY; without even the implied warranty of
41 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
42 GNU General Public License for more details.
43
44 You should have received a copy of the GNU General Public License
45 along with darktable. If not, see <http://www.gnu.org/licenses/>.
46*/
47#ifdef HAVE_CONFIG_H
48#include "config.h"
49#endif
50#include "bauhaus/bauhaus.h"
51#include "common/darktable.h"
52#include "common/imagebuf.h"
53#include "common/dwt.h"
54#include "control/control.h"
55#include "develop/imageop.h"
57#include "develop/imageop_gui.h"
59#include "dtgtk/drawingarea.h"
60
61#include "gui/draw.h"
62#include "gui/gtk.h"
63#include "iop/iop_api.h"
64
65#include <gtk/gtk.h>
66#include <stdlib.h>
67#include <strings.h>
68
70
71#define DT_IOP_RAWDENOISE_INSET DT_PIXEL_APPLY_DPI(5)
72#define DT_IOP_RAWDENOISE_RES 64
73#define DT_IOP_RAWDENOISE_BANDS 5
74
83
85{
86 float threshold; // $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.01 $DESCRIPTION: "noise threshold"
90
108
116
120
121int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params,
122 const int new_version)
123{
124 if(old_version == 1 && new_version == 2)
125 {
126 // Since first version, the dt_iop_params_t struct have new members
127 // at the end of the struct.
128 // Yet, the beginning of the struct is exactly the same:
129 // threshold is still the first member of the struct.
130 // This allows to define the variable o with dt_iop_rawdenoise_params_t
131 // as long as we don't try to access new members on o.
132 // In other words, o can be seen as a dt_iop_rawdenoise_params_t
133 // with no allocated space for the new member.
136 n->threshold = o->threshold;
137 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
138 {
139 for(int ch = 0; ch < DT_RAWDENOISE_NONE; ch++)
140 {
141 n->x[ch][k] = k / (DT_IOP_RAWDENOISE_BANDS - 1.0);
142 n->y[ch][k] = 0.5f;
143 }
144 }
145 return 0;
146 }
147 return 1;
148}
149
150
151const char *name()
152{
153 return _("raw denoise");
154}
155
156const char **description(struct dt_iop_module_t *self)
157{
158 return dt_iop_set_description(self, _("denoise the raw picture early in the pipeline"),
159 _("corrective"),
160 _("linear, raw, scene-referred"),
161 _("linear, raw"),
162 _("linear, raw, scene-referred"));
163}
164
169
171{
172 return IOP_GROUP_REPAIR;
173}
174
176{
177 return IOP_CS_RAW;
178}
179
182{
183 default_input_format(self, pipe, piece, dsc);
184 dsc->channels = 1;
186}
187
188#define BIT16 65536.0
189
191static void compute_channel_noise(float *const noise, int color, const dt_iop_rawdenoise_data_t *const data)
192{
193 // note that these constants are the same for X-Trans and Bayer, as they are proportional to image detail on
194 // each channel, not the sensor pattern
195 static const float noise_all[] = { 0.8002, 0.2735, 0.1202, 0.0585, 0.0291, 0.0152, 0.0080, 0.0044 };
196 for(int i = 0; i < DT_IOP_RAWDENOISE_BANDS; i++)
197 {
198 // scale the value from [0,1] to [0,16],
199 // and makes the "0.5" neutral value become 1
200 float chan_threshold_exp_4;
201 switch(color)
202 {
203 case 0:
204 chan_threshold_exp_4 = data->force[DT_RAWDENOISE_R][DT_IOP_RAWDENOISE_BANDS - i - 1];
205 break;
206 case 2:
207 chan_threshold_exp_4 = data->force[DT_RAWDENOISE_B][DT_IOP_RAWDENOISE_BANDS - i - 1];
208 break;
209 default:
210 chan_threshold_exp_4 = data->force[DT_RAWDENOISE_G][DT_IOP_RAWDENOISE_BANDS - i - 1];
211 break;
212 }
213 chan_threshold_exp_4 *= chan_threshold_exp_4;
214 chan_threshold_exp_4 *= chan_threshold_exp_4;
215 // repeat for the overall all-channels thresholds
216 float all_threshold_exp_4 = data->force[DT_RAWDENOISE_ALL][DT_IOP_RAWDENOISE_BANDS - i - 1];
217 all_threshold_exp_4 *= all_threshold_exp_4;
218 all_threshold_exp_4 *= all_threshold_exp_4;
219 noise[i] = noise_all[i] * all_threshold_exp_4 * chan_threshold_exp_4 * 16.0f * 16.0f;
220 // the following multiplication needs to stay separate from the above line, because merging the two changes
221 // the results on the integration test!
222 noise[i] *= data->threshold;
223 }
224}
225
227static int wavelet_denoise(const float *const restrict in, float *const restrict out, const dt_iop_roi_t *const roi,
228 const dt_iop_rawdenoise_data_t * const data, const uint32_t filters)
229{
230 const size_t size = (size_t)(roi->width / 2 + 1) * (roi->height / 2 + 1);
231 float *const restrict fimg = dt_pixelpipe_cache_alloc_align_float_cache(size, 0);
232 if (IS_NULL_PTR(fimg))
233 return 1;
234
235 const int nc = 4;
236 for(int c = 0; c < nc; c++) /* denoise R,G1,B,G3 individually */
237 {
238 const int color = FC(c % 2, c / 2, filters);
240 compute_channel_noise(noise,color,data);
241
242 // adjust for odd width and height
243 const int halfwidth = roi->width / 2 + (roi->width & (~(c >> 1)) & 1);
244 const int halfheight = roi->height / 2 + (roi->height & (~c) & 1);
245
246 // collect one of the R/G1/G2/B channels into a monochrome image, applying sqrt() to the values as a
247 // variance-stabilizing transform
249 for(int row = c & 1; row < roi->height; row += 2)
250 {
251 float *const restrict fimgp = fimg + (size_t)row / 2 * halfwidth;
252 const int offset = (c & 2) >> 1;
253 const float *const restrict inp = in + (size_t)row * roi->width + offset;
254 const int senselwidth = (roi->width-offset+1)/2;
255 for(int col = 0; col < senselwidth; col++)
256 fimgp[col] = sqrtf(MAX(0.0f, inp[2*col]));
257 }
258
259 // perform the wavelet decomposition and denoising
260 if(dwt_denoise(fimg, halfwidth, halfheight, DT_IOP_RAWDENOISE_BANDS, noise) != 0)
261 {
263 return 1;
264 }
265
266 // distribute the denoised data back out to the original R/G1/G2/B channel, squaring the resulting values to
267 // undo the original transform
269 for(int row = c & 1; row < roi->height; row += 2)
270 {
271 const float *const restrict fimgp = fimg + (size_t)row / 2 * halfwidth;
272 const int offset = (c & 2) >> 1;
273 float *const restrict outp = out + (size_t)row * roi->width + offset;
274 const int senselwidth = (roi->width-offset+1)/2;
275 for(int col = 0; col < senselwidth; col++)
276 {
277 float d = fimgp[col];
278 outp[2*col] = d * d;
279 }
280 }
281 }
282#if 0
283 /* FIXME: Haven't ported this part yet */
284 float maximum = 1.0; /* FIXME */
285 float black = 0.0; /* FIXME */
286 maximum *= BIT16;
287 black *= BIT16;
288 for (c=0; c<4; c++)
289 cblack[c] *= BIT16;
290 if (filters && colors == 3) /* pull G1 and G3 closer together */
291 {
292 float *window[4];
293 int wlast, blk[2];
294 float mul[2];
295 float thold = threshold/512;
296 for (row=0; row < 2; row++)
297 {
298 mul[row] = 0.125 * pre_mul[FC(row+1,0) | 1] / pre_mul[FC(row,0) | 1];
299 blk[row] = cblack[FC(row,0) | 1];
300 }
301 for (i=0; i < 4; i++)
302 window[i] = fimg + width*i;
303 for (wlast=-1, row=1; row < height-1; row++)
304 {
305 while (wlast < row+1)
306 {
307 for (wlast++, i=0; i < 4; i++)
308 window[(i+3) & 3] = window[i];
309 for (col = FC(wlast,1) & 1; col < width; col+=2)
310 window[2][col] = BAYER(wlast,col);
311 }
312 for (col = (FC(row,0) & 1)+1; col < width-1; col+=2)
313 {
314 float avg = ( window[0][col-1] + window[0][col+1] +
315 window[2][col-1] + window[2][col+1] - blk[~row & 1]*4 )
316 * mul[row & 1] + (window[1][col] + blk[row & 1]) * 0.5;
317 avg = avg > 0 ? sqrtf(avg) : 0;
318 float diff = sqrtf(BAYER(row,col)) - avg;
319 if (diff < -thold) diff += thold;
320 else if (diff > thold) diff -= thold;
321 else diff = 0;
322 BAYER(row,col) = SQR(avg+diff);
323 }
324 }
325 }
326#endif
328 return 0;
329}
330
331static inline float vstransform(const float value)
332{
333 return sqrtf(MAX(0.0f, value));
334}
335
337static int wavelet_denoise_xtrans(const float *const restrict in, float *const restrict out,
338 const dt_iop_roi_t *const restrict roi,
339 const dt_iop_rawdenoise_data_t *const data, const uint8_t (*const xtrans)[6])
340{
341 const int width = roi->width;
342 const int height = roi->height;
343 const size_t size = (size_t)width * height;
344 // allocate a buffer for the particular color channel to be denoise; we add two rows to simplify the
345 // channel-extraction code (no special case for top/bottom row)
346 float *const img = dt_pixelpipe_cache_alloc_align_float_cache((size_t)width * (height+2), 0);
347 if (IS_NULL_PTR(img))
348 {
349 // we ran out of memory, so just pass through the image without denoising
350 memcpy(out, in, sizeof(float) * size);
351 return 1;
352 }
353 float *const fimg = img + width; // point at the actual color channel contents in the buffer
354
355 for(int c = 0; c < 3; c++)
356 {
359
360 // ensure a defined value for every pixel in the top and bottom rows, even if they are more than
361 // one pixel away from the nearest neighbor of the same color and thus the simple interpolation
362 // used in the following loop does not set them
363 for (size_t col = 0; col < width; col++)
364 {
365 fimg[col] = 0.5f;
366 fimg[(size_t)(height-1)*width + col] = 0.5f;
367 }
368 const size_t nthreads = darktable.num_openmp_threads; // go direct, darktable.num_openmp_threads always returns numprocs
369 const size_t chunksize = (height + nthreads - 1) / nthreads;
370 __OMP_PARALLEL_FOR__(num_threads(nthreads) )
371 for(size_t chunk = 0; chunk < nthreads; chunk++)
372 {
373 const size_t start = chunk * chunksize;
374 const size_t pastend = MIN(start + chunksize,height);
375 for(size_t row = start; row < pastend; row++)
376 {
377 const float *const restrict inp = in + row * width;
378 float *const restrict fimgp = fimg + row * width;
379 // handle red/blue pixel in first column
380 if (c != 1 && FCxtrans(row, 0, roi, xtrans) == c)
381 {
382 // copy to neighbors above and right
383 const float d = vstransform(inp[0]);
384 fimgp[0] = fimgp[-width] = fimgp[-width+1] = d;
385 }
386 for(size_t col = (c != 1); col < width-1; col++)
387 {
388 if (FCxtrans(row, col, roi, xtrans) == c)
389 {
390 // the pixel at the current location has the desired color, so apply sqrt() as a variance-stablizing
391 // transform, and then do cheap nearest-neighbor interpolation by copying it to appropriate neighbors
392 const float d = vstransform(inp[col]);
393 fimgp[col] = d;
394 if (c == 1) // green pixel
395 {
396 // Copy to the right and down. The X-Trans color layout is such that copying to those two neighbors
397 // results in all positions being filled except in the left-most and right-most columns and sometimes
398 // the topmost and bottom-most rows (depending on how the ROI aligns with the CFA).
399 fimgp[col+1] = fimgp[col+width] = d;
400 }
401 else // red or blue pixel
402 {
403 // Copy value to all eight neighbors; it's OK to copy to the row above even when we're in row 0 (or
404 // the row below when in the last row) because the destination is sandwiched between other buffers
405 // that will be overwritten afterwards anyway. We need to copy to all adjacent positions because
406 // there may be two green pixels between nearest red/red or blue/blue, so each will cover one of the
407 // greens.
408 fimgp[col-width-1] = fimgp[col-width] = fimgp[col-width+1] = d; // row above
409 fimgp[col-1] = fimgp[col+1] = d; // left and right
410 if (row < pastend-1)
411 fimgp[col+width-1] = fimgp[col+width] = fimgp[col+width+1] = d; // row below
412 }
413 }
414 }
415 // leftmost and rightmost pixel in the row may still need to be filled in from a neighbor
416 if (FCxtrans(row, 0, roi, xtrans) != c)
417 {
418 int src = 0; // fallback is current sensel even if it has the wrong color
419 if (row > 1 && FCxtrans(row-1, 0, roi, xtrans) == c)
420 src = -width;
421 else if (FCxtrans(row, 1, roi, xtrans) == c)
422 src = 1;
423 else if (row > 1 && FCxtrans(row-1, 1, roi, xtrans) == c)
424 src = -width + 1;
425 fimgp[0] = vstransform(inp[src]);
426 }
427 // check the right-most pixel; if it's the desired color and not green, copy it to the neighbors
428 if (c != 1 && FCxtrans(row, width-1, roi, xtrans) == c)
429 {
430 // copy to neighbors above and left
431 const float d = vstransform(inp[width-1]);
432 fimgp[width-2] = fimgp[width-1] = fimgp[-1] = d;
433 }
434 else if (FCxtrans(row, width-1, roi, xtrans) != c)
435 {
436 int src = width-1; // fallback is current sensel even if it has the wrong color
437 if (FCxtrans(row, width-2, roi, xtrans) == c)
438 src = width-2;
439 else if (row > 1 && FCxtrans(row-1, width-1, roi, xtrans) == c)
440 src = -1;
441 else if (row > 1 && FCxtrans(row-1, width-2, roi, xtrans) == c)
442 src = -2;
443 fimgp[width-1] = vstransform(inp[src]);
444 }
445 }
446 if (pastend < height)
447 {
448 // Another slice follows us, and by updating the last row of our slice, we've clobbered values that
449 // were previously written by the other thread. Restore them.
450 const float *const restrict inp = in + pastend * width;
451 float *const restrict fimgp = fimg + pastend * width;
452 for (size_t col = 0; col < width-1; col++)
453 {
454 if (FCxtrans(pastend, col, roi, xtrans) == c)
455 {
456 const float d = vstransform(inp[col]);
457 if (c == 1) // green pixel
458 {
459 if (FCxtrans(pastend, col+1, roi, xtrans) != c)
460 fimgp[col] = fimgp[col+1] = d; // copy to the right
461 }
462 else // red/blue pixel
463 {
464 // copy the pixel's adjusted value to the prior row and left and right (if not at edge)
465 fimgp[col-width] = fimgp[col-width+1] = d;
466 if (col > 0) fimgp[col-width-1] = d;
467 }
468 }
469 // some red and blue values may need to be restored from the row TWO past the end of our slice
470 if (c != 1 && pastend+1 < height && FCxtrans(pastend+1, col, roi, xtrans) == c)
471 {
472 const float d = vstransform(inp[col+width]);
473 fimgp[col] = fimgp[col+1] = d;
474 if (col > 0) fimgp[col-1] = d;
475 }
476 }
477 }
478 }
479
480 // perform the wavelet decomposition and denoising
482 {
484 return 1;
485 }
486
487 // distribute the denoised data back out to the original R/G/B channel, squaring the resulting values to
488 // undo the original transform
490 for(int row = 0; row < height; row++)
491 {
492 const float *const restrict fimgp = fimg + (size_t)row * width;
493 float *const restrict outp = out + (size_t)row * width;
494 for(int col = 0; col < width; col++)
495 if(FCxtrans(row, col, roi, xtrans) == c)
496 {
497 float d = fimgp[col];
498 outp[col] = d * d;
499 }
500 }
501 }
502
504 return 0;
505}
506
507int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
508 void *const ovoid)
509{
510 const dt_iop_roi_t *const roi_in = &piece->roi_in;
511 const dt_iop_rawdenoise_data_t *const restrict d = (dt_iop_rawdenoise_data_t *)piece->data;
512
513 if(!(d->threshold > 0.0f))
514 {
515 dt_iop_image_copy_by_size(ovoid, ivoid, roi_in->width, roi_in->height, 1);
516 }
517 else
518 {
519 const uint32_t filters = piece->dsc_in.filters;
520 const uint8_t(*const xtrans)[6] = (const uint8_t(*const)[6])piece->dsc_in.xtrans;
521 if (filters != 9u)
522 return wavelet_denoise(ivoid, ovoid, roi_in, d, filters);
523 else
524 return wavelet_denoise_xtrans(ivoid, ovoid, roi_in, d, xtrans);
525 }
526 return 0;
527}
528
530{
531 dt_iop_default_init(module);
532
533 dt_iop_rawdenoise_params_t *d = module->default_params;
534
535 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
536 {
537 for(int ch = 0; ch < DT_RAWDENOISE_NONE; ch++)
538 {
539 d->x[ch][k] = k / (DT_IOP_RAWDENOISE_BANDS - 1.f);
540 }
541 }
542}
543
544/* Single source of truth: raw CFA wavelet denoise operates on the mosaiced buffer, so it needs a
545 * CFA (needs_demosaic), not merely the RAW flag. An already-demosaiced raw (sRAW / linear DNG) has
546 * no mosaic to denoise. Shared by reload_defaults() and force_enable(). */
547static gboolean _rawdenoise_supported(const dt_image_t *img)
548{
549 return dt_image_needs_demosaic(img);
550}
551
553{
554 const dt_image_t *const img = &module->dev->image_storage;
555 module->hide_enable_button = !_rawdenoise_supported(img);
556 dt_iop_fmt_log(module, "reload_defaults: class=%s needs_demosaic=%d -> hide_enable=%d",
558 module->hide_enable_button);
559
560 if(module->widget)
561 {
562 gtk_stack_set_visible_child_name(GTK_STACK(module->widget), module->hide_enable_button ? "non_raw" : "raw");
563 }
564
565 module->default_enabled = 0;
566}
567
568gboolean force_enable(struct dt_iop_module_t *self, const gboolean current_state)
569{
570 // History sanitization: a rawdenoise entry pasted onto a non-mosaic image is forced off here,
571 // at history-read time, rather than patched on the pipeline node in commit_params().
572 const gboolean active = _rawdenoise_supported(&self->dev->image_storage);
573 const gboolean state = current_state && active;
574 dt_iop_fmt_log(self, "force_enable: class=%s supported=%d current=%d -> %d",
576 active, current_state, state);
577 return state;
578}
579
582{
585
586 d->threshold = p->threshold;
587
588 for(int ch = 0; ch < DT_RAWDENOISE_NONE; ch++)
589 {
590 dt_draw_curve_set_point(d->curve[ch], 0, p->x[ch][DT_IOP_RAWDENOISE_BANDS - 2] - 1.0, p->y[ch][0]);
591 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
592 dt_draw_curve_set_point(d->curve[ch], k, p->x[ch][k], p->y[ch][k]);
593 dt_draw_curve_set_point(d->curve[ch], DT_IOP_RAWDENOISE_BANDS + 1, p->x[ch][1] + 1.0,
594 p->y[ch][DT_IOP_RAWDENOISE_BANDS - 1]);
595 dt_draw_curve_calc_values(d->curve[ch], 0.0, 1.0, DT_IOP_RAWDENOISE_BANDS, NULL, d->force[ch]);
596 }
597
598 // Image-type gating handled at history level by force_enable()/reload_defaults().
599 dt_iop_fmt_log(self, "commit: class=%s enabled=%d",
601}
602
604{
607
608 piece->data = (void *)d;
609 piece->data_size = sizeof(dt_iop_rawdenoise_data_t);
610 for(int ch = 0; ch < DT_RAWDENOISE_NONE; ch++)
611 {
612 d->curve[ch] = dt_draw_curve_new(0.0, 1.0, CATMULL_ROM);
613 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
614 (void)dt_draw_curve_add_point(d->curve[ch], default_params->x[ch][k], default_params->y[ch][k]);
615 }
616}
617
619{
621 for(int ch = 0; ch < DT_RAWDENOISE_NONE; ch++) dt_draw_curve_destroy(d->curve[ch]);
622 dt_free_align(piece->data);
623 piece->data = NULL;
624}
625
627{
629 gtk_widget_queue_draw(self->widget);
630}
631
632static void dt_iop_rawdenoise_get_params(dt_iop_rawdenoise_params_t *p, const int ch, const double mouse_x,
633 const double mouse_y, const float rad)
634{
635 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
636 {
637 const float f = expf(-(mouse_x - p->x[ch][k]) * (mouse_x - p->x[ch][k]) / (rad * rad));
638 p->y[ch][k] = (1 - f) * p->y[ch][k] + f * mouse_y;
639 }
640}
641
642static gboolean rawdenoise_draw(GtkWidget *widget, cairo_t *crf, gpointer user_data)
643{
644 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
647
648 int ch = (int)c->channel;
649 dt_draw_curve_set_point(c->transition_curve, 0, p.x[ch][DT_IOP_RAWDENOISE_BANDS - 2] - 1.0, p.y[ch][0]);
650 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
651 dt_draw_curve_set_point(c->transition_curve, k + 1, p.x[ch][k], p.y[ch][k]);
652 dt_draw_curve_set_point(c->transition_curve, DT_IOP_RAWDENOISE_BANDS + 1, p.x[ch][1] + 1.0,
654
655 const int inset = DT_IOP_RAWDENOISE_INSET;
656 GtkAllocation allocation;
657 gtk_widget_get_allocation(widget, &allocation);
658 int width = allocation.width, height = allocation.height;
659 cairo_surface_t *cst = dt_cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
660 cairo_t *cr = cairo_create(cst);
661 cairo_set_source_rgb(cr, .2, .2, .2);
662
663 cairo_paint(cr);
664
665 cairo_translate(cr, inset, inset);
666 width -= 2 * inset;
667 height -= 2 * inset;
668
669 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.0));
670 cairo_set_source_rgb(cr, .1, .1, .1);
671 cairo_rectangle(cr, 0, 0, width, height);
672 cairo_stroke(cr);
673
674 cairo_set_source_rgb(cr, .3, .3, .3);
675 cairo_rectangle(cr, 0, 0, width, height);
676 cairo_fill(cr);
677
678 // draw grid
679 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(.4));
680 cairo_set_source_rgb(cr, .1, .1, .1);
681 dt_draw_grid(cr, 8, 0, 0, width, height);
682
683 if(c->mouse_y > 0 || c->dragging)
684 {
685 // draw min/max curves:
686 dt_iop_rawdenoise_get_params(&p, c->channel, c->mouse_x, 1., c->mouse_radius);
687 dt_draw_curve_set_point(c->transition_curve, 0, p.x[ch][DT_IOP_RAWDENOISE_BANDS - 2] - 1.0, p.y[ch][0]);
688 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
689 dt_draw_curve_set_point(c->transition_curve, k + 1, p.x[ch][k], p.y[ch][k]);
690 dt_draw_curve_set_point(c->transition_curve, DT_IOP_RAWDENOISE_BANDS + 1, p.x[ch][1] + 1.0,
692 dt_draw_curve_calc_values(c->transition_curve, 0.0, 1.0, DT_IOP_RAWDENOISE_RES, c->draw_min_xs, c->draw_min_ys);
693
695 dt_iop_rawdenoise_get_params(&p, c->channel, c->mouse_x, .0, c->mouse_radius);
696 dt_draw_curve_set_point(c->transition_curve, 0, p.x[ch][DT_IOP_RAWDENOISE_BANDS - 2] - 1.0, p.y[ch][0]);
697 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
698 dt_draw_curve_set_point(c->transition_curve, k + 1, p.x[ch][k], p.y[ch][k]);
699 dt_draw_curve_set_point(c->transition_curve, DT_IOP_RAWDENOISE_BANDS + 1, p.x[ch][1] + 1.0,
701 dt_draw_curve_calc_values(c->transition_curve, 0.0, 1.0, DT_IOP_RAWDENOISE_RES, c->draw_max_xs, c->draw_max_ys);
702 }
703
704 cairo_save(cr);
705
706 // draw selected cursor
707 cairo_translate(cr, 0, height);
708
709 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
710 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(2.));
711
712 for(int i = 0; i < DT_RAWDENOISE_NONE; i++)
713 {
714 // draw curves, selected last
715 ch = ((int)c->channel + i + 1) % DT_RAWDENOISE_NONE;
716 float alpha = 0.3;
717 if(i == DT_RAWDENOISE_NONE - 1) alpha = 1.0;
718 switch(ch)
719 {
720 case 0:
721 cairo_set_source_rgba(cr, .7, .7, .7, alpha);
722 break;
723 case 1:
724 cairo_set_source_rgba(cr, .7, .1, .1, alpha);
725 break;
726 case 2:
727 cairo_set_source_rgba(cr, .1, .7, .1, alpha);
728 break;
729 case 3:
730 cairo_set_source_rgba(cr, .1, .1, .7, alpha);
731 break;
732 }
733
735 dt_draw_curve_set_point(c->transition_curve, 0, p.x[ch][DT_IOP_RAWDENOISE_BANDS - 2] - 1.0, p.y[ch][0]);
736 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
737 dt_draw_curve_set_point(c->transition_curve, k + 1, p.x[ch][k], p.y[ch][k]);
738 dt_draw_curve_set_point(c->transition_curve, DT_IOP_RAWDENOISE_BANDS + 1, p.x[ch][1] + 1.0,
740 dt_draw_curve_calc_values(c->transition_curve, 0.0, 1.0, DT_IOP_RAWDENOISE_RES, c->draw_xs, c->draw_ys);
741 cairo_move_to(cr, 0 * width / (float)(DT_IOP_RAWDENOISE_RES - 1), -height * c->draw_ys[0]);
742 for(int k = 1; k < DT_IOP_RAWDENOISE_RES; k++)
743 cairo_line_to(cr, k * width / (float)(DT_IOP_RAWDENOISE_RES - 1), -height * c->draw_ys[k]);
744 cairo_stroke(cr);
745 }
746
747 ch = c->channel;
748 // draw dots on knots
749 cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
750 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.));
751 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
752 {
753 cairo_arc(cr, width * p.x[ch][k], -height * p.y[ch][k], DT_PIXEL_APPLY_DPI(3.0), 0.0, 2.0 * M_PI);
754 if(c->x_move == k)
755 cairo_fill(cr);
756 else
757 cairo_stroke(cr);
758 }
759
760 if(c->mouse_y > 0 || c->dragging)
761 {
762 // draw min/max, if selected
763 cairo_set_source_rgba(cr, .7, .7, .7, .6);
764 cairo_move_to(cr, 0, -height * c->draw_min_ys[0]);
765 for(int k = 1; k < DT_IOP_RAWDENOISE_RES; k++)
766 cairo_line_to(cr, k * width / (float)(DT_IOP_RAWDENOISE_RES - 1), -height * c->draw_min_ys[k]);
767 for(int k = DT_IOP_RAWDENOISE_RES - 1; k >= 0; k--)
768 cairo_line_to(cr, k * width / (float)(DT_IOP_RAWDENOISE_RES - 1), -height * c->draw_max_ys[k]);
769 cairo_close_path(cr);
770 cairo_fill(cr);
771 // draw mouse focus circle
772 cairo_set_source_rgba(cr, .9, .9, .9, .5);
773 const float pos = DT_IOP_RAWDENOISE_RES * c->mouse_x;
774 int k = (int)pos;
775 const float f = k - pos;
777 float ht = -height * (f * c->draw_ys[k] + (1 - f) * c->draw_ys[k + 1]);
778 cairo_arc(cr, c->mouse_x * width, ht, c->mouse_radius * width, 0, 2. * M_PI);
779 cairo_stroke(cr);
780 }
781
782 cairo_restore(cr);
783
784 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
785
786 // draw labels:
787 PangoLayout *layout;
788 PangoRectangle ink;
789 PangoFontDescription *desc = pango_font_description_copy_static(darktable.bauhaus->pango_font_desc);
790 pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD);
791 pango_font_description_set_absolute_size(desc, (.08 * height) * PANGO_SCALE);
792 layout = pango_cairo_create_layout(cr);
793 pango_layout_set_font_description(layout, desc);
794 cairo_set_source_rgb(cr, .1, .1, .1);
795
796 pango_layout_set_text(layout, _("coarse"), -1);
797 pango_layout_get_pixel_extents(layout, &ink, NULL);
798 cairo_move_to(cr, .02 * width - ink.y, .5 * (height + ink.width));
799 cairo_save(cr);
800 cairo_rotate(cr, -M_PI * .5f);
801 pango_cairo_show_layout(cr, layout);
802 cairo_restore(cr);
803
804 pango_layout_set_text(layout, _("fine"), -1);
805 pango_layout_get_pixel_extents(layout, &ink, NULL);
806 cairo_move_to(cr, .98 * width - ink.height, .5 * (height + ink.width));
807 cairo_save(cr);
808 cairo_rotate(cr, -M_PI * .5f);
809 pango_cairo_show_layout(cr, layout);
810 cairo_restore(cr);
811
812
813 pango_layout_set_text(layout, _("smooth"), -1);
814 pango_layout_get_pixel_extents(layout, &ink, NULL);
815 cairo_move_to(cr, .5 * (width - ink.width), .08 * height - ink.height);
816 pango_cairo_show_layout(cr, layout);
817
818 pango_layout_set_text(layout, _("noisy"), -1);
819 pango_layout_get_pixel_extents(layout, &ink, NULL);
820 cairo_move_to(cr, .5 * (width - ink.width), .97 * height - ink.height);
821 pango_cairo_show_layout(cr, layout);
822
823 pango_font_description_free(desc);
824 g_object_unref(layout);
825 cairo_destroy(cr);
826 cairo_set_source_surface(crf, cst, 0, 0);
827 cairo_paint(crf);
828 cairo_surface_destroy(cst);
829 return TRUE;
830}
831
832static gboolean rawdenoise_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
833{
834 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
837 const int inset = DT_IOP_RAWDENOISE_INSET;
838 GtkAllocation allocation;
839 gtk_widget_get_allocation(widget, &allocation);
840 int height = allocation.height - 2 * inset, width = allocation.width - 2 * inset;
841 if(!c->dragging) c->mouse_x = CLAMP(event->x - inset, 0, width) / (float)width;
842 c->mouse_y = 1.0 - CLAMP(event->y - inset, 0, height) / (float)height;
843 if(c->dragging)
844 {
845 *p = c->drag_params;
846 if(c->x_move < 0)
847 {
848 dt_iop_rawdenoise_get_params(p, c->channel, c->mouse_x, c->mouse_y + c->mouse_pick, c->mouse_radius);
849 }
850 gtk_widget_queue_draw(widget);
852 }
853 else
854 {
855 c->x_move = -1;
856 gtk_widget_queue_draw(widget);
857 }
858 return TRUE;
859}
860
861static gboolean rawdenoise_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
862{
863 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
865 const int ch = c->channel;
866 if(event->button == 1 && event->type == GDK_2BUTTON_PRESS)
867 {
868 // reset current curve
871 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
872 {
873 p->x[ch][k] = d->x[ch][k];
874 p->y[ch][k] = d->y[ch][k];
875 }
877 gtk_widget_queue_draw(self->widget);
878 }
879 else if(event->button == 1)
880 {
881 c->drag_params = *(dt_iop_rawdenoise_params_t *)self->params;
882 const int inset = DT_IOP_RAWDENOISE_INSET;
883 GtkAllocation allocation;
884 gtk_widget_get_allocation(widget, &allocation);
885 int height = allocation.height - 2 * inset, width = allocation.width - 2 * inset;
886 c->mouse_pick
887 = dt_draw_curve_calc_value(c->transition_curve, CLAMP(event->x - inset, 0, width) / (float)width);
888 c->mouse_pick -= 1.0 - CLAMP(event->y - inset, 0, height) / (float)height;
889 c->dragging = 1;
890 return TRUE;
891 }
892 return FALSE;
893}
894
895static gboolean rawdenoise_button_release(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
896{
897 if(event->button == 1)
898 {
899 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
901 c->dragging = 0;
902 return TRUE;
903 }
904 return FALSE;
905}
906
907static gboolean rawdenoise_leave_notify(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
908{
909 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
911 if(!c->dragging) c->mouse_y = -1.0;
912 gtk_widget_queue_draw(widget);
913 return TRUE;
914}
915
916static gboolean rawdenoise_scrolled(GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
917{
918 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
920
921 int delta_y;
922 if(dt_gui_get_scroll_unit_deltas(event, NULL, &delta_y))
923 {
924 c->mouse_radius = CLAMP(c->mouse_radius * (1.0 + 0.1 * delta_y), 0.2 / DT_IOP_RAWDENOISE_BANDS, 1.0);
925 gtk_widget_queue_draw(widget);
926 }
927
928 return TRUE;
929}
930
931static void rawdenoise_tab_switch(GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer user_data)
932{
933 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
934 if(darktable.gui->reset) return;
936 c->channel = (dt_iop_rawdenoise_channel_t)page_num;
937 gtk_widget_queue_draw(self->widget);
938}
939
941{
944
945 c->channel = dt_conf_get_int("plugins/darkroom/rawdenoise/gui_channel");
946 c->channel_tabs = GTK_NOTEBOOK(gtk_notebook_new());
947
948 dt_ui_notebook_page(c->channel_tabs, N_("all"), NULL);
949 dt_ui_notebook_page(c->channel_tabs, N_("R"), NULL);
950 dt_ui_notebook_page(c->channel_tabs, N_("G"), NULL);
951 dt_ui_notebook_page(c->channel_tabs, N_("B"), NULL);
952
953 gtk_widget_show(gtk_notebook_get_nth_page(c->channel_tabs, c->channel));
954 gtk_notebook_set_current_page(c->channel_tabs, c->channel);
955 g_signal_connect(G_OBJECT(c->channel_tabs), "switch_page", G_CALLBACK(rawdenoise_tab_switch), self);
956
957 const int ch = (int)c->channel;
958 c->transition_curve = dt_draw_curve_new(0.0, 1.0, CATMULL_ROM);
959 (void)dt_draw_curve_add_point(c->transition_curve, p->x[ch][DT_IOP_RAWDENOISE_BANDS - 2] - 1.0,
960 p->y[ch][DT_IOP_RAWDENOISE_BANDS - 2]);
961 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
962 (void)dt_draw_curve_add_point(c->transition_curve, p->x[ch][k], p->y[ch][k]);
963 (void)dt_draw_curve_add_point(c->transition_curve, p->x[ch][1] + 1.0, p->y[ch][1]);
964
965 c->mouse_x = c->mouse_y = c->mouse_pick = -1.0;
966 c->dragging = 0;
967 c->x_move = -1;
968 self->timeout_handle = 0;
969 c->mouse_radius = 1.0 / (DT_IOP_RAWDENOISE_BANDS * 2);
970
971 GtkWidget *box_raw = self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
972
973 c->area = GTK_DRAWING_AREA(gtk_drawing_area_new());
974 gtk_widget_set_hexpand(GTK_WIDGET(c->area), TRUE);
975 g_object_set_data(G_OBJECT(c->area), "iop-instance", self);
976
977 gtk_box_pack_start(GTK_BOX(box_raw), GTK_WIDGET(c->channel_tabs), FALSE, FALSE, 0);
978 gtk_box_pack_start(GTK_BOX(box_raw),
979 dt_ui_resizable_drawing_area(GTK_WIDGET(c->area),
980 "plugins/darkroom/rawdenoise/graphheight", 280, 100),
981 FALSE, FALSE, 0);
982
983 gtk_widget_add_events(GTK_WIDGET(c->area), GDK_POINTER_MOTION_MASK | darktable.gui->scroll_mask
984 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
985 | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
986 g_signal_connect(G_OBJECT(c->area), "draw", G_CALLBACK(rawdenoise_draw), self);
987 g_signal_connect(G_OBJECT(c->area), "button-press-event", G_CALLBACK(rawdenoise_button_press), self);
988 g_signal_connect(G_OBJECT(c->area), "button-release-event", G_CALLBACK(rawdenoise_button_release), self);
989 g_signal_connect(G_OBJECT(c->area), "motion-notify-event", G_CALLBACK(rawdenoise_motion_notify), self);
990 g_signal_connect(G_OBJECT(c->area), "leave-notify-event", G_CALLBACK(rawdenoise_leave_notify), self);
991 g_signal_connect(G_OBJECT(c->area), "scroll-event", G_CALLBACK(rawdenoise_scrolled), self);
992
993 c->threshold = dt_bauhaus_slider_from_params(self, "threshold");
994 dt_bauhaus_slider_set_soft_max(c->threshold, 0.1);
995 dt_bauhaus_slider_set_digits(c->threshold, 3);
996
997 // start building top level widget
998 self->widget = gtk_stack_new();
999 gtk_stack_set_homogeneous(GTK_STACK(self->widget), FALSE);
1000
1001 GtkWidget *label_non_raw = dt_ui_label_new(_("raw denoising\nonly works for raw images."));
1002
1003 gtk_stack_add_named(GTK_STACK(self->widget), label_non_raw, "non_raw");
1004 gtk_stack_add_named(GTK_STACK(self->widget), box_raw, "raw");
1005}
1006
1008{
1010 dt_conf_set_int("plugins/darkroom/rawdenoise/gui_channel", c->channel);
1011 dt_draw_curve_destroy(c->transition_curve);
1013
1015}
1016// clang-format off
1017// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1018// vim: shiftwidth=2 expandtab tabstop=2 cindent
1019// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1020// clang-format on
#define SQR(a)
Definition ashift.c:128
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dt_bauhaus_slider_set_digits(GtkWidget *widget, int val)
Definition bauhaus.c:3534
void dt_bauhaus_slider_set_soft_max(GtkWidget *widget, float val)
Definition bauhaus.c:1624
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
@ IOP_CS_RAW
const dt_aligned_pixel_t f
const float threshold
const dt_colormatrix_t dt_aligned_pixel_t out
static const int row
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
dt_image_pipe_class_t dt_image_pipe_class(const dt_image_t *img)
const char * dt_image_pipe_class_name(const dt_image_pipe_class_t klass)
gboolean dt_image_needs_demosaic(const dt_image_t *img)
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:181
#define dt_free_align(ptr)
Definition darktable.h:481
static void * dt_calloc_align(size_t size)
Definition darktable.h:488
#define dt_pixelpipe_cache_alloc_align_float_cache(pixels, id)
Definition darktable.h:447
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
Definition darktable.h:151
#define dt_pixelpipe_cache_free_align(mem)
Definition darktable.h:453
#define __DT_CLONE_TARGETS__
Definition darktable.h:367
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:258
static const dt_aligned_pixel_simd_t value
Definition darktable.h:577
#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:281
static int FCxtrans(const int row, const int col, global const unsigned char(*const xtrans)[6])
static int FC(const int row, const int col, const unsigned int filters)
#define dt_dev_add_history_item(dev, module, enable, redraw)
void dt_iop_params_t
Definition dev_history.h:41
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
__DT_CLONE_TARGETS__ int dwt_denoise(float *const img, const int width, const int height, const int bands, const float *const noise)
Definition dwt.c:513
const dt_collection_filter_flag_t colors[6]
Definition filter.c:295
void default_input_format(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_iop_buffer_dsc_t *dsc)
Definition format.c:57
void dt_iop_buffer_dsc_update_bpp(dt_iop_buffer_dsc_t *dsc)
Definition format.c:28
gboolean dt_gui_get_scroll_unit_deltas(const GdkEventScroll *event, int *delta_x, int *delta_y)
Definition gtk.c:219
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:2836
GtkWidget * dt_ui_notebook_page(GtkNotebook *notebook, const char *text, const char *tooltip)
Definition gtk.c:2259
static cairo_surface_t * dt_cairo_image_surface_create(cairo_format_t format, int width, int height)
Definition gtk.h:316
#define DT_GUI_BOX_SPACING
Definition gtk.h:109
#define DT_PIXEL_APPLY_DPI(value)
Definition gtk.h:90
static GtkWidget * dt_ui_label_new(const gchar *str)
Definition gtk.h:461
void dt_gui_throttle_cancel(gpointer source)
void dt_gui_throttle_queue(gpointer source, dt_gui_throttle_callback_t callback, gpointer user_data)
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:3135
void dt_iop_default_init(dt_iop_module_t *module)
Definition imageop.c:316
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:3141
#define IOP_GUI_FREE
Definition imageop.h:602
#define dt_iop_fmt_log(module, fmt,...)
Debug helper to trace a module's input-format-driven decisions on the -d pipe channel (DT_DEBUG_PIPE)...
Definition imageop.h:453
@ IOP_FLAGS_DEPRECATED
Definition imageop.h:168
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:167
@ IOP_GROUP_REPAIR
Definition imageop.h:140
#define IOP_GUI_ALLOC(module)
Definition imageop.h:599
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
Definition imageop_gui.c:77
void *const ovoid
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
#define M_PI
Definition math.h:45
size_t size
Definition mipmap_cache.c:3
#define DT_IOP_RAWDENOISE_RES
Definition rawdenoise.c:72
void init(dt_iop_module_t *module)
Definition rawdenoise.c:529
static void rawdenoise_tab_switch(GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer user_data)
Definition rawdenoise.c:931
const char ** description(struct dt_iop_module_t *self)
Definition rawdenoise.c:156
int default_group()
Definition rawdenoise.c:170
static gboolean rawdenoise_button_release(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
Definition rawdenoise.c:895
static __DT_CLONE_TARGETS__ void compute_channel_noise(float *const noise, int color, const dt_iop_rawdenoise_data_t *const data)
Definition rawdenoise.c:191
void reload_defaults(dt_iop_module_t *module)
Definition rawdenoise.c:552
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 rawdenoise.c:580
static gboolean rawdenoise_leave_notify(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
Definition rawdenoise.c:907
void gui_update(dt_iop_module_t *self)
Refresh GUI controls from current params and configuration.
Definition rawdenoise.c:626
#define DT_IOP_RAWDENOISE_INSET
Definition rawdenoise.c:71
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition rawdenoise.c:603
const char * name()
Definition rawdenoise.c:151
static gboolean _rawdenoise_supported(const dt_image_t *img)
Definition rawdenoise.c:547
void gui_init(dt_iop_module_t *self)
Definition rawdenoise.c:940
#define BIT16
Definition rawdenoise.c:188
void gui_cleanup(dt_iop_module_t *self)
static __DT_CLONE_TARGETS__ int wavelet_denoise(const float *const restrict in, float *const restrict out, const dt_iop_roi_t *const roi, const dt_iop_rawdenoise_data_t *const data, const uint32_t filters)
Definition rawdenoise.c:227
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition rawdenoise.c:175
void input_format(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_iop_buffer_dsc_t *dsc)
Definition rawdenoise.c:180
int flags()
Definition rawdenoise.c:165
static __DT_CLONE_TARGETS__ int wavelet_denoise_xtrans(const float *const restrict in, float *const restrict out, const dt_iop_roi_t *const restrict roi, const dt_iop_rawdenoise_data_t *const data, const uint8_t(*const xtrans)[6])
Definition rawdenoise.c:337
#define DT_IOP_RAWDENOISE_BANDS
Definition rawdenoise.c:73
static gboolean rawdenoise_scrolled(GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
Definition rawdenoise.c:916
static gboolean rawdenoise_draw(GtkWidget *widget, cairo_t *crf, gpointer user_data)
Definition rawdenoise.c:642
static gboolean rawdenoise_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
Definition rawdenoise.c:861
int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid)
Definition rawdenoise.c:507
gboolean force_enable(struct dt_iop_module_t *self, const gboolean current_state)
Definition rawdenoise.c:568
static float vstransform(const float value)
Definition rawdenoise.c:331
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition rawdenoise.c:618
static gboolean rawdenoise_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
Definition rawdenoise.c:832
static void dt_iop_rawdenoise_get_params(dt_iop_rawdenoise_params_t *p, const int ch, const double mouse_x, const double mouse_y, const float rad)
Definition rawdenoise.c:632
dt_iop_rawdenoise_channel_t
Definition rawdenoise.c:76
@ DT_RAWDENOISE_G
Definition rawdenoise.c:79
@ DT_RAWDENOISE_R
Definition rawdenoise.c:78
@ DT_RAWDENOISE_B
Definition rawdenoise.c:80
@ DT_RAWDENOISE_ALL
Definition rawdenoise.c:77
@ DT_RAWDENOISE_NONE
Definition rawdenoise.c:81
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 rawdenoise.c:121
struct _GtkWidget GtkWidget
Definition splash.h:29
const float uint32_t state[4]
const float noise
int32_t num_openmp_threads
Definition darktable.h:758
struct dt_gui_gtk_t * gui
Definition darktable.h:775
struct dt_bauhaus_t * bauhaus
Definition darktable.h:778
struct dt_develop_t * develop
Definition darktable.h:770
PangoFontDescription * pango_font_desc
Definition bauhaus.h:274
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
struct dt_develop_t * dev
dt_image_t image_storage
Definition develop.h:259
gint scroll_mask
Definition gtk.h:224
int32_t reset
Definition gtk.h:172
uint32_t filters
Definition format.h:60
unsigned int channels
Definition format.h:54
uint8_t xtrans[6][6]
Definition format.h:70
int32_t hide_enable_button
Definition imageop.h:262
dt_iop_params_t * default_params
Definition imageop.h:307
GtkWidget * widget
Definition imageop.h:337
struct dt_develop_t * dev
Definition imageop.h:296
dt_iop_gui_data_t * gui_data
Definition imageop.h:311
guint timeout_handle
Definition imageop.h:371
dt_iop_params_t * params
Definition imageop.h:307
float force[DT_RAWDENOISE_NONE][5]
Definition rawdenoise.c:114
dt_draw_curve_t * curve[DT_RAWDENOISE_NONE]
Definition rawdenoise.c:112
dt_iop_rawdenoise_channel_t channel
Definition rawdenoise.c:113
dt_iop_rawdenoise_channel_t channel
Definition rawdenoise.c:103
dt_iop_rawdenoise_params_t drag_params
Definition rawdenoise.c:100
dt_draw_curve_t * transition_curve
Definition rawdenoise.c:93
float x[DT_RAWDENOISE_NONE][5]
Definition rawdenoise.c:87
float y[DT_RAWDENOISE_NONE][5]
Definition rawdenoise.c:88
Region of interest passed through the pixelpipe.
Definition imageop.h:72
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29