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 "common/darktable.h"
40#include "config.h"
41#endif
42#include "bauhaus/bauhaus.h"
43#include "common/imagebuf.h"
44#include "common/imageio.h"
45#include "common/math.h"
46#include "common/opencl.h"
47#include "common/tea.h"
48#include "control/control.h"
49#include "develop/develop.h"
50#include "develop/imageop.h"
52#include "develop/imageop_gui.h"
53#include "develop/tiling.h"
55
56#include "gui/gtk.h"
57#include "gui/presets.h"
58#include "iop/iop_api.h"
59#include <assert.h>
60#include <stdlib.h>
61#include <string.h>
62
63#include <gtk/gtk.h>
64#include <inttypes.h>
65
67
69{
70 DITHER_RANDOM, // $DESCRIPTION: "random"
71 DITHER_FS1BIT, // $DESCRIPTION: "Floyd-Steinberg 1-bit B&W"
72 DITHER_FS4BIT_GRAY, // $DESCRIPTION: "Floyd-Steinberg 4-bit gray"
73 DITHER_FS8BIT, // $DESCRIPTION: "Floyd-Steinberg 8-bit RGB"
74 DITHER_FS16BIT, // $DESCRIPTION: "Floyd-Steinberg 16-bit RGB"
75 DITHER_FSAUTO // $DESCRIPTION: "Floyd-Steinberg auto"
77
78
80{
81 dt_iop_dither_type_t dither_type; // $DEFAULT: DITHER_FSAUTO $DESCRIPTION: "method"
82 int palette; // reserved for future extensions
83 struct
84 {
85 float radius; // reserved for future extensions
86 float range[4]; // reserved for future extensions {0,0,1,1}
87 float damping; // $MIN: -200.0 $MAX: 0.0 $DEFAULT: -200.0 $DESCRIPTION: "damping"
90
100
111
116
117
118const char *name()
119{
120 return _("dithering");
121}
122
123const char **description(struct dt_iop_module_t *self)
124{
125 return dt_iop_set_description(self, _("reduce banding and posterization effects in output JPEGs by adding random noise"),
126 _("corrective"),
127 _("non-linear, RGB, display-referred"),
128 _("non-linear, RGB"),
129 _("non-linear, RGB, display-referred"));
130}
131
133{
134 return IOP_GROUP_TECHNICAL;
135}
136
137int flags()
138{
140}
141
146
148{
150
152 = (dt_iop_dither_params_t){ DITHER_FSAUTO, 0, { 0.0f, { 0.0f, 0.0f, 1.0f, 1.0f }, -200.0f } };
153 // add the preset.
154 dt_gui_presets_add_generic(_("dither"), self->op,
155 self->version(), &tmp, sizeof(dt_iop_dither_params_t), 1,
157 // make it auto-apply for all images:
158 // dt_gui_presets_update_autoapply(_("dither"), self->op, self->version(), 1);
159
161}
162
163
165{
166 // Auto-enable for safe JPEG export
167 module->default_enabled = TRUE;
168}
169
170
171__OMP_DECLARE_SIMD__(simdlen(4))
172static inline float _quantize(const float val, const float f, const float rf)
173{
174 return rf * ceilf((val * f) - 0.5); // round up only if frac(x) strictly greater than 0.5
175 //originally (and more slowly):
176 //const float tmp = val * f;
177 //const float itmp = floorf(tmp);
178 //return rf * (itmp + ((tmp - itmp > 0.5f) ? 1.0f : 0.0f));
179}
180
182static inline float _rgb_to_gray(const float *const restrict val)
183{
184 return 0.30f * val[0] + 0.59f * val[1] + 0.11f * val[2]; // RGB -> GRAY
185}
186
188static inline void nearest_color(float *const restrict val, float *const restrict err, int graymode,
189 const float f, const float rf)
190{
191 if (graymode)
192 {
193 // dither pixel into gray, with f=levels-1 and rf=1/f, return err=old-new
194 const float in = _rgb_to_gray(val);
195 const float new = _quantize(in,f,rf);
196 __OMP_SIMD__(aligned(val, err : 16))
197 for(int c = 0; c < 4; c++)
198 {
199 err[c] = val[c] - new;
200 val[c] = new;
201 }
202 }
203 else
204 {
205 // dither pixel into RGB, with f=levels-1 and rf=1/f, return err=old-new
206 __OMP_SIMD__(aligned(val, err : 16))
207 for(int c = 0; c < 4; c++)
208 {
209 const float old = val[c];
210 const float new = _quantize(old, f, rf);
211 err[c] = old - new;
212 val[c] = new;
213 }
214 }
215}
216
217static inline void _diffuse_error(float *const restrict val, const float *const restrict err, const float factor)
218{
219 __OMP_SIMD__(aligned(val, err))
220 for(int c = 0; c < 4; c++)
221 {
222 val[c] += err[c] * factor;
223 }
224}
225
226#if defined(__x86_64__) || defined(__i386__)
227static inline void _diffuse_error_sse(float *val, const __m128 err, const float factor)
228{
229 _mm_store_ps(val, _mm_load_ps(val) + (err * _mm_set1_ps(factor))); // *val += err * factor
230}
231#endif
232
234static inline float clipnan(const float x)
235{
236 // convert NaN to 0.5, otherwise clamp to between 0.0 and 1.0
237 return (x > 0.0f) ? ((x < 1.0f) ? x // 0 < x < 1
238 : 1.0f // x >= 1
239 )
240 : isnan(x) ? 0.5f // x is NaN
241 : 0.0f; // x <= 0
242}
243
244static inline void clipnan_pixel(float *const restrict out, const float *const restrict in)
245{
246 __OMP_SIMD__(aligned(in, out : 16))
247 for (int c = 0; c < 4; c++)
248 out[c] = clipnan(in[c]);
249}
250
251// clipnan_pixel proves to vectorize just fine, so don't bother implementing a separate SSE version
252#define clipnan_pixel_sse clipnan_pixel
253
254static inline __attribute__((always_inline)) int get_dither_parameters(const dt_iop_dither_data_t *const data, const dt_dev_pixelpipe_t *const pipe,
255 const dt_dev_pixelpipe_iop_t *const piece,
256 const float scale, unsigned int *const restrict levels)
257{
258 int graymode = -1;
259 *levels = 65536;
260 const int l1 = floorf(1.0f + dt_log2f(1.0f / scale));
261 const int bds = (pipe->type != DT_DEV_PIXELPIPE_EXPORT) ? l1 * l1 : 1;
262
263 switch(data->dither_type)
264 {
265 case DITHER_FS1BIT:
266 graymode = 1;
267 *levels = MAX(2, MIN(bds + 1, 256));
268 break;
270 graymode = 1;
271 *levels = MAX(16, MIN(15 * bds + 1, 256));
272 break;
273 case DITHER_FS8BIT:
274 graymode = 0;
275 *levels = 256;
276 break;
277 case DITHER_FS16BIT:
278 graymode = 0;
279 *levels = 65536;
280 break;
281 case DITHER_FSAUTO:
282 switch(pipe->levels & IMAGEIO_CHANNEL_MASK)
283 {
284 case IMAGEIO_RGB:
285 graymode = 0;
286 break;
287 case IMAGEIO_GRAY:
288 graymode = 1;
289 break;
290 }
291
292 switch(pipe->levels & IMAGEIO_PREC_MASK)
293 {
294 case IMAGEIO_INT8:
295 *levels = 256;
296 break;
297 case IMAGEIO_INT12:
298 *levels = 4096;
299 break;
300 case IMAGEIO_INT16:
301 *levels = 65536;
302 break;
303 case IMAGEIO_BW:
304 *levels = 2;
305 break;
306 case IMAGEIO_INT32:
307 case IMAGEIO_FLOAT:
308 default:
309 graymode = -1;
310 break;
311 }
312 break;
313 case DITHER_RANDOM:
314 // this function won't ever be called for that type
315 // instead, process_random() will be called
316 __builtin_unreachable();
317 break;
318 }
319 return graymode;
320}
321
322// what fraction of the error to spread to each neighbor pixel
323#define RIGHT_WT (7.0f/16.0f)
324#define DOWNRIGHT_WT (1.0f/16.0f)
325#define DOWN_WT (5.0f/16.0f)
326#define DOWNLEFT_WT (3.0f/16.0f)
328static void process_floyd_steinberg(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe,
329 const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid,
330 const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
331{
332 const dt_iop_dither_data_t *const restrict data = (dt_iop_dither_data_t *)piece->data;
333
334 const int width = roi_in->width;
335 const int height = roi_in->height;
336 const float scale = roi_in->scale;
337
338 const float *const restrict in = (const float *)ivoid;
339 float *const restrict out = (float *)ovoid;
340
341 unsigned int levels = 1;
342 int graymode = get_dither_parameters(data, pipe, piece, scale, &levels);
343 if(graymode < 0)
344 {
345 for(int j = 0; j < height * width; j++)
346 clipnan_pixel(out + 4*j, in + 4*j);
347 return;
348 }
349
350 const float f = levels - 1;
351 const float rf = 1.0 / f;
353
354 // dither without error diffusion on very tiny images
355 if(width < 3 || height < 3)
356 {
357 for(int j = 0; j < height * width; j++)
358 {
359 clipnan_pixel(out + 4 * j, in + 4 * j);
360 nearest_color(out + 4 * j, err, graymode, f, rf);
361 }
362
364 dt_iop_alpha_copy(ivoid, ovoid, roi_out->width, roi_out->height);
365 return;
366 }
367
368 // offsets to neighboring pixels
369 const size_t right = 4;
370 const size_t downleft = 4 * (width-1);
371 const size_t down = 4 * width;
372 const size_t downright = 4 * (width+1);
373
374#define PROCESS_PIXEL_FULL(_pixel, inpix) \
375 { \
376 float *const pixel_ = (_pixel); \
377 nearest_color(pixel_, err, graymode, f, rf); /* quantize pixel */ \
378 clipnan_pixel(pixel_ + downright,(inpix) + downright); /* prepare downright for first access */ \
379 _diffuse_error(pixel_ + right, err, RIGHT_WT); /* diffuse quantization error to neighbors */ \
380 _diffuse_error(pixel_ + downleft, err, DOWNLEFT_WT); \
381 _diffuse_error(pixel_ + down, err, DOWN_WT); \
382 _diffuse_error(pixel_ + downright, err, DOWNRIGHT_WT); \
383 }
384
385#define PROCESS_PIXEL_LEFT(_pixel, inpix) \
386 { \
387 float *const pixel_ = (_pixel); \
388 nearest_color(pixel_, err, graymode, f, rf); /* quantize pixel */ \
389 clipnan_pixel(pixel_ + down,(inpix) + down); /* prepare down for first access */ \
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_ + down, err, DOWN_WT); \
393 _diffuse_error(pixel_ + downright, err, DOWNRIGHT_WT); \
394 }
395
396#define PROCESS_PIXEL_RIGHT(pixel) \
397 nearest_color(pixel, err, graymode, f, rf); /* quantize pixel */ \
398 _diffuse_error(pixel + downleft, err, DOWNLEFT_WT); /* diffuse quantization error to neighbors */ \
399 _diffuse_error(pixel + down, err, DOWN_WT);
400
401 // once the FS dithering gets started, we can copy&clip the downright pixel, as that will be the first time
402 // it will be accessed. But to get the process started, we need to prepare the top row of pixels
403 __OMP_SIMD__(aligned(in, out : 64))
404 for (int j = 0; j < width; j++)
405 {
406 clipnan_pixel(out + 4*j, in + 4*j);
407 }
408
409 // floyd-steinberg dithering follows here
410 // do the bulk of the image (all except the last row)
411 for(int j = 0; j < height - 1; j++)
412 {
413 const float *const restrict inrow = in + (size_t)4 * j * width;
414 float *const restrict outrow = out + (size_t)4 * j * width;
415
416 // first two columns
417 PROCESS_PIXEL_LEFT(outrow, inrow); // leftmost pixel in first (upper) row
418
419 // main part of the current row
420 for(int i = 1; i < width - 1; i++)
421 {
422 PROCESS_PIXEL_FULL(outrow + 4 * i, inrow + 4 * i);
423 }
424
425 // last column of upper row
426 PROCESS_PIXEL_RIGHT(outrow + 4 * (width-1));
427 }
428
429 // final row
430 {
431 float *const restrict outrow = out + (size_t)4 * (height - 1) * width;
432
433 // last row except for the right-most pixel
434 for(int i = 0; i < width - 1; i++)
435 {
436 float *const restrict pixel = outrow + 4 * i;
437 nearest_color(pixel, err, graymode, f, rf); // quantize the pixel
438 _diffuse_error(pixel + right, err, RIGHT_WT); // spread error to only remaining neighbor
439 }
440
441 // lower right pixel
442 nearest_color(outrow + 4 * (width - 1), err, graymode, f, rf); // quantize the last pixel, no neighbors left
443 }
444
445 // copy alpha channel if needed
447 dt_iop_alpha_copy(ivoid, ovoid, roi_out->width, roi_out->height);
448}
449
451static void process_random(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe,
452 const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid,
453 const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
454{
455 const dt_iop_dither_data_t *const data = (dt_iop_dither_data_t *)piece->data;
456
457 const int width = roi_in->width;
458 const int height = roi_in->height;
459 const float dither = powf(2.0f, data->random.damping / 10.0f);
460
461 unsigned int *const tea_states = alloc_tea_states(darktable.num_openmp_threads);
463 {
464 // get a pointer to each thread's private buffer *outside* the for loop, to avoid a function call per iteration
465 unsigned int *const tea_state = get_tea_state(tea_states,dt_get_thread_num());
467 for(int j = 0; j < height; j++)
468 {
469 const size_t k = (size_t)4 * width * j;
470 const float *const in = (const float *)ivoid + k;
471 float *const out = (float *)ovoid + k;
472 tea_state[0] = j * height; /* + dt_get_thread_num() -- do not include, makes results unreproducible */
473 for(int i = 0; i < width; i++)
474 {
475 encrypt_tea(tea_state);
476 float dith = dither * tpdf(tea_state[0]);
477 __OMP_SIMD__(aligned(in, out : 64))
478 for(int c = 0; c < 4; c++)
479 {
480 out[4*i+c] = CLIP(in[4*i+c] + dith);
481 }
482 }
483 }
484 }
485 free_tea_states(tea_states);
486
487 if(pipe->mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK) dt_iop_alpha_copy(ivoid, ovoid, width, height);
488}
489
490
491int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
492 void *const ovoid)
493{
494 const dt_iop_roi_t *const roi_in = &piece->roi_in;
495 const dt_iop_roi_t *const roi_out = &piece->roi_out;
497
498 if(data->dither_type == DITHER_RANDOM)
499 process_random(self, pipe, piece, ivoid, ovoid, roi_in, roi_out);
500 else
501 {
502 process_floyd_steinberg(self, pipe, piece, ivoid, ovoid, roi_in, roi_out);
503 }
504 return 0;
505}
506
507#ifdef HAVE_OPENCL
508int 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)
509{
510 const dt_iop_roi_t *const roi_in = &piece->roi_in;
511 const dt_iop_dither_data_t *const d = (dt_iop_dither_data_t *)piece->data;
513 cl_mem dev_work = NULL;
514
515 const int width = roi_in->width;
516 const int height = roi_in->height;
517 const int devid = pipe->devid;
518 const int preserve_alpha = (pipe->mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK) ? 1 : 0;
519 cl_int err = CL_SUCCESS;
520 size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
521
522 if(d->dither_type == DITHER_RANDOM)
523 {
524 const float dither = powf(2.0f, d->random.damping / 10.0f);
525 dt_opencl_set_kernel_arg(devid, gd->kernel_dither_random, 0, sizeof(cl_mem), (void *)&dev_in);
526 dt_opencl_set_kernel_arg(devid, gd->kernel_dither_random, 1, sizeof(cl_mem), (void *)&dev_out);
527 dt_opencl_set_kernel_arg(devid, gd->kernel_dither_random, 2, sizeof(int), (void *)&width);
528 dt_opencl_set_kernel_arg(devid, gd->kernel_dither_random, 3, sizeof(int), (void *)&height);
529 dt_opencl_set_kernel_arg(devid, gd->kernel_dither_random, 4, sizeof(float), (void *)&dither);
530 dt_opencl_set_kernel_arg(devid, gd->kernel_dither_random, 5, sizeof(int), (void *)&preserve_alpha);
531
532 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_dither_random, sizes);
533 if(err != CL_SUCCESS) goto error;
534 return TRUE;
535 }
536
537error:
539 dt_print(DT_DEBUG_OPENCL, "[opencl_dither] couldn't enqueue kernel! %d\n", err);
540 return FALSE;
541}
542#endif
543
544void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
545{
548
549 if(w == g->dither_type)
550 {
551 gtk_widget_set_visible(g->random, p->dither_type == DITHER_RANDOM);
552 }
553}
554
555#if 0
556static void
557radius_callback (GtkWidget *slider, gpointer user_data)
558{
559 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
560 if(darktable.gui->reset) return;
562 p->random.radius = dt_bauhaus_slider_get(slider);
564}
565#endif
566
567#if 0
568static void
569range_callback (GtkWidget *slider, gpointer user_data)
570{
571 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
572 if(darktable.gui->reset) return;
579}
580#endif
581
584{
587
588 d->dither_type = p->dither_type;
589 memcpy(&(d->random.range), &(p->random.range), sizeof(p->random.range));
590 d->random.radius = p->random.radius;
591 d->random.damping = p->random.damping;
592
593 // Floyd-Steinberg can't run on OpenCL because it's a super-serial algo
594 // But it's still slow on CPU
596 piece->enabled = FALSE;
597 else if (d->dither_type != DITHER_RANDOM)
598 piece->process_cl_ready = FALSE;
599}
600
602{
603 piece->data = dt_calloc_align(sizeof(dt_iop_dither_data_t));
604 piece->data_size = sizeof(dt_iop_dither_data_t);
605}
606
608{
609 dt_free_align(piece->data);
610 piece->data = NULL;
611}
612
614{
615 const int program = 8; // extended.cl, from programs.conf
617 if(IS_NULL_PTR(gd)) return;
618 module->data = gd;
619 gd->kernel_dither_random = dt_opencl_create_kernel(program, "dither_random");
620}
621
628
629
630void gui_update(struct dt_iop_module_t *self)
631{
634#if 0
635 dt_bauhaus_slider_set(g->radius, p->random.radius);
636
641#endif
642
643 gtk_widget_set_visible(g->random, p->dither_type == DITHER_RANDOM);
644}
645
646void gui_init(struct dt_iop_module_t *self)
647{
649
650 g->random = self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
651
652#if 0
653 g->radius = dt_bauhaus_slider_new_with_range(darktable.bauhaus, DT_GUI_MODULE(self), 0.0, 200.0, 0.1, p->random.radius, 2);
654 gtk_widget_set_tooltip_text(g->radius, _("radius for blurring step"));
655 dt_bauhaus_widget_set_label(g->radius, N_("radius"));
656
666 gtk_widget_set_tooltip_text(g->range, _("the gradient range where to apply random dither"));
667 g->range_label = gtk_label_new(_("gradient range"));
668
669 GtkWidget *rlabel = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
670 gtk_box_pack_start(GTK_BOX(rlabel), GTK_WIDGET(g->range_label), FALSE, FALSE, 0);
671#endif
672
673 g->damping = dt_bauhaus_slider_from_params(self, "random.damping");
674
675 gtk_widget_set_tooltip_text(g->damping, _("damping level of random dither"));
676 dt_bauhaus_slider_set_digits(g->damping, 3);
677 dt_bauhaus_slider_set_format(g->damping, " dB");
678
679#if 0
680 gtk_box_pack_start(GTK_BOX(g->random), g->radius, TRUE, TRUE, 0);
681 gtk_box_pack_start(GTK_BOX(g->random), rlabel, TRUE, TRUE, 0);
682 gtk_box_pack_start(GTK_BOX(g->random), g->range, TRUE, TRUE, 0);
683#endif
684
685 self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
686
687 g->dither_type = dt_bauhaus_combobox_from_params(self, "dither_type");
688
689 gtk_box_pack_start(GTK_BOX(self->widget), g->random, TRUE, TRUE, 0);
690
691#if 0
692 g_signal_connect (G_OBJECT (g->radius), "value-changed",
693 G_CALLBACK (radius_callback), self);
694 g_signal_connect (G_OBJECT (g->range), "value-changed",
695 G_CALLBACK (range_callback), self);
696#endif
697}
698
699// clang-format off
700// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
701// vim: shiftwidth=2 expandtab tabstop=2 cindent
702// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
703// 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:635
void dt_bauhaus_slider_set_digits(GtkWidget *widget, int val)
Definition bauhaus.c:3534
float dt_bauhaus_slider_get(GtkWidget *widget)
Definition bauhaus.c:3483
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3506
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
Definition bauhaus.c:1653
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:1780
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
Definition bauhaus.c:3598
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
@ DEVELOP_BLEND_CS_NONE
Definition blend.h:56
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
const dt_aligned_pixel_t f
const dt_colormatrix_t dt_aligned_pixel_t out
darktable_t darktable
Definition darktable.c:181
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1542
#define dt_free_align(ptr)
Definition darktable.h:481
static void * dt_calloc_align(size_t size)
Definition darktable.h:488
#define __OMP_SIMD__(...)
Definition darktable.h:262
@ DT_DEBUG_OPENCL
Definition darktable.h:722
#define __OMP_FOR__(...)
Definition darktable.h:261
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Enable aggressive floating-point arithmetic optimizations, in denormals handling. Set through user pr...
Definition darktable.h:524
static int dt_get_thread_num()
Definition darktable.h:291
#define dt_free(ptr)
Definition darktable.h:456
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
Definition darktable.h:151
#define __OMP_DECLARE_SIMD__(...)
Definition darktable.h:263
#define __OMP_PARALLEL__(...)
Definition darktable.h:257
#define __DT_CLONE_TARGETS__
Definition darktable.h:367
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:281
#define dt_database_start_transaction(db)
Definition database.h:77
#define dt_database_release_transaction(db)
Definition database.h:78
#define dt_dev_add_history_item(dev, module, enable, redraw)
void dt_iop_params_t
Definition dev_history.h:41
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
Definition develop.h:118
static float _quantize(const float val, const float f, const float rf)
Definition dither.c:172
#define RIGHT_WT
Definition dither.c:323
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:582
const char ** description(struct dt_iop_module_t *self)
Definition dither.c:123
int default_group()
Definition dither.c:132
void reload_defaults(dt_iop_module_t *module)
Definition dither.c:164
static void nearest_color(float *const restrict val, float *const restrict err, int graymode, const float f, const float rf)
Definition dither.c:188
dt_iop_dither_type_t
Definition dither.c:69
@ DITHER_RANDOM
Definition dither.c:70
@ DITHER_FS16BIT
Definition dither.c:74
@ DITHER_FSAUTO
Definition dither.c:75
@ DITHER_FS1BIT
Definition dither.c:71
@ DITHER_FS8BIT
Definition dither.c:73
@ DITHER_FS4BIT_GRAY
Definition dither.c:72
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:451
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition dither.c:601
static void clipnan_pixel(float *const restrict out, const float *const restrict in)
Definition dither.c:244
const char * name()
Definition dither.c:118
#define PROCESS_PIXEL_LEFT(_pixel, inpix)
void gui_update(struct dt_iop_module_t *self)
Definition dither.c:630
void gui_init(struct dt_iop_module_t *self)
Definition dither.c:646
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:328
void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
Definition dither.c:544
static float _rgb_to_gray(const float *const restrict val)
Definition dither.c:182
void cleanup_global(dt_iop_module_so_t *module)
Definition dither.c:622
static void _diffuse_error(float *const restrict val, const float *const restrict err, const float factor)
Definition dither.c:217
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition dither.c:142
int flags()
Definition dither.c:137
#define PROCESS_PIXEL_FULL(_pixel, inpix)
void init_presets(dt_iop_module_so_t *self)
Definition dither.c:147
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:491
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition dither.c:607
void init_global(dt_iop_module_so_t *module)
Definition dither.c:613
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:508
static float clipnan(const float x)
Definition dither.c:234
#define PROCESS_PIXEL_RIGHT(pixel)
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
#define DT_GUI_BOX_SPACING
Definition gtk.h:109
void dt_gui_presets_add_generic(const char *name, dt_dev_operation_t op, const int32_t version, const void *params, const int32_t params_size, const int32_t enabled, const dt_develop_blend_colorspace_t blend_cst)
#define DT_GUI_MODULE(x)
@ IMAGEIO_INT32
Definition imageio.h:65
@ IMAGEIO_INT16
Definition imageio.h:64
@ IMAGEIO_PREC_MASK
Definition imageio.h:68
@ IMAGEIO_RGB
Definition imageio.h:70
@ IMAGEIO_INT12
Definition imageio.h:63
@ IMAGEIO_INT8
Definition imageio.h:62
@ IMAGEIO_CHANNEL_MASK
Definition imageio.h:72
@ IMAGEIO_GRAY
Definition imageio.h:71
@ IMAGEIO_FLOAT
Definition imageio.h:66
@ IMAGEIO_BW
Definition imageio.h:67
const char ** dt_iop_set_description(dt_iop_module_t *module, const char *main_text, const char *purpose, const char *input, const char *process, const char *output)
Definition imageop.c:3141
@ IOP_FLAGS_ONE_INSTANCE
Definition imageop.h:172
@ IOP_CS_RGB_DISPLAY
Definition imageop.h:210
@ IOP_GROUP_TECHNICAL
Definition imageop.h:143
#define IOP_GUI_ALLOC(module)
Definition imageop.h:599
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
Definition imageop_gui.c:77
GtkWidget * dt_bauhaus_combobox_from_params(dt_iop_module_t *self, const char *param)
void *const ovoid
static const float x
const float l1
float *const restrict const size_t k
#define CLIP(x)
Definition math.h:81
float dt_aligned_pixel_t[4]
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
Definition opencl.c:2136
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2030
void dt_opencl_free_kernel(const int kernel)
Definition opencl.c:2073
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:2127
void dt_opencl_release_mem_object(cl_mem mem)
Definition opencl.c:2383
#define ROUNDUPDHT(a, b)
Definition opencl.h:82
#define ROUNDUPDWD(a, b)
Definition opencl.h:81
const float factor
Definition pdf.h:90
@ DT_DEV_PIXELPIPE_EXPORT
Definition pixelpipe.h:38
gboolean dt_dev_pixelpipe_get_realtime(const dt_dev_pixelpipe_t *pipe)
struct _GtkWidget GtkWidget
Definition splash.h:29
int32_t num_openmp_threads
Definition darktable.h:758
struct dt_gui_gtk_t * gui
Definition darktable.h:775
const struct dt_database_t * db
Definition darktable.h:779
struct dt_bauhaus_t * bauhaus
Definition darktable.h:778
struct dt_develop_t * develop
Definition darktable.h:770
struct dt_iop_module_t *void * data
dt_imageio_levels_t levels
dt_dev_pixelpipe_type_t type
int32_t reset
Definition gtk.h:172
dt_iop_dither_type_t dither_type
Definition dither.c:103
struct dt_iop_dither_data_t::@55 random
GtkWidget * damping
Definition dither.c:98
GtkWidget * range
Definition dither.c:96
GtkWidget * random
Definition dither.c:94
GtkWidget * dither_type
Definition dither.c:93
GtkWidget * radius
Definition dither.c:95
GtkWidget * range_label
Definition dither.c:97
struct dt_iop_dither_params_t::@54 random
dt_iop_dither_type_t dither_type
Definition dither.c:81
GModule *dt_dev_operation_t op
Definition imageop.h:230
dt_iop_global_data_t * data
Definition imageop.h:233
GtkWidget * widget
Definition imageop.h:337
dt_iop_gui_data_t * gui_data
Definition imageop.h:311
dt_iop_global_data_t * global_data
Definition imageop.h:314
dt_iop_params_t * params
Definition imageop.h:307
Region of interest passed through the pixelpipe.
Definition imageop.h:72
double scale
Definition imageop.h:74
static float tpdf(unsigned int urandom)
Definition tea.h:71
static unsigned int * get_tea_state(unsigned int *const states, int threadnum)
Definition tea.h:40
static void free_tea_states(unsigned int *states)
Definition tea.h:45
static unsigned int * alloc_tea_states(size_t numthreads)
Definition tea.h:31
static void encrypt_tea(unsigned int *arg)
Definition tea.h:55
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29