123 return _(
"dithering");
128 return dt_iop_set_description(self, _(
"reduce banding and posterization effects in output JPEGs by adding random noise"),
130 _(
"non-linear, RGB, display-referred"),
131 _(
"non-linear, RGB"),
132 _(
"non-linear, RGB, display-referred"));
169 module->default_enabled = TRUE;
174static inline
float _quantize(const
float val, const
float f, const
float rf)
176 return rf * ceilf((val *
f) - 0.5);
186 return 0.30f * val[0] + 0.59f * val[1] + 0.11f * val[2];
190static inline
void nearest_color(
float *const restrict val,
float *const restrict err,
int graymode,
191 const
float f, const
float rf)
199 for(
int c = 0; c < 4; c++)
201 err[c] = val[c] -
new;
209 for(
int c = 0; c < 4; c++)
211 const float old = val[c];
219static inline void _diffuse_error(
float *
const restrict val,
const float *
const restrict err,
const float factor)
222 for(
int c = 0; c < 4; c++)
224 val[c] += err[c] *
factor;
228#if defined(__x86_64__) || defined(__i386__)
229static inline void _diffuse_error_sse(
float *val,
const __m128 err,
const float factor)
231 _mm_store_ps(val, _mm_load_ps(val) + (err * _mm_set1_ps(
factor)));
239 return (
x > 0.0f) ? ((
x < 1.0f) ?
x
249 for (
int c = 0; c < 4; c++)
254#define clipnan_pixel_sse clipnan_pixel
258 const float scale,
unsigned int *
const restrict
levels)
262 const int l1 = floorf(1.0f + dt_log2f(1.0f / scale));
318 __builtin_unreachable();
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)
338 const float scale = roi_in->
scale;
340 const float *
const restrict in = (
const float *)ivoid;
341 float *
const restrict
out = (
float *)
ovoid;
344 int graymode = get_dither_parameters(data, pipe, piece, scale, &
levels);
356 const float rf = 1.0 /
f;
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);
379#define PROCESS_PIXEL_FULL(_pixel, inpix) \
381 float *const pixel_ = (_pixel); \
382 nearest_color(pixel_, err, graymode, f, rf); \
383 clipnan_pixel(pixel_ + downright,(inpix) + downright); \
384 _diffuse_error(pixel_ + right, err, RIGHT_WT); \
385 _diffuse_error(pixel_ + downleft, err, DOWNLEFT_WT); \
386 _diffuse_error(pixel_ + down, err, DOWN_WT); \
387 _diffuse_error(pixel_ + downright, err, DOWNRIGHT_WT); \
390#define PROCESS_PIXEL_LEFT(_pixel, inpix) \
392 float *const pixel_ = (_pixel); \
393 nearest_color(pixel_, err, graymode, f, rf); \
394 clipnan_pixel(pixel_ + down,(inpix) + down); \
395 clipnan_pixel(pixel_ + downright,(inpix) + downright); \
396 _diffuse_error(pixel_ + right, err, RIGHT_WT); \
397 _diffuse_error(pixel_ + down, err, DOWN_WT); \
398 _diffuse_error(pixel_ + downright, err, DOWNRIGHT_WT); \
401#define PROCESS_PIXEL_RIGHT(pixel) \
402 nearest_color(pixel, err, graymode, f, rf); \
403 _diffuse_error(pixel + downleft, err, DOWNLEFT_WT); \
404 _diffuse_error(pixel + down, err, DOWN_WT);
409 for (
int j = 0; j <
width; j++)
416 for(
int j = 0; j <
height - 1; j++)
418 const float *
const restrict inrow = in + (size_t)4 * j *
width;
419 float *
const restrict outrow =
out + (size_t)4 * j *
width;
436 float *
const restrict outrow =
out + (size_t)4 * (
height - 1) *
width;
441 float *
const restrict pixel = outrow + 4 *
i;
464 const float dither = powf(2.0f, data->
random.
damping / 10.0f);
472 for(
int j = 0; j <
height; j++)
474 const size_t k = (size_t)4 *
width * j;
475 const float *
const in = (
const float *)ivoid +
k;
477 tea_state[0] = j *
height;
481 float dith = dither *
tpdf(tea_state[0]);
483 for(
int c = 0; c < 4; c++)
518 cl_mem dev_work = NULL;
522 const int devid = pipe->
devid;
524 cl_int err = CL_SUCCESS;
529 const float dither = powf(2.0f,
d->random.damping / 10.0f);
538 if(err != CL_SUCCESS)
goto error;
554 if(w ==
g->dither_type)
562radius_callback (
GtkWidget *slider, gpointer user_data)
574range_callback (
GtkWidget *slider, gpointer user_data)
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;
629 const int program = 8;
668 gtk_widget_set_tooltip_text(
g->radius, _(
"radius for blurring step"));
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"));
684 gtk_box_pack_start(GTK_BOX(rlabel), GTK_WIDGET(
g->range_label),
FALSE,
FALSE, 0);
689 gtk_widget_set_tooltip_text(
g->damping, _(
"damping level of random dither"));
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);
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);
static void error(char *msg)
int levels(struct dt_imageio_module_data_t *data)
void dt_bauhaus_slider_set_digits(GtkWidget *widget, int val)
float dt_bauhaus_slider_get(GtkWidget *widget)
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
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)
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
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.
struct dt_bauhaus_t * dt_bauhaus_get_global(void)
#define dt_database_start_transaction()
#define dt_database_release_transaction()
#define dt_dev_add_history_item(dev, module, enable, redraw)
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
static float _quantize(const float val, const float f, const float rf)
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)
const char ** description(struct dt_iop_module_t *self)
void reload_defaults(dt_iop_module_t *module)
static void nearest_color(float *const restrict val, float *const restrict err, int graymode, const float f, const float rf)
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)
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static void clipnan_pixel(float *const restrict out, const float *const restrict in)
#define PROCESS_PIXEL_LEFT(_pixel, inpix)
void gui_update(struct dt_iop_module_t *self)
void gui_init(struct dt_iop_module_t *self)
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)
void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
static float _rgb_to_gray(const float *const restrict val)
void cleanup_global(dt_iop_module_so_t *module)
static void _diffuse_error(float *const restrict val, const float *const restrict err, const float factor)
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
#define PROCESS_PIXEL_FULL(_pixel, inpix)
void init_presets(dt_iop_module_so_t *self)
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)
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
void init_global(dt_iop_module_so_t *module)
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)
static float clipnan(const float x)
#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
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)
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)
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)
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...
#define IOP_GUI_ALLOC(module)
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...
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
static void * dt_calloc_align(size_t size)
dt_alloc_align() followed by a zero fill.
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
#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)
int dt_opencl_create_kernel(const int prog, const char *name)
void dt_opencl_free_kernel(const int kernel)
int dt_opencl_set_kernel_arg(const int dev, const int kernel, const int num, const size_t size, const void *arg)
void dt_opencl_release_mem_object(cl_mem mem)
#define __OMP_SIMD__(...)
static int dt_get_thread_num()
Index of the calling thread within its parallel region, 0 outside one.
#define __OMP_DECLARE_SIMD__(...)
#define __OMP_PARALLEL__(...)
@ DT_DEV_PIXELPIPE_EXPORT
gboolean dt_dev_pixelpipe_get_realtime(const dt_dev_pixelpipe_t *pipe)
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
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...
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
struct dt_iop_dither_data_t::@41 random
dt_iop_dither_type_t dither_type
struct dt_iop_dither_params_t::@40 random
dt_iop_global_data_t * data
struct dt_iop_module_gui_t * gui
struct dt_develop_t * dev
dt_iop_global_data_t * global_data
Region of interest passed through the pixelpipe.
#define __DT_CLONE_TARGETS__
static float tpdf(unsigned int urandom)
static unsigned int * get_tea_state(unsigned int *const states, int threadnum)
static void free_tea_states(unsigned int *states)
static unsigned int * alloc_tea_states(size_t numthreads)
static void encrypt_tea(unsigned int *arg)