Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
dither.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2012-2014, 2017 Ulrich Pegelow.
4 Copyright (C) 2013, 2016 johannes hanika.
5 Copyright (C) 2013 Pascal de Bruijn.
6 Copyright (C) 2013 Thomas Pryds.
7 Copyright (C) 2013-2014, 2016, 2019 Tobias Ellinghaus.
8 Copyright (C) 2014-2016, 2019 Roman Lebedev.
9 Copyright (C) 2015 Pedro Côrte-Real.
10 Copyright (C) 2018 Edgardo Hoszowski.
11 Copyright (C) 2018, 2021 Heiko Bauke.
12 Copyright (C) 2018 Maurizio Paglia.
13 Copyright (C) 2018, 2020-2022 Pascal Obry.
14 Copyright (C) 2018 rawfiner.
15 Copyright (C) 2019 Andreas Schneider.
16 Copyright (C) 2020 Aldric Renaudin.
17 Copyright (C) 2020, 2022-2023, 2025-2026 Aurélien PIERRE.
18 Copyright (C) 2020 Chris Elston.
19 Copyright (C) 2020, 2022 Diederik Ter Rahe.
20 Copyright (C) 2020-2021 Ralf Brown.
21 Copyright (C) 2022 Martin Bařinka.
22 Copyright (C) 2022 Philipp Lutz.
23 Copyright (C) 2022 Victor Forsiuk.
24 Copyright (C) 2026 Guillaume Stutin.
25
26 darktable is free software: you can redistribute it and/or modify
27 it under the terms of the GNU General Public License as published by
28 the Free Software Foundation, either version 3 of the License, or
29 (at your option) any later version.
30
31 darktable is distributed in the hope that it will be useful,
32 but WITHOUT ANY WARRANTY; without even the implied warranty of
33 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34 GNU General Public License for more details.
35
36 You should have received a copy of the GNU General Public License
37 along with darktable. If not, see <http://www.gnu.org/licenses/>.
38*/
39#ifdef HAVE_CONFIG_H
40#include "config.h"
41#endif
42#include "widgets/bauhaus.h"
43#include "system/macros.h"
44#include "system/openmp.h"
46#include "system/mem_alloc.h"
47#include "system/simd.h"
48#include "common/logging.h"
50#include "database/database.h"
51#include "common/imagebuf.h"
53#include "math/math.h"
54#include "common/opencl.h"
55#include "pixel/tea.h"
56#include "develop/develop.h"
57#include "develop/imageop.h"
59#include "develop/imageop_gui.h"
60
61#include "gui/presets.h"
62#include "iop/iop_api.h"
63#include <assert.h>
64#include <stdlib.h>
65#include <string.h>
66
67#include <gtk/gtk.h>
68#include <inttypes.h>
69
71
73{
74 DITHER_RANDOM, // $DESCRIPTION: "random"
75 DITHER_FS1BIT, // $DESCRIPTION: "Floyd-Steinberg 1-bit B&W"
76 DITHER_FS4BIT_GRAY, // $DESCRIPTION: "Floyd-Steinberg 4-bit gray"
77 DITHER_FS8BIT, // $DESCRIPTION: "Floyd-Steinberg 8-bit RGB"
78 DITHER_FS16BIT, // $DESCRIPTION: "Floyd-Steinberg 16-bit RGB"
79 DITHER_FSAUTO // $DESCRIPTION: "Floyd-Steinberg auto"
81
82
84{
85 dt_iop_dither_type_t dither_type; // $DEFAULT: DITHER_FSAUTO $DESCRIPTION: "method"
86 int palette; // reserved for future extensions
87 struct
88 {
89 float radius; // reserved for future extensions
90 float range[4]; // reserved for future extensions {0,0,1,1}
91 float damping; // $MIN: -200.0 $MAX: 0.0 $DEFAULT: -200.0 $DESCRIPTION: "damping"
94
104
106{
108 struct
109 {
110 float radius;
111 float range[4];
112 float damping;
114 // What the quantization reads from the pipe, copied at commit so that the cache key, which
115 // hashes this struct (runtime_data_hash()), covers it: a JPEG and a 16-bit TIFF export differ
116 // only there.
120
125
126
127const char *name()
128{
129 return _("dithering");
130}
131
132const char **description(struct dt_iop_module_t *self)
133{
134 return dt_iop_set_description(self, _("reduce banding and posterization effects in output JPEGs by adding random noise"),
135 _("corrective"),
136 _("non-linear, RGB, display-referred"),
137 _("non-linear, RGB"),
138 _("non-linear, RGB, display-referred"));
139}
140
142{
143 return IOP_GROUP_TECHNICAL;
144}
145
146int flags()
147{
149}
150
155
157{
159
161 = (dt_iop_dither_params_t){ DITHER_FSAUTO, 0, { 0.0f, { 0.0f, 0.0f, 1.0f, 1.0f }, -200.0f } };
162 // add the preset.
163 dt_gui_presets_add_generic(_("dither"), self->op,
164 self->version(), &tmp, sizeof(dt_iop_dither_params_t), 1);
165 // make it auto-apply for all images:
166 // dt_gui_presets_update_autoapply(_("dither"), self->op, self->version(), 1);
167
169}
170
171
173{
174 // Auto-enable for safe JPEG export
175 module->default_enabled = TRUE;
176}
177
178
179__OMP_DECLARE_SIMD__(simdlen(4))
180static inline float _quantize(const float val, const float f, const float rf)
181{
182 return rf * ceilf((val * f) - 0.5); // round up only if frac(x) strictly greater than 0.5
183 //originally (and more slowly):
184 //const float tmp = val * f;
185 //const float itmp = floorf(tmp);
186 //return rf * (itmp + ((tmp - itmp > 0.5f) ? 1.0f : 0.0f));
187}
188
190static inline float _rgb_to_gray(const float *const restrict val)
191{
192 return 0.30f * val[0] + 0.59f * val[1] + 0.11f * val[2]; // RGB -> GRAY
193}
194
196static inline void nearest_color(float *const restrict val, float *const restrict err, int graymode,
197 const float f, const float rf)
198{
199 if (graymode)
200 {
201 // dither pixel into gray, with f=levels-1 and rf=1/f, return err=old-new
202 const float in = _rgb_to_gray(val);
203 const float new = _quantize(in,f,rf);
204 __OMP_SIMD__(aligned(val, err : 16))
205 for(int c = 0; c < 4; c++)
206 {
207 err[c] = val[c] - new;
208 val[c] = new;
209 }
210 }
211 else
212 {
213 // dither pixel into RGB, with f=levels-1 and rf=1/f, return err=old-new
214 __OMP_SIMD__(aligned(val, err : 16))
215 for(int c = 0; c < 4; c++)
216 {
217 const float old = val[c];
218 const float new = _quantize(old, f, rf);
219 err[c] = old - new;
220 val[c] = new;
221 }
222 }
223}
224
225static inline void _diffuse_error(float *const restrict val, const float *const restrict err, const float factor)
226{
227 __OMP_SIMD__(aligned(val, err))
228 for(int c = 0; c < 4; c++)
229 {
230 val[c] += err[c] * factor;
231 }
232}
233
234#if defined(__x86_64__) || defined(__i386__)
235static inline void _diffuse_error_sse(float *val, const __m128 err, const float factor)
236{
237 _mm_store_ps(val, _mm_load_ps(val) + (err * _mm_set1_ps(factor))); // *val += err * factor
238}
239#endif
240
242static inline float clipnan(const float x)
243{
244 // convert NaN to 0.5, otherwise clamp to between 0.0 and 1.0
245 return (x > 0.0f) ? ((x < 1.0f) ? x // 0 < x < 1
246 : 1.0f // x >= 1
247 )
248 : isnan(x) ? 0.5f // x is NaN
249 : 0.0f; // x <= 0
250}
251
252static inline void clipnan_pixel(float *const restrict out, const float *const restrict in)
253{
254 __OMP_SIMD__(aligned(in, out : 16))
255 for (int c = 0; c < 4; c++)
256 out[c] = clipnan(in[c]);
257}
258
259// clipnan_pixel proves to vectorize just fine, so don't bother implementing a separate SSE version
260#define clipnan_pixel_sse clipnan_pixel
261
262static inline __attribute__((always_inline)) int get_dither_parameters(const dt_iop_dither_data_t *const data,
263 const float scale, unsigned int *const restrict levels)
264{
265 int graymode = -1;
266 *levels = 65536;
267 const int l1 = floorf(1.0f + dt_log2f(1.0f / scale));
268 const int bds = data->display_scaled ? l1 * l1 : 1;
269
270 switch(data->dither_type)
271 {
272 case DITHER_FS1BIT:
273 graymode = 1;
274 *levels = MAX(2, MIN(bds + 1, 256));
275 break;
277 graymode = 1;
278 *levels = MAX(16, MIN(15 * bds + 1, 256));
279 break;
280 case DITHER_FS8BIT:
281 graymode = 0;
282 *levels = 256;
283 break;
284 case DITHER_FS16BIT:
285 graymode = 0;
286 *levels = 65536;
287 break;
288 case DITHER_FSAUTO:
289 switch(data->output_levels & IMAGEIO_CHANNEL_MASK)
290 {
291 case IMAGEIO_RGB:
292 graymode = 0;
293 break;
294 case IMAGEIO_GRAY:
295 graymode = 1;
296 break;
297 default: // no channel layout: graymode stays -1 and the buffer passes through
298 break;
299 }
300
301 switch(data->output_levels & IMAGEIO_PREC_MASK)
302 {
303 case IMAGEIO_INT8:
304 *levels = 256;
305 break;
306 case IMAGEIO_INT12:
307 *levels = 4096;
308 break;
309 case IMAGEIO_INT16:
310 *levels = 65536;
311 break;
312 case IMAGEIO_BW:
313 *levels = 2;
314 break;
315 case IMAGEIO_INT32:
316 case IMAGEIO_FLOAT:
317 default:
318 graymode = -1;
319 break;
320 }
321 break;
322 case DITHER_RANDOM:
323 // this function won't ever be called for that type
324 // instead, process_random() will be called
325 __builtin_unreachable();
326 break;
327 }
328 return graymode;
329}
330
331// what fraction of the error to spread to each neighbor pixel
332#define RIGHT_WT (7.0f/16.0f)
333#define DOWNRIGHT_WT (1.0f/16.0f)
334#define DOWN_WT (5.0f/16.0f)
335#define DOWNLEFT_WT (3.0f/16.0f)
338 const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid,
339 const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
340{
341 const dt_iop_dither_data_t *const restrict data = (dt_iop_dither_data_t *)piece->data;
342
343 const int width = roi_in->width;
344 const int height = roi_in->height;
345 const float scale = roi_in->scale;
346
347 const float *const restrict in = (const float *)ivoid;
348 float *const restrict out = (float *)ovoid;
349
350 unsigned int levels = 1;
351 int graymode = get_dither_parameters(data, scale, &levels);
352 if(graymode < 0)
353 {
354 // No determinable quantization for this output: pass the buffer through UNTOUCHED.
355 // (Float/int32 outputs never even reach here: commit_params() disables the module for
356 // them. The historical clipnan_pixel() pass here clamped to [0,1], which destroyed the
357 // HDR values of every float/EXR export.)
359 return;
360 }
361
362 const float f = levels - 1;
363 const float rf = 1.0 / f;
365
366 // dither without error diffusion on very tiny images
367 if(width < 3 || height < 3)
368 {
369 for(int j = 0; j < height * width; j++)
370 {
371 clipnan_pixel(out + 4 * j, in + 4 * j);
372 nearest_color(out + 4 * j, err, graymode, f, rf);
373 }
374
376 dt_iop_alpha_copy(ivoid, ovoid, roi_out->width, roi_out->height);
377 return;
378 }
379
380 // offsets to neighboring pixels
381 const size_t right = 4;
382 const size_t downleft = 4 * (width-1);
383 const size_t down = 4 * width;
384 const size_t downright = 4 * (width+1);
385
386#define PROCESS_PIXEL_FULL(_pixel, inpix) \
387 { \
388 float *const pixel_ = (_pixel); \
389 nearest_color(pixel_, err, graymode, f, rf); /* quantize pixel */ \
390 clipnan_pixel(pixel_ + downright,(inpix) + downright); /* prepare downright for first access */ \
391 _diffuse_error(pixel_ + right, err, RIGHT_WT); /* diffuse quantization error to neighbors */ \
392 _diffuse_error(pixel_ + downleft, err, DOWNLEFT_WT); \
393 _diffuse_error(pixel_ + down, err, DOWN_WT); \
394 _diffuse_error(pixel_ + downright, err, DOWNRIGHT_WT); \
395 }
396
397#define PROCESS_PIXEL_LEFT(_pixel, inpix) \
398 { \
399 float *const pixel_ = (_pixel); \
400 nearest_color(pixel_, err, graymode, f, rf); /* quantize pixel */ \
401 clipnan_pixel(pixel_ + down,(inpix) + down); /* prepare down for first access */ \
402 clipnan_pixel(pixel_ + downright,(inpix) + downright); /* prepare downright for first access */ \
403 _diffuse_error(pixel_ + right, err, RIGHT_WT); /* diffuse quantization error to neighbors */ \
404 _diffuse_error(pixel_ + down, err, DOWN_WT); \
405 _diffuse_error(pixel_ + downright, err, DOWNRIGHT_WT); \
406 }
407
408#define PROCESS_PIXEL_RIGHT(pixel) \
409 nearest_color(pixel, err, graymode, f, rf); /* quantize pixel */ \
410 _diffuse_error(pixel + downleft, err, DOWNLEFT_WT); /* diffuse quantization error to neighbors */ \
411 _diffuse_error(pixel + down, err, DOWN_WT);
412
413 // once the FS dithering gets started, we can copy&clip the downright pixel, as that will be the first time
414 // it will be accessed. But to get the process started, we need to prepare the top row of pixels
415 __OMP_SIMD__(aligned(in, out : 64))
416 for (int j = 0; j < width; j++)
417 {
418 clipnan_pixel(out + 4*j, in + 4*j);
419 }
420
421 // floyd-steinberg dithering follows here
422 // do the bulk of the image (all except the last row)
423 for(int j = 0; j < height - 1; j++)
424 {
425 const float *const restrict inrow = in + (size_t)4 * j * width;
426 float *const restrict outrow = out + (size_t)4 * j * width;
427
428 // first two columns
429 PROCESS_PIXEL_LEFT(outrow, inrow); // leftmost pixel in first (upper) row
430
431 // main part of the current row
432 for(int i = 1; i < width - 1; i++)
433 {
434 PROCESS_PIXEL_FULL(outrow + 4 * i, inrow + 4 * i);
435 }
436
437 // last column of upper row
438 PROCESS_PIXEL_RIGHT(outrow + 4 * (width-1));
439 }
440
441 // final row
442 {
443 float *const restrict outrow = out + (size_t)4 * (height - 1) * width;
444
445 // last row except for the right-most pixel
446 for(int i = 0; i < width - 1; i++)
447 {
448 float *const restrict pixel = outrow + 4 * i;
449 nearest_color(pixel, err, graymode, f, rf); // quantize the pixel
450 _diffuse_error(pixel + right, err, RIGHT_WT); // spread error to only remaining neighbor
451 }
452
453 // lower right pixel
454 nearest_color(outrow + 4 * (width - 1), err, graymode, f, rf); // quantize the last pixel, no neighbors left
455 }
456
457 // copy alpha channel if needed
459 dt_iop_alpha_copy(ivoid, ovoid, roi_out->width, roi_out->height);
460}
461
463static void process_random(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe,
464 const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid,
465 const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
466{
467 const dt_iop_dither_data_t *const data = (dt_iop_dither_data_t *)piece->data;
468
469 const int width = roi_in->width;
470 const int height = roi_in->height;
471 const float dither = powf(2.0f, data->random.damping / 10.0f);
472
473 unsigned int *const tea_states = alloc_tea_states(dt_get_num_openmp_threads());
475 {
476 // get a pointer to each thread's private buffer *outside* the for loop, to avoid a function call per iteration
477 unsigned int *const tea_state = get_tea_state(tea_states,dt_get_thread_num());
479 for(int j = 0; j < height; j++)
480 {
481 const size_t k = (size_t)4 * width * j;
482 const float *const in = (const float *)ivoid + k;
483 float *const out = (float *)ovoid + k;
484 tea_state[0] = j * height; /* + dt_get_thread_num() -- do not include, makes results unreproducible */
485 for(int i = 0; i < width; i++)
486 {
487 encrypt_tea(tea_state);
488 float dith = dither * tpdf(tea_state[0]);
489 __OMP_SIMD__(aligned(in, out : 64))
490 for(int c = 0; c < 4; c++)
491 {
492 out[4*i+c] = CLIP(in[4*i+c] + dith);
493 }
494 }
495 }
496 }
497 free_tea_states(tea_states);
498
499 if(pipe->mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK) dt_iop_alpha_copy(ivoid, ovoid, width, height);
500}
501
502
503int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
504 void *const ovoid)
505{
506 const dt_iop_roi_t *const roi_in = &piece->roi_in;
507 const dt_iop_roi_t *const roi_out = &piece->roi_out;
509
510 if(data->dither_type == DITHER_RANDOM)
511 process_random(self, pipe, piece, ivoid, ovoid, roi_in, roi_out);
512 else
513 {
514 process_floyd_steinberg(self, pipe, piece, ivoid, ovoid, roi_in, roi_out);
515 }
516 return 0;
517}
518
519#ifdef HAVE_OPENCL
520int process_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out)
521{
522 const dt_iop_roi_t *const roi_in = &piece->roi_in;
523 const dt_iop_dither_data_t *const d = (dt_iop_dither_data_t *)piece->data;
525 cl_mem dev_work = NULL;
526
527 const int width = roi_in->width;
528 const int height = roi_in->height;
529 const int devid = pipe->devid;
530 const int preserve_alpha = (pipe->mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK) ? 1 : 0;
531 cl_int err = CL_SUCCESS;
532 size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
533
534 if(d->dither_type == DITHER_RANDOM)
535 {
536 const float dither = powf(2.0f, d->random.damping / 10.0f);
537 dt_opencl_set_kernel_arg(devid, gd->kernel_dither_random, 0, sizeof(cl_mem), (void *)&dev_in);
538 dt_opencl_set_kernel_arg(devid, gd->kernel_dither_random, 1, sizeof(cl_mem), (void *)&dev_out);
539 dt_opencl_set_kernel_arg(devid, gd->kernel_dither_random, 2, sizeof(int), (void *)&width);
540 dt_opencl_set_kernel_arg(devid, gd->kernel_dither_random, 3, sizeof(int), (void *)&height);
541 dt_opencl_set_kernel_arg(devid, gd->kernel_dither_random, 4, sizeof(float), (void *)&dither);
542 dt_opencl_set_kernel_arg(devid, gd->kernel_dither_random, 5, sizeof(int), (void *)&preserve_alpha);
543
544 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_dither_random, sizes);
545 if(err != CL_SUCCESS) goto error;
546 return TRUE;
547 }
548
549error:
551 dt_print(DT_DEBUG_OPENCL, "[opencl_dither] couldn't enqueue kernel! %d\n", err);
552 return FALSE;
553}
554#endif
555
556void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
557{
560
561 if(w == g->dither_type)
562 {
563 gtk_widget_set_visible(g->random, p->dither_type == DITHER_RANDOM);
564 }
565}
566
567#if 0
568static void
569radius_callback (GtkWidget *slider, gpointer user_data)
570{
571 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
572 if(dt_gui_widgets_suppressed()) return;
574 p->random.radius = dt_bauhaus_slider_get(slider);
575 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
576}
577#endif
578
579#if 0
580static void
581range_callback (GtkWidget *slider, gpointer user_data)
582{
583 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
584 if(dt_gui_widgets_suppressed()) return;
590 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
591}
592#endif
593
596{
599
600 d->dither_type = p->dither_type;
601 memcpy(&(d->random.range), &(p->random.range), sizeof(p->random.range));
602 d->random.radius = p->random.radius;
603 d->random.damping = p->random.damping;
604 d->output_levels = pipe->levels;
605 d->display_scaled = (pipe->type != DT_DEV_PIXELPIPE_EXPORT);
606
607 // Dithering quantizes to integer levels: on floating-point (or int32) outputs it is
608 // meaningless whatever the dither type, so disable the node entirely. Its passthrough
609 // paths used to clamp every float/EXR export to [0,1], destroying HDR values (e.g.
610 // reconstructed highlights above the white point).
611 const int prec = d->output_levels & IMAGEIO_PREC_MASK;
612
613 if(prec == IMAGEIO_FLOAT || prec == IMAGEIO_INT32)
614 piece->enabled = FALSE;
615
616 // Floyd-Steinberg can't run on OpenCL because it's a super-serial algo
617 // But it's still slow on CPU
619 piece->enabled = FALSE;
620 else if (d->dither_type != DITHER_RANDOM)
621 piece->process_cl_ready = FALSE;
622}
623
624gboolean runtime_data_hash(struct dt_iop_module_t *self __attribute__((unused)),
627{
628 return TRUE;
629}
630
636
638{
639 dt_free_align(piece->data);
640 piece->data = NULL;
641}
642
644{
645 const int program = 8; // extended.cl, from programs.conf
647 if(IS_NULL_PTR(gd)) return;
648 module->data = gd;
649 gd->kernel_dither_random = dt_opencl_create_kernel(program, "dither_random");
650}
651
658
659
660void gui_update(struct dt_iop_module_t *self)
661{
664#if 0
665 dt_bauhaus_slider_set(g->radius, p->random.radius);
666
671#endif
672
673 gtk_widget_set_visible(g->random, p->dither_type == DITHER_RANDOM);
674}
675
676void gui_init(struct dt_iop_module_t *self)
677{
679
680 g->random = self->gui->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
681
682#if 0
683 g->radius = dt_bauhaus_slider_new_with_range(dt_bauhaus_get_global(), DT_GUI_MODULE(self), 0.0, 200.0, 0.1, p->random.radius, 2);
684 gtk_widget_set_tooltip_text(g->radius, _("radius for blurring step"));
685 dt_bauhaus_widget_set_label(g->radius, N_("radius"));
686
696 gtk_widget_set_tooltip_text(g->range, _("the gradient range where to apply random dither"));
697 g->range_label = gtk_label_new(_("gradient range"));
698
699 GtkWidget *rlabel = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
700 gtk_box_pack_start(GTK_BOX(rlabel), GTK_WIDGET(g->range_label), FALSE, FALSE, 0);
701#endif
702
703 g->damping = dt_bauhaus_slider_from_params(self, "random.damping");
704
705 gtk_widget_set_tooltip_text(g->damping, _("damping level of random dither"));
706 dt_bauhaus_slider_set_digits(g->damping, 3);
707 dt_bauhaus_slider_set_format(g->damping, " dB");
708
709#if 0
710 gtk_box_pack_start(GTK_BOX(g->random), g->radius, TRUE, TRUE, 0);
711 gtk_box_pack_start(GTK_BOX(g->random), rlabel, TRUE, TRUE, 0);
712 gtk_box_pack_start(GTK_BOX(g->random), g->range, TRUE, TRUE, 0);
713#endif
714
715 self->gui->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
716
717 g->dither_type = dt_bauhaus_combobox_from_params(self, "dither_type");
718
719 gtk_box_pack_start(GTK_BOX(self->gui->widget), g->random, TRUE, TRUE, 0);
720
721#if 0
722 g_signal_connect (G_OBJECT (g->radius), "value-changed",
723 G_CALLBACK (radius_callback), self);
724 g_signal_connect (G_OBJECT (g->range), "value-changed",
725 G_CALLBACK (range_callback), self);
726#endif
727}
728
729// clang-format off
730// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
731// vim: shiftwidth=2 expandtab tabstop=2 cindent
732// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
733// clang-format on
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
int levels(struct dt_imageio_module_data_t *data)
Definition avif.c:641
void dt_bauhaus_slider_set_digits(GtkWidget *widget, int val)
Definition bauhaus.c:3346
float dt_bauhaus_slider_get(GtkWidget *widget)
Definition bauhaus.c:3283
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3334
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
Definition bauhaus.c:1504
GtkWidget * dt_bauhaus_slider_new_with_range(dt_bauhaus_t *bh, dt_gui_module_t *self, float min, float max, float step, float defval, int digits)
Definition bauhaus.c:1635
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
Definition bauhaus.c:3410
static const float x
const float f
const float l1
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
const dt_colormatrix_t dt_aligned_pixel_t out
int dt_get_num_openmp_threads(void)
Number of OpenMP threads the application decided to use.
Definition darktable.c:521
struct dt_bauhaus_t * dt_bauhaus_get_global(void)
Definition darktable.c:649
#define dt_database_start_transaction()
Definition database.h:290
#define dt_database_release_transaction()
Definition database.h:291
#define dt_dev_add_history_item(dev, module, enable, redraw)
void dt_iop_params_t
Definition dev_history.h:43
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
Definition develop.h:123
static float _quantize(const float val, const float f, const float rf)
Definition dither.c:180
#define RIGHT_WT
Definition dither.c:332
void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition dither.c:594
const char ** description(struct dt_iop_module_t *self)
Definition dither.c:132
int default_group()
Definition dither.c:141
void reload_defaults(dt_iop_module_t *module)
Definition dither.c:172
static void nearest_color(float *const restrict val, float *const restrict err, int graymode, const float f, const float rf)
Definition dither.c:196
dt_iop_dither_type_t
Definition dither.c:73
@ DITHER_RANDOM
Definition dither.c:74
@ DITHER_FS16BIT
Definition dither.c:78
@ DITHER_FSAUTO
Definition dither.c:79
@ DITHER_FS1BIT
Definition dither.c:75
@ DITHER_FS8BIT
Definition dither.c:77
@ DITHER_FS4BIT_GRAY
Definition dither.c:76
static __DT_CLONE_TARGETS__ void process_random(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, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
Definition dither.c:463
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition dither.c:631
static void clipnan_pixel(float *const restrict out, const float *const restrict in)
Definition dither.c:252
const char * name()
Definition dither.c:127
#define PROCESS_PIXEL_LEFT(_pixel, inpix)
void gui_update(struct dt_iop_module_t *self)
Definition dither.c:660
void gui_init(struct dt_iop_module_t *self)
Definition dither.c:676
static __DT_CLONE_TARGETS__ void process_floyd_steinberg(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, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
Definition dither.c:337
void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
Definition dither.c:556
static float _rgb_to_gray(const float *const restrict val)
Definition dither.c:190
void cleanup_global(dt_iop_module_so_t *module)
Definition dither.c:652
static void _diffuse_error(float *const restrict val, const float *const restrict err, const float factor)
Definition dither.c:225
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition dither.c:151
int flags()
Definition dither.c:146
gboolean runtime_data_hash(struct dt_iop_module_t *self __attribute__((unused)), dt_dev_pixelpipe_t *pipe __attribute__((unused)), const dt_dev_pixelpipe_iop_t *piece __attribute__((unused)))
Definition dither.c:624
#define PROCESS_PIXEL_FULL(_pixel, inpix)
void init_presets(dt_iop_module_so_t *self)
Definition dither.c:156
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 dither.c:503
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition dither.c:637
void init_global(dt_iop_module_so_t *module)
Definition dither.c:643
int process_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out)
Definition dither.c:520
static float clipnan(const float x)
Definition dither.c:242
#define PROCESS_PIXEL_RIGHT(pixel)
@ IOP_CS_RGB_DISPLAY
Definition format.h:71
void dtgtk_gradient_slider_multivalue_set_value(GtkDarktableGradientSlider *gslider, gdouble value, gint pos)
gdouble dtgtk_gradient_slider_multivalue_get_value(GtkDarktableGradientSlider *gslider, gint pos)
GtkWidget * dtgtk_gradient_slider_multivalue_new(gint positions)
void dtgtk_gradient_slider_multivalue_set_marker(GtkDarktableGradientSlider *gslider, gint mark, gint pos)
#define DTGTK_GRADIENT_SLIDER(obj)
@ GRADIENT_SLIDER_MARKER_UPPER_FILLED_BIG
@ GRADIENT_SLIDER_MARKER_LOWER_OPEN_BIG
void dt_gui_presets_add_generic(const char *name, dt_dev_operation_t op, const int32_t version, const void *params, const int32_t params_size, const int32_t enabled)
#define DT_GUI_MODULE(x)
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
dt_imageio_levels_t
@ IMAGEIO_INT32
@ IMAGEIO_INT16
@ IMAGEIO_PREC_MASK
@ IMAGEIO_RGB
@ IMAGEIO_INT12
@ IMAGEIO_INT8
@ IMAGEIO_CHANNEL_MASK
@ IMAGEIO_GRAY
@ IMAGEIO_FLOAT
@ IMAGEIO_BW
const char ** dt_iop_set_description(dt_iop_module_t *module, const char *main_text, const char *purpose, const char *input, const char *process, const char *output)
Definition imageop.c:1909
@ IOP_FLAGS_ONE_INSTANCE
Definition imageop.h:191
@ IOP_GROUP_TECHNICAL
Definition imageop.h:162
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
GtkWidget * dt_bauhaus_combobox_from_params(dt_iop_module_t *self, const char *param)
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
static const dt_dev_pixelpipe_t * pipe
Definition layers.h:49
static const dt_dev_pixelpipe_t const dt_dev_pixelpipe_iop_t * piece
Definition layers.h:50
@ DT_DEBUG_OPENCL
Definition logging.h:57
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
float *const restrict const size_t k
#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 CLIP(x)
Definition math.h:83
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
Definition mem_alloc.h:214
static void * dt_calloc_align(size_t size)
dt_alloc_align() followed by a zero fill.
Definition mem_alloc.h:225
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
DT_MODULE() for a module whose params struct is introspected.
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
Definition opencl.c:2612
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2506
void dt_opencl_free_kernel(const int kernel)
Definition opencl.c:2549
int dt_opencl_set_kernel_arg(const int dev, const int kernel, const int num, const size_t size, const void *arg)
Definition opencl.c:2603
void dt_opencl_release_mem_object(cl_mem mem)
Definition opencl.c:2863
#define ROUNDUPDHT(a, b)
Definition opencl.h:86
#define ROUNDUPDWD(a, b)
Definition opencl.h:85
#define __OMP_SIMD__(...)
Definition openmp.h:99
#define __OMP_FOR__(...)
Definition openmp.h:98
static int dt_get_thread_num()
Index of the calling thread within its parallel region, 0 outside one.
Definition openmp.h:129
#define __OMP_DECLARE_SIMD__(...)
Definition openmp.h:100
#define __OMP_PARALLEL__(...)
Definition openmp.h:94
const float factor
Definition pdf.h:91
@ DT_DEV_PIXELPIPE_EXPORT
Definition pixelpipe.h:42
gboolean dt_dev_pixelpipe_get_realtime(const dt_dev_pixelpipe_t *pipe)
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
Definition simd.h:53
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Apply one channel's tone curve to each of the three colour channels, or pass the channel through unto...
Definition simd.h:55
dt_imageio_levels_t levels
dt_dev_pixelpipe_type_t type
gboolean display_scaled
Definition dither.c:118
dt_iop_dither_type_t dither_type
Definition dither.c:107
struct dt_iop_dither_data_t::@42 random
dt_imageio_levels_t output_levels
Definition dither.c:117
GtkWidget * damping
Definition dither.c:102
GtkWidget * random
Definition dither.c:98
GtkWidget * dither_type
Definition dither.c:97
GtkWidget * radius
Definition dither.c:99
GtkWidget * range_label
Definition dither.c:101
struct dt_iop_dither_params_t::@41 random
dt_iop_dither_type_t dither_type
Definition dither.c:85
GtkWidget * widget
Definition imageop_gui.h:47
dt_dev_operation_t op
Definition imageop.h:235
dt_iop_global_data_t * data
Definition imageop.h:238
struct dt_iop_module_gui_t * gui
Definition imageop.h:346
struct dt_develop_t * dev
Definition imageop.h:311
dt_iop_global_data_t * global_data
Definition imageop.h:337
dt_iop_params_t * params
Definition imageop.h:333
Region of interest passed through the pixelpipe.
Definition format.h:49
double scale
Definition format.h:51
int width
Definition format.h:50
int height
Definition format.h:50
#define __DT_CLONE_TARGETS__
static float tpdf(unsigned int urandom)
Definition tea.h:74
static unsigned int * get_tea_state(unsigned int *const states, int threadnum)
Definition tea.h:43
static void free_tea_states(unsigned int *states)
Definition tea.h:48
static unsigned int * alloc_tea_states(size_t numthreads)
Definition tea.h:34
static void encrypt_tea(unsigned int *arg)
Definition tea.h:58
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29
gboolean dt_gui_widgets_suppressed(void)
#define DT_GUI_BOX_SPACING