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"
50#include "common/conf.h"
51#endif
52#include "widgets/bauhaus.h"
53#include "system/macros.h"
54#include "system/openmp.h"
56#include "system/mem_alloc.h"
59#include "common/imagebuf.h"
60#include "pixel/dwt.h"
61#include "develop/imageop.h"
63#include "develop/imageop_gui.h"
64#include "develop/dev_history.h" // dt_dev_add_history_item, previously reached through gui/draw.h
65#include "develop/develop.h"
66#include "math/openmp_maths.h"
67
68#include "widgets/draw.h"
69#include "iop/iop_api.h"
70
71#include <gtk/gtk.h>
72#include <stdlib.h>
73#include "widgets/label.h"
74#include "widgets/notebook.h"
75#include "widgets/scroll_wrap.h"
76#include "gui/screen_metrics.h"
77
79
80#define DT_IOP_RAWDENOISE_INSET DT_PIXEL_APPLY_DPI(5)
81#define DT_IOP_RAWDENOISE_RES 64
82#define DT_IOP_RAWDENOISE_BANDS 5
83
92
94{
95 float threshold; // $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.01 $DESCRIPTION: "noise threshold"
99
117
125
129
130int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params,
131 const int new_version)
132{
133 if(old_version == 1 && new_version == 2)
134 {
135 // Since first version, the dt_iop_params_t struct have new members
136 // at the end of the struct.
137 // Yet, the beginning of the struct is exactly the same:
138 // threshold is still the first member of the struct.
139 // This allows to define the variable o with dt_iop_rawdenoise_params_t
140 // as long as we don't try to access new members on o.
141 // In other words, o can be seen as a dt_iop_rawdenoise_params_t
142 // with no allocated space for the new member.
145 n->threshold = o->threshold;
146 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
147 {
148 for(int ch = 0; ch < DT_RAWDENOISE_NONE; ch++)
149 {
150 n->x[ch][k] = k / (DT_IOP_RAWDENOISE_BANDS - 1.0);
151 n->y[ch][k] = 0.5f;
152 }
153 }
154 return 0;
155 }
156 return 1;
157}
158
159
160const char *name()
161{
162 return _("raw denoise");
163}
164
165const char **description(struct dt_iop_module_t *self)
166{
167 return dt_iop_set_description(self, _("denoise the raw picture early in the pipeline"),
168 _("corrective"),
169 _("linear, raw, scene-referred"),
170 _("linear, raw"),
171 _("linear, raw, scene-referred"));
172}
173
178
180{
181 return IOP_GROUP_REPAIR;
182}
183
185{
186 return IOP_CS_RAW;
187}
188
191{
192 default_input_format(self, pipe, piece, dsc);
193 dsc->channels = 1;
195}
196
197#define BIT16 65536.0
198
200static void compute_channel_noise(float *const noise, int color, const dt_iop_rawdenoise_data_t *const data)
201{
202 // note that these constants are the same for X-Trans and Bayer, as they are proportional to image detail on
203 // each channel, not the sensor pattern
204 static const float noise_all[] = { 0.8002, 0.2735, 0.1202, 0.0585, 0.0291, 0.0152, 0.0080, 0.0044 };
205 for(int i = 0; i < DT_IOP_RAWDENOISE_BANDS; i++)
206 {
207 // scale the value from [0,1] to [0,16],
208 // and makes the "0.5" neutral value become 1
209 float chan_threshold_exp_4;
210 switch(color)
211 {
212 case 0:
213 chan_threshold_exp_4 = data->force[DT_RAWDENOISE_R][DT_IOP_RAWDENOISE_BANDS - i - 1];
214 break;
215 case 2:
216 chan_threshold_exp_4 = data->force[DT_RAWDENOISE_B][DT_IOP_RAWDENOISE_BANDS - i - 1];
217 break;
218 default:
219 chan_threshold_exp_4 = data->force[DT_RAWDENOISE_G][DT_IOP_RAWDENOISE_BANDS - i - 1];
220 break;
221 }
222 chan_threshold_exp_4 *= chan_threshold_exp_4;
223 chan_threshold_exp_4 *= chan_threshold_exp_4;
224 // repeat for the overall all-channels thresholds
225 float all_threshold_exp_4 = data->force[DT_RAWDENOISE_ALL][DT_IOP_RAWDENOISE_BANDS - i - 1];
226 all_threshold_exp_4 *= all_threshold_exp_4;
227 all_threshold_exp_4 *= all_threshold_exp_4;
228 noise[i] = noise_all[i] * all_threshold_exp_4 * chan_threshold_exp_4 * 16.0f * 16.0f;
229 // the following multiplication needs to stay separate from the above line, because merging the two changes
230 // the results on the integration test!
231 noise[i] *= data->threshold;
232 }
233}
234
236static int wavelet_denoise(const float *const restrict in, float *const restrict out, const dt_iop_roi_t *const roi,
237 const dt_iop_rawdenoise_data_t * const data, const uint32_t filters)
238{
239 const size_t size = (size_t)(roi->width / 2 + 1) * (roi->height / 2 + 1);
240 float *const restrict fimg = dt_pixelpipe_cache_alloc_align_float_cache(size, 0);
241 if (IS_NULL_PTR(fimg))
242 return 1;
243
244 const int nc = 4;
245 for(int c = 0; c < nc; c++) /* denoise R,G1,B,G3 individually */
246 {
247 const int color = FC(c % 2, c / 2, filters);
250
251 // adjust for odd width and height
252 const int halfwidth = roi->width / 2 + (roi->width & (~(c >> 1)) & 1);
253 const int halfheight = roi->height / 2 + (roi->height & (~c) & 1);
254
255 // collect one of the R/G1/G2/B channels into a monochrome image, applying sqrt() to the values as a
256 // variance-stabilizing transform
258 for(int row = c & 1; row < roi->height; row += 2)
259 {
260 float *const restrict fimgp = fimg + (size_t)row / 2 * halfwidth;
261 const int offset = (c & 2) >> 1;
262 const float *const restrict inp = in + (size_t)row * roi->width + offset;
263 const int senselwidth = (roi->width-offset+1)/2;
264 for(int col = 0; col < senselwidth; col++)
265 fimgp[col] = sqrtf(MAX(0.0f, inp[2*col]));
266 }
267
268 // perform the wavelet decomposition and denoising
269 if(dwt_denoise(fimg, halfwidth, halfheight, DT_IOP_RAWDENOISE_BANDS, noise) != 0)
270 {
272 return 1;
273 }
274
275 // distribute the denoised data back out to the original R/G1/G2/B channel, squaring the resulting values to
276 // undo the original transform
278 for(int row = c & 1; row < roi->height; row += 2)
279 {
280 const float *const restrict fimgp = fimg + (size_t)row / 2 * halfwidth;
281 const int offset = (c & 2) >> 1;
282 float *const restrict outp = out + (size_t)row * roi->width + offset;
283 const int senselwidth = (roi->width-offset+1)/2;
284 for(int col = 0; col < senselwidth; col++)
285 {
286 float d = fimgp[col];
287 outp[2*col] = d * d;
288 }
289 }
290 }
291#if 0
292 /* FIXME: Haven't ported this part yet */
293 float maximum = 1.0; /* FIXME */
294 float black = 0.0; /* FIXME */
295 maximum *= BIT16;
296 black *= BIT16;
297 for (c=0; c<4; c++)
298 cblack[c] *= BIT16;
299 if (filters && colors == 3) /* pull G1 and G3 closer together */
300 {
301 float *window[4];
302 int wlast, blk[2];
303 float mul[2];
304 float thold = threshold/512;
305 for (row=0; row < 2; row++)
306 {
307 mul[row] = 0.125 * pre_mul[FC(row+1,0) | 1] / pre_mul[FC(row,0) | 1];
308 blk[row] = cblack[FC(row,0) | 1];
309 }
310 for (i=0; i < 4; i++)
311 window[i] = fimg + width*i;
312 for (wlast=-1, row=1; row < height-1; row++)
313 {
314 while (wlast < row+1)
315 {
316 for (wlast++, i=0; i < 4; i++)
317 window[(i+3) & 3] = window[i];
318 for (col = FC(wlast,1) & 1; col < width; col+=2)
319 window[2][col] = BAYER(wlast,col);
320 }
321 for (col = (FC(row,0) & 1)+1; col < width-1; col+=2)
322 {
323 float avg = ( window[0][col-1] + window[0][col+1] +
324 window[2][col-1] + window[2][col+1] - blk[~row & 1]*4 )
325 * mul[row & 1] + (window[1][col] + blk[row & 1]) * 0.5;
326 avg = avg > 0 ? sqrtf(avg) : 0;
327 float diff = sqrtf(BAYER(row,col)) - avg;
328 if (diff < -thold) diff += thold;
329 else if (diff > thold) diff -= thold;
330 else diff = 0;
331 BAYER(row,col) = SQR(avg+diff);
332 }
333 }
334 }
335#endif
337 return 0;
338}
339
340static inline float vstransform(const float value)
341{
342 return sqrtf(MAX(0.0f, value));
343}
344
346static int wavelet_denoise_xtrans(const float *const restrict in, float *const restrict out,
347 const dt_iop_roi_t *const restrict roi,
348 const dt_iop_rawdenoise_data_t *const data, const uint8_t (*const xtrans)[6])
349{
350 const int width = roi->width;
351 const int height = roi->height;
352 const size_t size = (size_t)width * height;
353 // allocate a buffer for the particular color channel to be denoise; we add two rows to simplify the
354 // channel-extraction code (no special case for top/bottom row)
355 float *const img = dt_pixelpipe_cache_alloc_align_float_cache((size_t)width * (height+2), 0);
356 if (IS_NULL_PTR(img))
357 {
358 // we ran out of memory, so just pass through the image without denoising
359 memcpy(out, in, sizeof(float) * size);
360 return 1;
361 }
362 float *const fimg = img + width; // point at the actual color channel contents in the buffer
363
364 for(int c = 0; c < 3; c++)
365 {
368
369 // ensure a defined value for every pixel in the top and bottom rows, even if they are more than
370 // one pixel away from the nearest neighbor of the same color and thus the simple interpolation
371 // used in the following loop does not set them
372 for (size_t col = 0; col < width; col++)
373 {
374 fimg[col] = 0.5f;
375 fimg[(size_t)(height-1)*width + col] = 0.5f;
376 }
377 const size_t nthreads = dt_get_num_openmp_threads(); // go direct, dt_get_num_openmp_threads() always returns numprocs
378 const size_t chunksize = (height + nthreads - 1) / nthreads;
379 __OMP_PARALLEL_FOR__(num_threads(nthreads) )
380 for(size_t chunk = 0; chunk < nthreads; chunk++)
381 {
382 const size_t start = chunk * chunksize;
383 const size_t pastend = MIN(start + chunksize,height);
384 for(size_t row = start; row < pastend; row++)
385 {
386 const float *const restrict inp = in + row * width;
387 float *const restrict fimgp = fimg + row * width;
388 // handle red/blue pixel in first column
389 if (c != 1 && FCxtrans(row, 0, roi, xtrans) == c)
390 {
391 // copy to neighbors above and right
392 const float d = vstransform(inp[0]);
393 fimgp[0] = fimgp[-width] = fimgp[-width+1] = d;
394 }
395 for(size_t col = (c != 1); col < width-1; col++)
396 {
397 if (FCxtrans(row, col, roi, xtrans) == c)
398 {
399 // the pixel at the current location has the desired color, so apply sqrt() as a variance-stablizing
400 // transform, and then do cheap nearest-neighbor interpolation by copying it to appropriate neighbors
401 const float d = vstransform(inp[col]);
402 fimgp[col] = d;
403 if (c == 1) // green pixel
404 {
405 // Copy to the right and down. The X-Trans color layout is such that copying to those two neighbors
406 // results in all positions being filled except in the left-most and right-most columns and sometimes
407 // the topmost and bottom-most rows (depending on how the ROI aligns with the CFA).
408 fimgp[col+1] = fimgp[col+width] = d;
409 }
410 else // red or blue pixel
411 {
412 // Copy value to all eight neighbors; it's OK to copy to the row above even when we're in row 0 (or
413 // the row below when in the last row) because the destination is sandwiched between other buffers
414 // that will be overwritten afterwards anyway. We need to copy to all adjacent positions because
415 // there may be two green pixels between nearest red/red or blue/blue, so each will cover one of the
416 // greens.
417 fimgp[col-width-1] = fimgp[col-width] = fimgp[col-width+1] = d; // row above
418 fimgp[col-1] = fimgp[col+1] = d; // left and right
419 if (row < pastend-1)
420 fimgp[col+width-1] = fimgp[col+width] = fimgp[col+width+1] = d; // row below
421 }
422 }
423 }
424 // leftmost and rightmost pixel in the row may still need to be filled in from a neighbor
425 if (FCxtrans(row, 0, roi, xtrans) != c)
426 {
427 int src = 0; // fallback is current sensel even if it has the wrong color
428 if (row > 1 && FCxtrans(row-1, 0, roi, xtrans) == c)
429 src = -width;
430 else if (FCxtrans(row, 1, roi, xtrans) == c)
431 src = 1;
432 else if (row > 1 && FCxtrans(row-1, 1, roi, xtrans) == c)
433 src = -width + 1;
434 fimgp[0] = vstransform(inp[src]);
435 }
436 // check the right-most pixel; if it's the desired color and not green, copy it to the neighbors
437 if (c != 1 && FCxtrans(row, width-1, roi, xtrans) == c)
438 {
439 // copy to neighbors above and left
440 const float d = vstransform(inp[width-1]);
441 fimgp[width-2] = fimgp[width-1] = fimgp[-1] = d;
442 }
443 else if (FCxtrans(row, width-1, roi, xtrans) != c)
444 {
445 int src = width-1; // fallback is current sensel even if it has the wrong color
446 if (FCxtrans(row, width-2, roi, xtrans) == c)
447 src = width-2;
448 else if (row > 1 && FCxtrans(row-1, width-1, roi, xtrans) == c)
449 src = -1;
450 else if (row > 1 && FCxtrans(row-1, width-2, roi, xtrans) == c)
451 src = -2;
452 fimgp[width-1] = vstransform(inp[src]);
453 }
454 }
455 if (pastend < height)
456 {
457 // Another slice follows us, and by updating the last row of our slice, we've clobbered values that
458 // were previously written by the other thread. Restore them.
459 const float *const restrict inp = in + pastend * width;
460 float *const restrict fimgp = fimg + pastend * width;
461 for (size_t col = 0; col < width-1; col++)
462 {
463 if (FCxtrans(pastend, col, roi, xtrans) == c)
464 {
465 const float d = vstransform(inp[col]);
466 if (c == 1) // green pixel
467 {
468 if (FCxtrans(pastend, col+1, roi, xtrans) != c)
469 fimgp[col] = fimgp[col+1] = d; // copy to the right
470 }
471 else // red/blue pixel
472 {
473 // copy the pixel's adjusted value to the prior row and left and right (if not at edge)
474 fimgp[col-width] = fimgp[col-width+1] = d;
475 if (col > 0) fimgp[col-width-1] = d;
476 }
477 }
478 // some red and blue values may need to be restored from the row TWO past the end of our slice
479 if (c != 1 && pastend+1 < height && FCxtrans(pastend+1, col, roi, xtrans) == c)
480 {
481 const float d = vstransform(inp[col+width]);
482 fimgp[col] = fimgp[col+1] = d;
483 if (col > 0) fimgp[col-1] = d;
484 }
485 }
486 }
487 }
488
489 // perform the wavelet decomposition and denoising
491 {
493 return 1;
494 }
495
496 // distribute the denoised data back out to the original R/G/B channel, squaring the resulting values to
497 // undo the original transform
499 for(int row = 0; row < height; row++)
500 {
501 const float *const restrict fimgp = fimg + (size_t)row * width;
502 float *const restrict outp = out + (size_t)row * width;
503 for(int col = 0; col < width; col++)
504 if(FCxtrans(row, col, roi, xtrans) == c)
505 {
506 float d = fimgp[col];
507 outp[col] = d * d;
508 }
509 }
510 }
511
513 return 0;
514}
515
516int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
517 void *const ovoid)
518{
519 const dt_iop_roi_t *const roi_in = &piece->roi_in;
520 const dt_iop_rawdenoise_data_t *const restrict d = (dt_iop_rawdenoise_data_t *)piece->data;
521
522 if(!(d->threshold > 0.0f))
523 {
524 dt_iop_image_copy_by_size(ovoid, ivoid, roi_in->width, roi_in->height, 1);
525 }
526 else
527 {
528 const uint32_t filters = piece->dsc_in.filters;
529 const uint8_t(*const xtrans)[6] = (const uint8_t(*const)[6])piece->dsc_in.xtrans;
530 if (filters != 9u)
531 return wavelet_denoise(ivoid, ovoid, roi_in, d, filters);
532 else
533 return wavelet_denoise_xtrans(ivoid, ovoid, roi_in, d, xtrans);
534 }
535 return 0;
536}
537
539{
540 dt_iop_default_init(module);
541
542 dt_iop_rawdenoise_params_t *d = module->default_params;
543
544 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
545 {
546 for(int ch = 0; ch < DT_RAWDENOISE_NONE; ch++)
547 {
548 d->x[ch][k] = k / (DT_IOP_RAWDENOISE_BANDS - 1.f);
549 }
550 }
551}
552
553/* Single source of truth: raw CFA wavelet denoise operates on the mosaiced buffer, so it needs a
554 * CFA (needs_demosaic), not merely the RAW flag. An already-demosaiced raw (sRAW / linear DNG) has
555 * no mosaic to denoise. Shared by reload_defaults() and force_enable(). */
556static gboolean _rawdenoise_supported(const dt_image_t *img)
557{
558 return dt_image_needs_demosaic(img);
559}
560
562{
563 const dt_image_t *const img = &module->dev->image_storage;
564 module->hide_enable_button = !_rawdenoise_supported(img);
565 dt_iop_fmt_log(module, "reload_defaults: class=%s needs_demosaic=%d -> hide_enable=%d",
567 module->hide_enable_button);
568
569 // Stack visibility (raw vs non_raw) is applied from hide_enable_button in gui_update(), on the GUI
570 // thread when the widget exists; reload_defaults() stays params-only.
571 module->default_enabled = 0;
572}
573
574gboolean force_enable(struct dt_iop_module_t *self, const gboolean current_state)
575{
576 // History sanitization: a rawdenoise entry pasted onto a non-mosaic image is forced off here,
577 // at history-read time, rather than patched on the pipeline node in commit_params().
578 const gboolean active = _rawdenoise_supported(&self->dev->image_storage);
579 const gboolean state = current_state && active;
580 dt_iop_fmt_log(self, "force_enable: class=%s supported=%d current=%d -> %d",
582 active, current_state, state);
583 return state;
584}
585
588{
591
592 d->threshold = p->threshold;
593
594 for(int ch = 0; ch < DT_RAWDENOISE_NONE; ch++)
595 {
596 dt_draw_curve_set_point(d->curve[ch], 0, p->x[ch][DT_IOP_RAWDENOISE_BANDS - 2] - 1.0, p->y[ch][0]);
597 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
598 dt_draw_curve_set_point(d->curve[ch], k, p->x[ch][k], p->y[ch][k]);
599 dt_draw_curve_set_point(d->curve[ch], DT_IOP_RAWDENOISE_BANDS + 1, p->x[ch][1] + 1.0,
600 p->y[ch][DT_IOP_RAWDENOISE_BANDS - 1]);
601 dt_draw_curve_calc_values(d->curve[ch], 0.0, 1.0, DT_IOP_RAWDENOISE_BANDS, NULL, d->force[ch]);
602 }
603
604 // Image-type gating handled at history level by force_enable()/reload_defaults().
605 dt_iop_fmt_log(self, "commit: class=%s enabled=%d",
607}
608
610{
613
614 piece->data = (void *)d;
615 piece->data_size = sizeof(dt_iop_rawdenoise_data_t);
616 // Checked AFTER both piece->data and piece->data_size are set, deliberately.
617 // dt_iop_init_pipe() disables the node for us when it sees data_size > 0 with a NULL
618 // data -- returning before data_size is assigned skips that and leaves the node enabled
619 // with no storage. dt_calloc_align() returns NULL on failure and pipe nodes are built
620 // exactly when memory is tightest (Sentry 134134395 faulted in atrous's init_pipe).
621 if(IS_NULL_PTR(piece->data)) return;
622 for(int ch = 0; ch < DT_RAWDENOISE_NONE; ch++)
623 {
624 d->curve[ch] = dt_draw_curve_new(0.0, 1.0, CATMULL_ROM);
625 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
626 (void)dt_draw_curve_add_point(d->curve[ch], default_params->x[ch][k], default_params->y[ch][k]);
627 }
628}
629
631{
632 /* init_pipe() may have failed to allocate, and cleanup runs regardless. */
633 if(IS_NULL_PTR(piece->data)) return;
635 for(int ch = 0; ch < DT_RAWDENOISE_NONE; ch++) dt_draw_curve_destroy(d->curve[ch]);
636 dt_free_align(piece->data);
637 piece->data = NULL;
638}
639
641{
642 // raw vs non_raw page, from the per-image support flag computed by reload_defaults()
643 gtk_stack_set_visible_child_name(GTK_STACK(self->gui->widget), self->hide_enable_button ? "non_raw" : "raw");
644 gtk_widget_queue_draw(self->gui->widget);
645}
646
647static void dt_iop_rawdenoise_get_params(dt_iop_rawdenoise_params_t *p, const int ch, const double mouse_x,
648 const double mouse_y, const float rad)
649{
650 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
651 {
652 const float f = expf(-(mouse_x - p->x[ch][k]) * (mouse_x - p->x[ch][k]) / (rad * rad));
653 p->y[ch][k] = (1 - f) * p->y[ch][k] + f * mouse_y;
654 }
655}
656
657static gboolean rawdenoise_draw(GtkWidget *widget, cairo_t *crf, gpointer user_data)
658{
659 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
662
663 int ch = (int)c->channel;
664 dt_draw_curve_set_point(c->transition_curve, 0, p.x[ch][DT_IOP_RAWDENOISE_BANDS - 2] - 1.0, p.y[ch][0]);
665 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
666 dt_draw_curve_set_point(c->transition_curve, k + 1, p.x[ch][k], p.y[ch][k]);
667 dt_draw_curve_set_point(c->transition_curve, DT_IOP_RAWDENOISE_BANDS + 1, p.x[ch][1] + 1.0,
669
670 const int inset = DT_IOP_RAWDENOISE_INSET;
671 GtkAllocation allocation;
672 gtk_widget_get_allocation(widget, &allocation);
673 int width = allocation.width, height = allocation.height;
674 cairo_surface_t *cst = dt_cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
675 cairo_t *cr = cairo_create(cst);
676 cairo_set_source_rgb(cr, .2, .2, .2);
677
678 cairo_paint(cr);
679
680 cairo_translate(cr, inset, inset);
681 width -= 2 * inset;
682 height -= 2 * inset;
683
684 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.0));
685 cairo_set_source_rgb(cr, .1, .1, .1);
686 cairo_rectangle(cr, 0, 0, width, height);
687 cairo_stroke(cr);
688
689 cairo_set_source_rgb(cr, .3, .3, .3);
690 cairo_rectangle(cr, 0, 0, width, height);
691 cairo_fill(cr);
692
693 // draw grid
694 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(.4));
695 cairo_set_source_rgb(cr, .1, .1, .1);
696 dt_draw_grid(cr, 8, 0, 0, width, height);
697
698 if(c->mouse_y > 0 || c->dragging)
699 {
700 // draw min/max curves:
701 dt_iop_rawdenoise_get_params(&p, c->channel, c->mouse_x, 1., c->mouse_radius);
702 dt_draw_curve_set_point(c->transition_curve, 0, p.x[ch][DT_IOP_RAWDENOISE_BANDS - 2] - 1.0, p.y[ch][0]);
703 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
704 dt_draw_curve_set_point(c->transition_curve, k + 1, p.x[ch][k], p.y[ch][k]);
705 dt_draw_curve_set_point(c->transition_curve, DT_IOP_RAWDENOISE_BANDS + 1, p.x[ch][1] + 1.0,
707 dt_draw_curve_calc_values(c->transition_curve, 0.0, 1.0, DT_IOP_RAWDENOISE_RES, c->draw_min_xs, c->draw_min_ys);
708
710 dt_iop_rawdenoise_get_params(&p, c->channel, c->mouse_x, .0, c->mouse_radius);
711 dt_draw_curve_set_point(c->transition_curve, 0, p.x[ch][DT_IOP_RAWDENOISE_BANDS - 2] - 1.0, p.y[ch][0]);
712 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
713 dt_draw_curve_set_point(c->transition_curve, k + 1, p.x[ch][k], p.y[ch][k]);
714 dt_draw_curve_set_point(c->transition_curve, DT_IOP_RAWDENOISE_BANDS + 1, p.x[ch][1] + 1.0,
716 dt_draw_curve_calc_values(c->transition_curve, 0.0, 1.0, DT_IOP_RAWDENOISE_RES, c->draw_max_xs, c->draw_max_ys);
717 }
718
719 cairo_save(cr);
720
721 // draw selected cursor
722 cairo_translate(cr, 0, height);
723
724 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
725 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(2.));
726
727 for(int i = 0; i < DT_RAWDENOISE_NONE; i++)
728 {
729 // draw curves, selected last
730 ch = ((int)c->channel + i + 1) % DT_RAWDENOISE_NONE;
731 float alpha = 0.3;
732 if(i == DT_RAWDENOISE_NONE - 1) alpha = 1.0;
733 switch(ch)
734 {
735 case 0:
736 cairo_set_source_rgba(cr, .7, .7, .7, alpha);
737 break;
738 case 1:
739 cairo_set_source_rgba(cr, .7, .1, .1, alpha);
740 break;
741 case 2:
742 cairo_set_source_rgba(cr, .1, .7, .1, alpha);
743 break;
744 case 3:
745 cairo_set_source_rgba(cr, .1, .1, .7, alpha);
746 break;
747 }
748
750 dt_draw_curve_set_point(c->transition_curve, 0, p.x[ch][DT_IOP_RAWDENOISE_BANDS - 2] - 1.0, p.y[ch][0]);
751 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
752 dt_draw_curve_set_point(c->transition_curve, k + 1, p.x[ch][k], p.y[ch][k]);
753 dt_draw_curve_set_point(c->transition_curve, DT_IOP_RAWDENOISE_BANDS + 1, p.x[ch][1] + 1.0,
755 dt_draw_curve_calc_values(c->transition_curve, 0.0, 1.0, DT_IOP_RAWDENOISE_RES, c->draw_xs, c->draw_ys);
756 cairo_move_to(cr, 0 * width / (float)(DT_IOP_RAWDENOISE_RES - 1), -height * c->draw_ys[0]);
757 for(int k = 1; k < DT_IOP_RAWDENOISE_RES; k++)
758 cairo_line_to(cr, k * width / (float)(DT_IOP_RAWDENOISE_RES - 1), -height * c->draw_ys[k]);
759 cairo_stroke(cr);
760 }
761
762 ch = c->channel;
763 // draw dots on knots
764 cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
765 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.));
766 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
767 {
768 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);
769 if(c->x_move == k)
770 cairo_fill(cr);
771 else
772 cairo_stroke(cr);
773 }
774
775 if(c->mouse_y > 0 || c->dragging)
776 {
777 // draw min/max, if selected
778 cairo_set_source_rgba(cr, .7, .7, .7, .6);
779 cairo_move_to(cr, 0, -height * c->draw_min_ys[0]);
780 for(int k = 1; k < DT_IOP_RAWDENOISE_RES; k++)
781 cairo_line_to(cr, k * width / (float)(DT_IOP_RAWDENOISE_RES - 1), -height * c->draw_min_ys[k]);
782 for(int k = DT_IOP_RAWDENOISE_RES - 1; k >= 0; k--)
783 cairo_line_to(cr, k * width / (float)(DT_IOP_RAWDENOISE_RES - 1), -height * c->draw_max_ys[k]);
784 cairo_close_path(cr);
785 cairo_fill(cr);
786 // draw mouse focus circle
787 cairo_set_source_rgba(cr, .9, .9, .9, .5);
788 const float pos = DT_IOP_RAWDENOISE_RES * c->mouse_x;
789 int k = (int)pos;
790 const float f = k - pos;
792 float ht = -height * (f * c->draw_ys[k] + (1 - f) * c->draw_ys[k + 1]);
793 cairo_arc(cr, c->mouse_x * width, ht, c->mouse_radius * width, 0, 2. * M_PI);
794 cairo_stroke(cr);
795 }
796
797 cairo_restore(cr);
798
799 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
800
801 // draw labels:
802 PangoLayout *layout;
803 PangoRectangle ink;
804 PangoFontDescription *desc = pango_font_description_copy_static(dt_bauhaus_get_global()->pango_font_desc);
805 pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD);
806 pango_font_description_set_absolute_size(desc, (.08 * height) * PANGO_SCALE);
807 layout = pango_cairo_create_layout(cr);
808 pango_layout_set_font_description(layout, desc);
809 cairo_set_source_rgb(cr, .1, .1, .1);
810
811 pango_layout_set_text(layout, _("coarse"), -1);
812 pango_layout_get_pixel_extents(layout, &ink, NULL);
813 cairo_move_to(cr, .02 * width - ink.y, .5 * (height + ink.width));
814 cairo_save(cr);
815 cairo_rotate(cr, -M_PI * .5f);
816 pango_cairo_show_layout(cr, layout);
817 cairo_restore(cr);
818
819 pango_layout_set_text(layout, _("fine"), -1);
820 pango_layout_get_pixel_extents(layout, &ink, NULL);
821 cairo_move_to(cr, .98 * width - ink.height, .5 * (height + ink.width));
822 cairo_save(cr);
823 cairo_rotate(cr, -M_PI * .5f);
824 pango_cairo_show_layout(cr, layout);
825 cairo_restore(cr);
826
827
828 pango_layout_set_text(layout, _("smooth"), -1);
829 pango_layout_get_pixel_extents(layout, &ink, NULL);
830 cairo_move_to(cr, .5 * (width - ink.width), .08 * height - ink.height);
831 pango_cairo_show_layout(cr, layout);
832
833 pango_layout_set_text(layout, _("noisy"), -1);
834 pango_layout_get_pixel_extents(layout, &ink, NULL);
835 cairo_move_to(cr, .5 * (width - ink.width), .97 * height - ink.height);
836 pango_cairo_show_layout(cr, layout);
837
838 pango_font_description_free(desc);
839 g_object_unref(layout);
840 cairo_destroy(cr);
841 cairo_set_source_surface(crf, cst, 0, 0);
842 cairo_paint(crf);
843 cairo_surface_destroy(cst);
844 return TRUE;
845}
846
847static gboolean rawdenoise_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
848{
849 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
852 const int inset = DT_IOP_RAWDENOISE_INSET;
853 GtkAllocation allocation;
854 gtk_widget_get_allocation(widget, &allocation);
855 int height = allocation.height - 2 * inset, width = allocation.width - 2 * inset;
856 if(!c->dragging) c->mouse_x = CLAMP(event->x - inset, 0, width) / (float)width;
857 c->mouse_y = 1.0 - CLAMP(event->y - inset, 0, height) / (float)height;
858 if(c->dragging)
859 {
860 *p = c->drag_params;
861 if(c->x_move < 0)
862 {
863 dt_iop_rawdenoise_get_params(p, c->channel, c->mouse_x, c->mouse_y + c->mouse_pick, c->mouse_radius);
864 }
865 gtk_widget_queue_draw(widget);
866 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
867 }
868 else
869 {
870 c->x_move = -1;
871 gtk_widget_queue_draw(widget);
872 }
873 return TRUE;
874}
875
876static gboolean rawdenoise_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
877{
878 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
880 const int ch = c->channel;
881 if(event->button == 1 && event->type == GDK_2BUTTON_PRESS)
882 {
883 // reset current curve
886 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
887 {
888 p->x[ch][k] = d->x[ch][k];
889 p->y[ch][k] = d->y[ch][k];
890 }
891 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
892 gtk_widget_queue_draw(self->gui->widget);
893 }
894 else if(event->button == 1)
895 {
896 c->drag_params = *(dt_iop_rawdenoise_params_t *)self->params;
897 const int inset = DT_IOP_RAWDENOISE_INSET;
898 GtkAllocation allocation;
899 gtk_widget_get_allocation(widget, &allocation);
900 int height = allocation.height - 2 * inset, width = allocation.width - 2 * inset;
901 c->mouse_pick
902 = dt_draw_curve_calc_value(c->transition_curve, CLAMP(event->x - inset, 0, width) / (float)width);
903 c->mouse_pick -= 1.0 - CLAMP(event->y - inset, 0, height) / (float)height;
904 c->dragging = 1;
905 return TRUE;
906 }
907 return FALSE;
908}
909
910static gboolean rawdenoise_button_release(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
911{
912 if(event->button == 1)
913 {
914 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
916 c->dragging = 0;
917 return TRUE;
918 }
919 return FALSE;
920}
921
922static gboolean rawdenoise_leave_notify(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
923{
924 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
926 if(!c->dragging) c->mouse_y = -1.0;
927 gtk_widget_queue_draw(widget);
928 return TRUE;
929}
930
931static gboolean rawdenoise_scrolled(GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
932{
933 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
935
936 int delta_y;
937 if(dt_gui_get_scroll_unit_deltas(event, NULL, &delta_y))
938 {
939 c->mouse_radius = CLAMP(c->mouse_radius * (1.0 + 0.1 * delta_y), 0.2 / DT_IOP_RAWDENOISE_BANDS, 1.0);
940 gtk_widget_queue_draw(widget);
941 }
942
943 return TRUE;
944}
945
946static void rawdenoise_tab_switch(GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer user_data)
947{
948 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
949 if(dt_gui_widgets_suppressed()) return;
951 c->channel = (dt_iop_rawdenoise_channel_t)page_num;
952 gtk_widget_queue_draw(self->gui->widget);
953}
954
956{
959
960 c->channel = dt_conf_get_int("plugins/darkroom/rawdenoise/gui_channel");
961 c->channel_tabs = GTK_NOTEBOOK(gtk_notebook_new());
962
963 dt_ui_notebook_page(c->channel_tabs, N_("all"), NULL);
964 dt_ui_notebook_page(c->channel_tabs, N_("R"), NULL);
965 dt_ui_notebook_page(c->channel_tabs, N_("G"), NULL);
966 dt_ui_notebook_page(c->channel_tabs, N_("B"), NULL);
967
968 gtk_widget_show(gtk_notebook_get_nth_page(c->channel_tabs, c->channel));
969 gtk_notebook_set_current_page(c->channel_tabs, c->channel);
970 g_signal_connect(G_OBJECT(c->channel_tabs), "switch_page", G_CALLBACK(rawdenoise_tab_switch), self);
971
972 const int ch = (int)c->channel;
973 c->transition_curve = dt_draw_curve_new(0.0, 1.0, CATMULL_ROM);
974 (void)dt_draw_curve_add_point(c->transition_curve, p->x[ch][DT_IOP_RAWDENOISE_BANDS - 2] - 1.0,
975 p->y[ch][DT_IOP_RAWDENOISE_BANDS - 2]);
976 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
977 (void)dt_draw_curve_add_point(c->transition_curve, p->x[ch][k], p->y[ch][k]);
978 (void)dt_draw_curve_add_point(c->transition_curve, p->x[ch][1] + 1.0, p->y[ch][1]);
979
980 c->mouse_x = c->mouse_y = c->mouse_pick = -1.0;
981 c->dragging = 0;
982 c->x_move = -1;
983 self->gui->timeout_handle = 0;
984 c->mouse_radius = 1.0 / (DT_IOP_RAWDENOISE_BANDS * 2);
985
986 GtkWidget *box_raw = self->gui->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
987
988 c->area = GTK_DRAWING_AREA(gtk_drawing_area_new());
989 gtk_widget_set_hexpand(GTK_WIDGET(c->area), TRUE);
990 g_object_set_data(G_OBJECT(c->area), "iop-instance", self);
991
992 gtk_box_pack_start(GTK_BOX(box_raw), GTK_WIDGET(c->channel_tabs), FALSE, FALSE, 0);
993 gtk_box_pack_start(GTK_BOX(box_raw),
994 dt_ui_resizable_drawing_area(GTK_WIDGET(c->area),
995 "plugins/darkroom/rawdenoise/graphheight", 280, 100),
996 FALSE, FALSE, 0);
997
998 gtk_widget_add_events(GTK_WIDGET(c->area), GDK_POINTER_MOTION_MASK | dt_widget_scroll_mask()
999 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
1000 | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
1001 g_signal_connect(G_OBJECT(c->area), "draw", G_CALLBACK(rawdenoise_draw), self);
1002 g_signal_connect(G_OBJECT(c->area), "button-press-event", G_CALLBACK(rawdenoise_button_press), self);
1003 g_signal_connect(G_OBJECT(c->area), "button-release-event", G_CALLBACK(rawdenoise_button_release), self);
1004 g_signal_connect(G_OBJECT(c->area), "motion-notify-event", G_CALLBACK(rawdenoise_motion_notify), self);
1005 g_signal_connect(G_OBJECT(c->area), "leave-notify-event", G_CALLBACK(rawdenoise_leave_notify), self);
1006 g_signal_connect(G_OBJECT(c->area), "scroll-event", G_CALLBACK(rawdenoise_scrolled), self);
1007
1008 c->threshold = dt_bauhaus_slider_from_params(self, "threshold");
1009 dt_bauhaus_slider_set_soft_max(c->threshold, 0.1);
1010 dt_bauhaus_slider_set_digits(c->threshold, 3);
1011
1012 // start building top level widget
1013 self->gui->widget = gtk_stack_new();
1014 gtk_stack_set_homogeneous(GTK_STACK(self->gui->widget), FALSE);
1015
1016 GtkWidget *label_non_raw = dt_ui_label_new(_("raw denoising\nonly works for raw images."));
1017
1018 gtk_stack_add_named(GTK_STACK(self->gui->widget), label_non_raw, "non_raw");
1019 gtk_stack_add_named(GTK_STACK(self->gui->widget), box_raw, "raw");
1020}
1021
1023{
1025 dt_conf_set_int("plugins/darkroom/rawdenoise/gui_channel", c->channel);
1026 dt_draw_curve_destroy(c->transition_curve);
1027
1029}
1030// clang-format off
1031// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1032// vim: shiftwidth=2 expandtab tabstop=2 cindent
1033// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1034// clang-format on
#define SQR(a)
Definition ashift.c:132
#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:3343
void dt_bauhaus_slider_set_soft_max(GtkWidget *widget, float val)
Definition bauhaus.c:1474
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
@ IOP_CS_RAW
const float f
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
const float threshold
const dt_colormatrix_t dt_aligned_pixel_t out
static const int row
void dt_conf_set_int(const char *name, int val)
int dt_conf_get_int(const char *name)
Integer for name, clamped to the bounds declared in the XML.
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)
#define CATMULL_ROM
Definition curve_tools.h:35
int dt_get_num_openmp_threads(void)
Number of OpenMP threads the application decided to use.
Definition darktable.c:518
struct dt_bauhaus_t * dt_bauhaus_get_global(void)
Definition darktable.c:646
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:43
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)
GtkWidget * window
static void dt_draw_curve_calc_values(dt_draw_curve_t *c, const float min, const float max, const int res, float *x, float *y)
Definition draw.h:324
static void dt_draw_grid(cairo_t *cr, const int num, const int left, const int top, const int right, const int bottom)
Definition draw.h:158
static float dt_draw_curve_calc_value(dt_draw_curve_t *c, const float x)
Definition draw.h:360
static void dt_draw_curve_destroy(dt_draw_curve_t *c)
Definition draw.h:297
static void dt_draw_curve_set_point(dt_draw_curve_t *c, const int num, const float x, const float y)
Definition draw.h:303
static int dt_draw_curve_add_point(dt_draw_curve_t *c, const float x, const float y)
Definition draw.h:379
static dt_draw_curve_t * dt_draw_curve_new(const float min, const float max, unsigned int type)
Definition draw.h:281
__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:516
const dt_collection_filter_flag_t colors[6]
Definition filter.c:302
GdkRGBA color[]
Definition geotagging.c:541
static void dt_iop_image_copy_by_size(float *const __restrict__ out, const float *const __restrict__ in, const size_t width, const size_t height, const size_t ch)
Definition imagebuf.h:91
void dt_iop_default_init(dt_iop_module_t *module)
Definition imageop.c:308
const char ** dt_iop_set_description(dt_iop_module_t *module, const char *main_text, const char *purpose, const char *input, const char *process, const char *output)
Definition imageop.c:1893
#define dt_iop_fmt_log(module, fmt,...)
Debug helper to trace a module's input-format-driven decisions on the -d pipe channel (DT_DEBUG_PIPE)...
Definition imageop.h:535
@ IOP_FLAGS_DEPRECATED
Definition imageop.h:187
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:186
@ IOP_GROUP_REPAIR
Definition imageop.h:159
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
#define IOP_GUI_FREE
Definition imageop_gui.h:96
static dt_iop_gui_data_t * dt_iop_gui_data(const struct dt_iop_module_t *m)
The module's GUI data blob, NULL-safe for headless callers: IOP process() implementations read it for...
Definition imageop_gui.h:81
#define IOP_GUI_ALLOC(module)
Definition imageop_gui.h:93
void *const ovoid
GtkWidget * dt_ui_label_new(const gchar *str)
Definition label.c:125
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
#define M_PI
Definition math.h:47
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
Definition mem_alloc.h:214
static void * dt_calloc_align(size_t size)
dt_alloc_align() followed by a zero fill.
Definition mem_alloc.h:225
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
size_t size
Definition mipmap_cache.c:3
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
DT_MODULE() for a module whose params struct is introspected.
GtkWidget * dt_ui_notebook_page(GtkNotebook *notebook, const char *text, const char *tooltip)
Definition notebook.c:88
#define __OMP_PARALLEL_FOR__(...)
Definition openmp.h:95
void dt_iop_buffer_dsc_update_bpp(dt_iop_buffer_dsc_t *dsc)
#define dt_pixelpipe_cache_alloc_align_float_cache(pixels, id)
#define dt_pixelpipe_cache_free_align(mem)
#define DT_IOP_RAWDENOISE_RES
Definition rawdenoise.c:81
void init(dt_iop_module_t *module)
Definition rawdenoise.c:538
static void rawdenoise_tab_switch(GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer user_data)
Definition rawdenoise.c:946
const char ** description(struct dt_iop_module_t *self)
Definition rawdenoise.c:165
int default_group()
Definition rawdenoise.c:179
static gboolean rawdenoise_button_release(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
Definition rawdenoise.c:910
static __DT_CLONE_TARGETS__ void compute_channel_noise(float *const noise, int color, const dt_iop_rawdenoise_data_t *const data)
Definition rawdenoise.c:200
void reload_defaults(dt_iop_module_t *module)
Definition rawdenoise.c:561
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:586
static gboolean rawdenoise_leave_notify(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
Definition rawdenoise.c:922
void gui_update(dt_iop_module_t *self)
Refresh GUI controls from current params and configuration.
Definition rawdenoise.c:640
#define DT_IOP_RAWDENOISE_INSET
Definition rawdenoise.c:80
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition rawdenoise.c:609
const char * name()
Definition rawdenoise.c:160
static gboolean _rawdenoise_supported(const dt_image_t *img)
Definition rawdenoise.c:556
void gui_init(dt_iop_module_t *self)
Definition rawdenoise.c:955
#define BIT16
Definition rawdenoise.c:197
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:236
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition rawdenoise.c:184
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:189
int flags()
Definition rawdenoise.c:174
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:346
#define DT_IOP_RAWDENOISE_BANDS
Definition rawdenoise.c:82
static gboolean rawdenoise_scrolled(GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
Definition rawdenoise.c:931
static gboolean rawdenoise_draw(GtkWidget *widget, cairo_t *crf, gpointer user_data)
Definition rawdenoise.c:657
static gboolean rawdenoise_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
Definition rawdenoise.c:876
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:516
gboolean force_enable(struct dt_iop_module_t *self, const gboolean current_state)
Definition rawdenoise.c:574
static float vstransform(const float value)
Definition rawdenoise.c:340
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition rawdenoise.c:630
static gboolean rawdenoise_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
Definition rawdenoise.c:847
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:647
dt_iop_rawdenoise_channel_t
Definition rawdenoise.c:85
@ DT_RAWDENOISE_G
Definition rawdenoise.c:88
@ DT_RAWDENOISE_R
Definition rawdenoise.c:87
@ DT_RAWDENOISE_B
Definition rawdenoise.c:89
@ DT_RAWDENOISE_ALL
Definition rawdenoise.c:86
@ DT_RAWDENOISE_NONE
Definition rawdenoise.c:90
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:130
static cairo_surface_t * dt_cairo_image_surface_create(cairo_format_t format, int width, int height)
GtkWidget * dt_ui_resizable_drawing_area(GtkWidget *area, char *config_str, int default_height, int min_height)
Make a self-drawing widget (typically a GtkDrawingArea graph or scope) vertically resizable.
static const dt_aligned_pixel_simd_t value
Definition simd.h:144
const float uint32_t state[4]
const float noise
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:225
uint32_t filters
Definition format.h:89
unsigned int channels
Definition format.h:83
uint8_t xtrans[6][6]
Definition format.h:99
GtkWidget * widget
Definition imageop_gui.h:47
int32_t hide_enable_button
Definition imageop.h:270
dt_iop_params_t * default_params
Definition imageop.h:333
struct dt_iop_module_gui_t * gui
Definition imageop.h:346
struct dt_develop_t * dev
Definition imageop.h:311
dt_iop_params_t * params
Definition imageop.h:333
float force[DT_RAWDENOISE_NONE][5]
Definition rawdenoise.c:123
dt_draw_curve_t * curve[DT_RAWDENOISE_NONE]
Definition rawdenoise.c:121
dt_iop_rawdenoise_channel_t channel
Definition rawdenoise.c:122
dt_iop_rawdenoise_channel_t channel
Definition rawdenoise.c:112
dt_iop_rawdenoise_params_t drag_params
Definition rawdenoise.c:109
dt_draw_curve_t * transition_curve
Definition rawdenoise.c:102
float x[DT_RAWDENOISE_NONE][5]
Definition rawdenoise.c:96
float y[DT_RAWDENOISE_NONE][5]
Definition rawdenoise.c:97
Region of interest passed through the pixelpipe.
Definition format.h:49
int width
Definition format.h:50
int height
Definition format.h:50
GtkWidget * notebook
#define __DT_CLONE_TARGETS__
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29
gboolean dt_gui_get_scroll_unit_deltas(const GdkEventScroll *event, int *delta_x, int *delta_y)
GdkEventMask dt_widget_scroll_mask(void)
gboolean dt_gui_widgets_suppressed(void)
#define DT_GUI_BOX_SPACING
#define DT_PIXEL_APPLY_DPI(value)