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
25 darktable is free software: you can redistribute it and/or modify
26 it under the terms of the GNU General Public License as published by
27 the Free Software Foundation, either version 3 of the License, or
28 (at your option) any later version.
29
30 darktable is distributed in the hope that it will be useful,
31 but WITHOUT ANY WARRANTY; without even the implied warranty of
32 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 GNU General Public License for more details.
34
35 You should have received a copy of the GNU General Public License
36 along with darktable. If not, see <http://www.gnu.org/licenses/>.
37*/
38#ifdef HAVE_CONFIG_H
39#include "config.h"
40#endif
41#include "widgets/bauhaus.h"
42#include "system/macros.h"
43#include "system/openmp.h"
45#include "system/mem_alloc.h"
46#include "system/simd.h"
47#include "common/logging.h"
49#include "database/database.h"
50#include "common/imagebuf.h"
52#include "math/math.h"
53#include "common/opencl.h"
54#include "pixel/tea.h"
55#include "develop/develop.h"
56#include "develop/imageop.h"
58#include "develop/imageop_gui.h"
59
60#include "gui/presets.h"
61#include "iop/iop_api.h"
62#include <assert.h>
63#include <stdlib.h>
64#include <string.h>
65
66#include <gtk/gtk.h>
67#include <inttypes.h>
68
70
72{
73 DITHER_RANDOM, // $DESCRIPTION: "random"
74 DITHER_FS1BIT, // $DESCRIPTION: "Floyd-Steinberg 1-bit B&W"
75 DITHER_FS4BIT_GRAY, // $DESCRIPTION: "Floyd-Steinberg 4-bit gray"
76 DITHER_FS8BIT, // $DESCRIPTION: "Floyd-Steinberg 8-bit RGB"
77 DITHER_FS16BIT, // $DESCRIPTION: "Floyd-Steinberg 16-bit RGB"
78 DITHER_FSAUTO // $DESCRIPTION: "Floyd-Steinberg auto"
80
81
83{
84 dt_iop_dither_type_t dither_type; // $DEFAULT: DITHER_FSAUTO $DESCRIPTION: "method"
85 int palette; // reserved for future extensions
86 struct
87 {
88 float radius; // reserved for future extensions
89 float range[4]; // reserved for future extensions {0,0,1,1}
90 float damping; // $MIN: -200.0 $MAX: 0.0 $DEFAULT: -200.0 $DESCRIPTION: "damping"
93
103
114
119
120
121const char *name()
122{
123 return _("dithering");
124}
125
126const char **description(struct dt_iop_module_t *self)
127{
128 return dt_iop_set_description(self, _("reduce banding and posterization effects in output JPEGs by adding random noise"),
129 _("corrective"),
130 _("non-linear, RGB, display-referred"),
131 _("non-linear, RGB"),
132 _("non-linear, RGB, display-referred"));
133}
134
136{
137 return IOP_GROUP_TECHNICAL;
138}
139
140int flags()
141{
143}
144
149
151{
153
155 = (dt_iop_dither_params_t){ DITHER_FSAUTO, 0, { 0.0f, { 0.0f, 0.0f, 1.0f, 1.0f }, -200.0f } };
156 // add the preset.
157 dt_gui_presets_add_generic(_("dither"), self->op,
158 self->version(), &tmp, sizeof(dt_iop_dither_params_t), 1);
159 // make it auto-apply for all images:
160 // dt_gui_presets_update_autoapply(_("dither"), self->op, self->version(), 1);
161
163}
164
165
167{
168 // Auto-enable for safe JPEG export
169 module->default_enabled = TRUE;
170}
171
172
173__OMP_DECLARE_SIMD__(simdlen(4))
174static inline float _quantize(const float val, const float f, const float rf)
175{
176 return rf * ceilf((val * f) - 0.5); // round up only if frac(x) strictly greater than 0.5
177 //originally (and more slowly):
178 //const float tmp = val * f;
179 //const float itmp = floorf(tmp);
180 //return rf * (itmp + ((tmp - itmp > 0.5f) ? 1.0f : 0.0f));
181}
182
184static inline float _rgb_to_gray(const float *const restrict val)
185{
186 return 0.30f * val[0] + 0.59f * val[1] + 0.11f * val[2]; // RGB -> GRAY
187}
188
190static inline void nearest_color(float *const restrict val, float *const restrict err, int graymode,
191 const float f, const float rf)
192{
193 if (graymode)
194 {
195 // dither pixel into gray, with f=levels-1 and rf=1/f, return err=old-new
196 const float in = _rgb_to_gray(val);
197 const float new = _quantize(in,f,rf);
198 __OMP_SIMD__(aligned(val, err : 16))
199 for(int c = 0; c < 4; c++)
200 {
201 err[c] = val[c] - new;
202 val[c] = new;
203 }
204 }
205 else
206 {
207 // dither pixel into RGB, with f=levels-1 and rf=1/f, return err=old-new
208 __OMP_SIMD__(aligned(val, err : 16))
209 for(int c = 0; c < 4; c++)
210 {
211 const float old = val[c];
212 const float new = _quantize(old, f, rf);
213 err[c] = old - new;
214 val[c] = new;
215 }
216 }
217}
218
219static inline void _diffuse_error(float *const restrict val, const float *const restrict err, const float factor)
220{
221 __OMP_SIMD__(aligned(val, err))
222 for(int c = 0; c < 4; c++)
223 {
224 val[c] += err[c] * factor;
225 }
226}
227
228#if defined(__x86_64__) || defined(__i386__)
229static inline void _diffuse_error_sse(float *val, const __m128 err, const float factor)
230{
231 _mm_store_ps(val, _mm_load_ps(val) + (err * _mm_set1_ps(factor))); // *val += err * factor
232}
233#endif
234
236static inline float clipnan(const float x)
237{
238 // convert NaN to 0.5, otherwise clamp to between 0.0 and 1.0
239 return (x > 0.0f) ? ((x < 1.0f) ? x // 0 < x < 1
240 : 1.0f // x >= 1
241 )
242 : isnan(x) ? 0.5f // x is NaN
243 : 0.0f; // x <= 0
244}
245
246static inline void clipnan_pixel(float *const restrict out, const float *const restrict in)
247{
248 __OMP_SIMD__(aligned(in, out : 16))
249 for (int c = 0; c < 4; c++)
250 out[c] = clipnan(in[c]);
251}
252
253// clipnan_pixel proves to vectorize just fine, so don't bother implementing a separate SSE version
254#define clipnan_pixel_sse clipnan_pixel
255
256static inline __attribute__((always_inline)) int get_dither_parameters(const dt_iop_dither_data_t *const data, const dt_dev_pixelpipe_t *const pipe,
257 const dt_dev_pixelpipe_iop_t *const piece,
258 const float scale, unsigned int *const restrict levels)
259{
260 int graymode = -1;
261 *levels = 65536;
262 const int l1 = floorf(1.0f + dt_log2f(1.0f / scale));
263 const int bds = (pipe->type != DT_DEV_PIXELPIPE_EXPORT) ? l1 * l1 : 1;
264
265 switch(data->dither_type)
266 {
267 case DITHER_FS1BIT:
268 graymode = 1;
269 *levels = MAX(2, MIN(bds + 1, 256));
270 break;
272 graymode = 1;
273 *levels = MAX(16, MIN(15 * bds + 1, 256));
274 break;
275 case DITHER_FS8BIT:
276 graymode = 0;
277 *levels = 256;
278 break;
279 case DITHER_FS16BIT:
280 graymode = 0;
281 *levels = 65536;
282 break;
283 case DITHER_FSAUTO:
284 switch(pipe->levels & IMAGEIO_CHANNEL_MASK)
285 {
286 case IMAGEIO_RGB:
287 graymode = 0;
288 break;
289 case IMAGEIO_GRAY:
290 graymode = 1;
291 break;
292 }
293
294 switch(pipe->levels & IMAGEIO_PREC_MASK)
295 {
296 case IMAGEIO_INT8:
297 *levels = 256;
298 break;
299 case IMAGEIO_INT12:
300 *levels = 4096;
301 break;
302 case IMAGEIO_INT16:
303 *levels = 65536;
304 break;
305 case IMAGEIO_BW:
306 *levels = 2;
307 break;
308 case IMAGEIO_INT32:
309 case IMAGEIO_FLOAT:
310 default:
311 graymode = -1;
312 break;
313 }
314 break;
315 case DITHER_RANDOM:
316 // this function won't ever be called for that type
317 // instead, process_random() will be called
318 __builtin_unreachable();
319 break;
320 }
321 return graymode;
322}
323
324// what fraction of the error to spread to each neighbor pixel
325#define RIGHT_WT (7.0f/16.0f)
326#define DOWNRIGHT_WT (1.0f/16.0f)
327#define DOWN_WT (5.0f/16.0f)
328#define DOWNLEFT_WT (3.0f/16.0f)
330static void process_floyd_steinberg(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe,
331 const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid,
332 const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
333{
334 const dt_iop_dither_data_t *const restrict data = (dt_iop_dither_data_t *)piece->data;
335
336 const int width = roi_in->width;
337 const int height = roi_in->height;
338 const float scale = roi_in->scale;
339
340 const float *const restrict in = (const float *)ivoid;
341 float *const restrict out = (float *)ovoid;
342
343 unsigned int levels = 1;
344 int graymode = get_dither_parameters(data, pipe, piece, scale, &levels);
345 if(graymode < 0)
346 {
347 // No determinable quantization for this output: pass the buffer through UNTOUCHED.
348 // (Float/int32 outputs never even reach here: commit_params() disables the module for
349 // them. The historical clipnan_pixel() pass here clamped to [0,1], which destroyed the
350 // HDR values of every float/EXR export.)
352 return;
353 }
354
355 const float f = levels - 1;
356 const float rf = 1.0 / f;
358
359 // dither without error diffusion on very tiny images
360 if(width < 3 || height < 3)
361 {
362 for(int j = 0; j < height * width; j++)
363 {
364 clipnan_pixel(out + 4 * j, in + 4 * j);
365 nearest_color(out + 4 * j, err, graymode, f, rf);
366 }
367
369 dt_iop_alpha_copy(ivoid, ovoid, roi_out->width, roi_out->height);
370 return;
371 }
372
373 // offsets to neighboring pixels
374 const size_t right = 4;
375 const size_t downleft = 4 * (width-1);
376 const size_t down = 4 * width;
377 const size_t downright = 4 * (width+1);
378
379#define PROCESS_PIXEL_FULL(_pixel, inpix) \
380 { \
381 float *const pixel_ = (_pixel); \
382 nearest_color(pixel_, err, graymode, f, rf); /* quantize pixel */ \
383 clipnan_pixel(pixel_ + downright,(inpix) + downright); /* prepare downright for first access */ \
384 _diffuse_error(pixel_ + right, err, RIGHT_WT); /* diffuse quantization error to neighbors */ \
385 _diffuse_error(pixel_ + downleft, err, DOWNLEFT_WT); \
386 _diffuse_error(pixel_ + down, err, DOWN_WT); \
387 _diffuse_error(pixel_ + downright, err, DOWNRIGHT_WT); \
388 }
389
390#define PROCESS_PIXEL_LEFT(_pixel, inpix) \
391 { \
392 float *const pixel_ = (_pixel); \
393 nearest_color(pixel_, err, graymode, f, rf); /* quantize pixel */ \
394 clipnan_pixel(pixel_ + down,(inpix) + down); /* prepare down for first access */ \
395 clipnan_pixel(pixel_ + downright,(inpix) + downright); /* prepare downright for first access */ \
396 _diffuse_error(pixel_ + right, err, RIGHT_WT); /* diffuse quantization error to neighbors */ \
397 _diffuse_error(pixel_ + down, err, DOWN_WT); \
398 _diffuse_error(pixel_ + downright, err, DOWNRIGHT_WT); \
399 }
400
401#define PROCESS_PIXEL_RIGHT(pixel) \
402 nearest_color(pixel, err, graymode, f, rf); /* quantize pixel */ \
403 _diffuse_error(pixel + downleft, err, DOWNLEFT_WT); /* diffuse quantization error to neighbors */ \
404 _diffuse_error(pixel + down, err, DOWN_WT);
405
406 // once the FS dithering gets started, we can copy&clip the downright pixel, as that will be the first time
407 // it will be accessed. But to get the process started, we need to prepare the top row of pixels
408 __OMP_SIMD__(aligned(in, out : 64))
409 for (int j = 0; j < width; j++)
410 {
411 clipnan_pixel(out + 4*j, in + 4*j);
412 }
413
414 // floyd-steinberg dithering follows here
415 // do the bulk of the image (all except the last row)
416 for(int j = 0; j < height - 1; j++)
417 {
418 const float *const restrict inrow = in + (size_t)4 * j * width;
419 float *const restrict outrow = out + (size_t)4 * j * width;
420
421 // first two columns
422 PROCESS_PIXEL_LEFT(outrow, inrow); // leftmost pixel in first (upper) row
423
424 // main part of the current row
425 for(int i = 1; i < width - 1; i++)
426 {
427 PROCESS_PIXEL_FULL(outrow + 4 * i, inrow + 4 * i);
428 }
429
430 // last column of upper row
431 PROCESS_PIXEL_RIGHT(outrow + 4 * (width-1));
432 }
433
434 // final row
435 {
436 float *const restrict outrow = out + (size_t)4 * (height - 1) * width;
437
438 // last row except for the right-most pixel
439 for(int i = 0; i < width - 1; i++)
440 {
441 float *const restrict pixel = outrow + 4 * i;
442 nearest_color(pixel, err, graymode, f, rf); // quantize the pixel
443 _diffuse_error(pixel + right, err, RIGHT_WT); // spread error to only remaining neighbor
444 }
445
446 // lower right pixel
447 nearest_color(outrow + 4 * (width - 1), err, graymode, f, rf); // quantize the last pixel, no neighbors left
448 }
449
450 // copy alpha channel if needed
452 dt_iop_alpha_copy(ivoid, ovoid, roi_out->width, roi_out->height);
453}
454
456static void process_random(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe,
457 const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid,
458 const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
459{
460 const dt_iop_dither_data_t *const data = (dt_iop_dither_data_t *)piece->data;
461
462 const int width = roi_in->width;
463 const int height = roi_in->height;
464 const float dither = powf(2.0f, data->random.damping / 10.0f);
465
466 unsigned int *const tea_states = alloc_tea_states(dt_get_num_openmp_threads());
468 {
469 // get a pointer to each thread's private buffer *outside* the for loop, to avoid a function call per iteration
470 unsigned int *const tea_state = get_tea_state(tea_states,dt_get_thread_num());
472 for(int j = 0; j < height; j++)
473 {
474 const size_t k = (size_t)4 * width * j;
475 const float *const in = (const float *)ivoid + k;
476 float *const out = (float *)ovoid + k;
477 tea_state[0] = j * height; /* + dt_get_thread_num() -- do not include, makes results unreproducible */
478 for(int i = 0; i < width; i++)
479 {
480 encrypt_tea(tea_state);
481 float dith = dither * tpdf(tea_state[0]);
482 __OMP_SIMD__(aligned(in, out : 64))
483 for(int c = 0; c < 4; c++)
484 {
485 out[4*i+c] = CLIP(in[4*i+c] + dith);
486 }
487 }
488 }
489 }
490 free_tea_states(tea_states);
491
492 if(pipe->mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK) dt_iop_alpha_copy(ivoid, ovoid, width, height);
493}
494
495
496int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
497 void *const ovoid)
498{
499 const dt_iop_roi_t *const roi_in = &piece->roi_in;
500 const dt_iop_roi_t *const roi_out = &piece->roi_out;
502
503 if(data->dither_type == DITHER_RANDOM)
504 process_random(self, pipe, piece, ivoid, ovoid, roi_in, roi_out);
505 else
506 {
507 process_floyd_steinberg(self, pipe, piece, ivoid, ovoid, roi_in, roi_out);
508 }
509 return 0;
510}
511
512#ifdef HAVE_OPENCL
513int 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)
514{
515 const dt_iop_roi_t *const roi_in = &piece->roi_in;
516 const dt_iop_dither_data_t *const d = (dt_iop_dither_data_t *)piece->data;
518 cl_mem dev_work = NULL;
519
520 const int width = roi_in->width;
521 const int height = roi_in->height;
522 const int devid = pipe->devid;
523 const int preserve_alpha = (pipe->mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK) ? 1 : 0;
524 cl_int err = CL_SUCCESS;
525 size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
526
527 if(d->dither_type == DITHER_RANDOM)
528 {
529 const float dither = powf(2.0f, d->random.damping / 10.0f);
530 dt_opencl_set_kernel_arg(devid, gd->kernel_dither_random, 0, sizeof(cl_mem), (void *)&dev_in);
531 dt_opencl_set_kernel_arg(devid, gd->kernel_dither_random, 1, sizeof(cl_mem), (void *)&dev_out);
532 dt_opencl_set_kernel_arg(devid, gd->kernel_dither_random, 2, sizeof(int), (void *)&width);
533 dt_opencl_set_kernel_arg(devid, gd->kernel_dither_random, 3, sizeof(int), (void *)&height);
534 dt_opencl_set_kernel_arg(devid, gd->kernel_dither_random, 4, sizeof(float), (void *)&dither);
535 dt_opencl_set_kernel_arg(devid, gd->kernel_dither_random, 5, sizeof(int), (void *)&preserve_alpha);
536
537 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_dither_random, sizes);
538 if(err != CL_SUCCESS) goto error;
539 return TRUE;
540 }
541
542error:
544 dt_print(DT_DEBUG_OPENCL, "[opencl_dither] couldn't enqueue kernel! %d\n", err);
545 return FALSE;
546}
547#endif
548
549void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
550{
553
554 if(w == g->dither_type)
555 {
556 gtk_widget_set_visible(g->random, p->dither_type == DITHER_RANDOM);
557 }
558}
559
560#if 0
561static void
562radius_callback (GtkWidget *slider, gpointer user_data)
563{
564 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
565 if(dt_gui_widgets_suppressed()) return;
567 p->random.radius = dt_bauhaus_slider_get(slider);
568 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
569}
570#endif
571
572#if 0
573static void
574range_callback (GtkWidget *slider, gpointer user_data)
575{
576 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
577 if(dt_gui_widgets_suppressed()) return;
583 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
584}
585#endif
586
589{
592
593 d->dither_type = p->dither_type;
594 memcpy(&(d->random.range), &(p->random.range), sizeof(p->random.range));
595 d->random.radius = p->random.radius;
596 d->random.damping = p->random.damping;
597
598 // Dithering quantizes to integer levels: on floating-point (or int32) outputs it is
599 // meaningless whatever the dither type, so disable the node entirely. Its passthrough
600 // paths used to clamp every float/EXR export to [0,1], destroying HDR values (e.g.
601 // reconstructed highlights above the white point).
602 const int prec = pipe->levels & IMAGEIO_PREC_MASK;
603
604 if(prec == IMAGEIO_FLOAT || prec == IMAGEIO_INT32)
605 piece->enabled = FALSE;
606
607 // Floyd-Steinberg can't run on OpenCL because it's a super-serial algo
608 // But it's still slow on CPU
610 piece->enabled = FALSE;
611 else if (d->dither_type != DITHER_RANDOM)
612 piece->process_cl_ready = FALSE;
613}
614
616{
617 piece->data = dt_calloc_align(sizeof(dt_iop_dither_data_t));
618 piece->data_size = sizeof(dt_iop_dither_data_t);
619}
620
622{
623 dt_free_align(piece->data);
624 piece->data = NULL;
625}
626
628{
629 const int program = 8; // extended.cl, from programs.conf
631 if(IS_NULL_PTR(gd)) return;
632 module->data = gd;
633 gd->kernel_dither_random = dt_opencl_create_kernel(program, "dither_random");
634}
635
642
643
644void gui_update(struct dt_iop_module_t *self)
645{
648#if 0
649 dt_bauhaus_slider_set(g->radius, p->random.radius);
650
655#endif
656
657 gtk_widget_set_visible(g->random, p->dither_type == DITHER_RANDOM);
658}
659
660void gui_init(struct dt_iop_module_t *self)
661{
663
664 g->random = self->gui->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
665
666#if 0
667 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);
668 gtk_widget_set_tooltip_text(g->radius, _("radius for blurring step"));
669 dt_bauhaus_widget_set_label(g->radius, N_("radius"));
670
680 gtk_widget_set_tooltip_text(g->range, _("the gradient range where to apply random dither"));
681 g->range_label = gtk_label_new(_("gradient range"));
682
683 GtkWidget *rlabel = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
684 gtk_box_pack_start(GTK_BOX(rlabel), GTK_WIDGET(g->range_label), FALSE, FALSE, 0);
685#endif
686
687 g->damping = dt_bauhaus_slider_from_params(self, "random.damping");
688
689 gtk_widget_set_tooltip_text(g->damping, _("damping level of random dither"));
690 dt_bauhaus_slider_set_digits(g->damping, 3);
691 dt_bauhaus_slider_set_format(g->damping, " dB");
692
693#if 0
694 gtk_box_pack_start(GTK_BOX(g->random), g->radius, TRUE, TRUE, 0);
695 gtk_box_pack_start(GTK_BOX(g->random), rlabel, TRUE, TRUE, 0);
696 gtk_box_pack_start(GTK_BOX(g->random), g->range, TRUE, TRUE, 0);
697#endif
698
699 self->gui->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
700
701 g->dither_type = dt_bauhaus_combobox_from_params(self, "dither_type");
702
703 gtk_box_pack_start(GTK_BOX(self->gui->widget), g->random, TRUE, TRUE, 0);
704
705#if 0
706 g_signal_connect (G_OBJECT (g->radius), "value-changed",
707 G_CALLBACK (radius_callback), self);
708 g_signal_connect (G_OBJECT (g->range), "value-changed",
709 G_CALLBACK (range_callback), self);
710#endif
711}
712
713// clang-format off
714// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
715// vim: shiftwidth=2 expandtab tabstop=2 cindent
716// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
717// 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:3343
float dt_bauhaus_slider_get(GtkWidget *widget)
Definition bauhaus.c:3280
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3331
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:1632
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
Definition bauhaus.c:3407
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:518
struct dt_bauhaus_t * dt_bauhaus_get_global(void)
Definition darktable.c:646
#define dt_database_start_transaction()
Definition database.h:290
#define dt_database_release_transaction()
Definition database.h:291
#define dt_dev_add_history_item(dev, module, enable, redraw)
void dt_iop_params_t
Definition dev_history.h:43
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
Definition develop.h:123
static float _quantize(const float val, const float f, const float rf)
Definition dither.c:174
#define RIGHT_WT
Definition dither.c:325
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:587
const char ** description(struct dt_iop_module_t *self)
Definition dither.c:126
int default_group()
Definition dither.c:135
void reload_defaults(dt_iop_module_t *module)
Definition dither.c:166
static void nearest_color(float *const restrict val, float *const restrict err, int graymode, const float f, const float rf)
Definition dither.c:190
dt_iop_dither_type_t
Definition dither.c:72
@ DITHER_RANDOM
Definition dither.c:73
@ DITHER_FS16BIT
Definition dither.c:77
@ DITHER_FSAUTO
Definition dither.c:78
@ DITHER_FS1BIT
Definition dither.c:74
@ DITHER_FS8BIT
Definition dither.c:76
@ DITHER_FS4BIT_GRAY
Definition dither.c:75
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:456
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition dither.c:615
static void clipnan_pixel(float *const restrict out, const float *const restrict in)
Definition dither.c:246
const char * name()
Definition dither.c:121
#define PROCESS_PIXEL_LEFT(_pixel, inpix)
void gui_update(struct dt_iop_module_t *self)
Definition dither.c:644
void gui_init(struct dt_iop_module_t *self)
Definition dither.c:660
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:330
void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
Definition dither.c:549
static float _rgb_to_gray(const float *const restrict val)
Definition dither.c:184
void cleanup_global(dt_iop_module_so_t *module)
Definition dither.c:636
static void _diffuse_error(float *const restrict val, const float *const restrict err, const float factor)
Definition dither.c:219
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition dither.c:145
int flags()
Definition dither.c:140
#define PROCESS_PIXEL_FULL(_pixel, inpix)
void init_presets(dt_iop_module_so_t *self)
Definition dither.c:150
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:496
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition dither.c:621
void init_global(dt_iop_module_so_t *module)
Definition dither.c:627
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:513
static float clipnan(const float x)
Definition dither.c:236
#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
@ 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:1893
@ 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
@ 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:2554
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2448
void dt_opencl_free_kernel(const int kernel)
Definition opencl.c:2491
int dt_opencl_set_kernel_arg(const int dev, const int kernel, const int num, const size_t size, const void *arg)
Definition opencl.c:2545
void dt_opencl_release_mem_object(cl_mem mem)
Definition opencl.c:2805
#define ROUNDUPDHT(a, b)
Definition opencl.h:86
#define ROUNDUPDWD(a, b)
Definition opencl.h:85
#define __OMP_SIMD__(...)
Definition openmp.h:99
#define __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
struct dt_iop_module_t *void * data
dt_imageio_levels_t levels
dt_dev_pixelpipe_type_t type
dt_iop_dither_type_t dither_type
Definition dither.c:106
struct dt_iop_dither_data_t::@41 random
GtkWidget * damping
Definition dither.c:101
GtkWidget * range
Definition dither.c:99
GtkWidget * random
Definition dither.c:97
GtkWidget * dither_type
Definition dither.c:96
GtkWidget * radius
Definition dither.c:98
GtkWidget * range_label
Definition dither.c:100
dt_iop_dither_type_t dither_type
Definition dither.c:84
struct dt_iop_dither_params_t::@40 random
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