129 return _(
"dithering");
134 return dt_iop_set_description(self, _(
"reduce banding and posterization effects in output JPEGs by adding random noise"),
136 _(
"non-linear, RGB, display-referred"),
137 _(
"non-linear, RGB"),
138 _(
"non-linear, RGB, display-referred"));
175 module->default_enabled = TRUE;
180static inline
float _quantize(const
float val, const
float f, const
float rf)
182 return rf * ceilf((val *
f) - 0.5);
192 return 0.30f * val[0] + 0.59f * val[1] + 0.11f * val[2];
196static inline
void nearest_color(
float *const restrict val,
float *const restrict err,
int graymode,
197 const
float f, const
float rf)
205 for(
int c = 0; c < 4; c++)
207 err[c] = val[c] -
new;
215 for(
int c = 0; c < 4; c++)
217 const float old = val[c];
225static inline void _diffuse_error(
float *
const restrict val,
const float *
const restrict err,
const float factor)
228 for(
int c = 0; c < 4; c++)
230 val[c] += err[c] *
factor;
234#if defined(__x86_64__) || defined(__i386__)
235static inline void _diffuse_error_sse(
float *val,
const __m128 err,
const float factor)
237 _mm_store_ps(val, _mm_load_ps(val) + (err * _mm_set1_ps(
factor)));
245 return (
x > 0.0f) ? ((
x < 1.0f) ?
x
255 for (
int c = 0; c < 4; c++)
260#define clipnan_pixel_sse clipnan_pixel
263 const float scale,
unsigned int *
const restrict
levels)
267 const int l1 = floorf(1.0f + dt_log2f(1.0f / scale));
325 __builtin_unreachable();
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)
345 const float scale = roi_in->
scale;
347 const float *
const restrict in = (
const float *)ivoid;
348 float *
const restrict
out = (
float *)
ovoid;
351 int graymode = get_dither_parameters(data, scale, &
levels);
363 const float rf = 1.0 /
f;
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);
386#define PROCESS_PIXEL_FULL(_pixel, inpix) \
388 float *const pixel_ = (_pixel); \
389 nearest_color(pixel_, err, graymode, f, rf); \
390 clipnan_pixel(pixel_ + downright,(inpix) + downright); \
391 _diffuse_error(pixel_ + right, err, RIGHT_WT); \
392 _diffuse_error(pixel_ + downleft, err, DOWNLEFT_WT); \
393 _diffuse_error(pixel_ + down, err, DOWN_WT); \
394 _diffuse_error(pixel_ + downright, err, DOWNRIGHT_WT); \
397#define PROCESS_PIXEL_LEFT(_pixel, inpix) \
399 float *const pixel_ = (_pixel); \
400 nearest_color(pixel_, err, graymode, f, rf); \
401 clipnan_pixel(pixel_ + down,(inpix) + down); \
402 clipnan_pixel(pixel_ + downright,(inpix) + downright); \
403 _diffuse_error(pixel_ + right, err, RIGHT_WT); \
404 _diffuse_error(pixel_ + down, err, DOWN_WT); \
405 _diffuse_error(pixel_ + downright, err, DOWNRIGHT_WT); \
408#define PROCESS_PIXEL_RIGHT(pixel) \
409 nearest_color(pixel, err, graymode, f, rf); \
410 _diffuse_error(pixel + downleft, err, DOWNLEFT_WT); \
411 _diffuse_error(pixel + down, err, DOWN_WT);
416 for (
int j = 0; j <
width; j++)
423 for(
int j = 0; j <
height - 1; j++)
425 const float *
const restrict inrow = in + (size_t)4 * j *
width;
426 float *
const restrict outrow =
out + (size_t)4 * j *
width;
443 float *
const restrict outrow =
out + (size_t)4 * (
height - 1) *
width;
448 float *
const restrict pixel = outrow + 4 *
i;
471 const float dither = powf(2.0f, data->
random.
damping / 10.0f);
479 for(
int j = 0; j <
height; j++)
481 const size_t k = (size_t)4 *
width * j;
482 const float *
const in = (
const float *)ivoid +
k;
484 tea_state[0] = j *
height;
488 float dith = dither *
tpdf(tea_state[0]);
490 for(
int c = 0; c < 4; c++)
525 cl_mem dev_work = NULL;
531 cl_int err = CL_SUCCESS;
536 const float dither = powf(2.0f,
d->random.damping / 10.0f);
545 if(err != CL_SUCCESS)
goto error;
561 if(w ==
g->dither_type)
569radius_callback (
GtkWidget *slider, gpointer user_data)
581range_callback (
GtkWidget *slider, gpointer user_data)
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;
645 const int program = 8;
684 gtk_widget_set_tooltip_text(
g->radius, _(
"radius for blurring step"));
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"));
700 gtk_box_pack_start(GTK_BOX(rlabel), GTK_WIDGET(
g->range_label),
FALSE,
FALSE, 0);
705 gtk_widget_set_tooltip_text(
g->damping, _(
"damping level of random dither"));
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);
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);
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)
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)))
#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)
static const dt_dev_pixelpipe_t * pipe
static const dt_dev_pixelpipe_t const dt_dev_pixelpipe_iop_t * piece
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...
dt_imageio_levels_t levels
dt_dev_pixelpipe_type_t type
dt_iop_dither_type_t dither_type
struct dt_iop_dither_data_t::@42 random
dt_imageio_levels_t output_levels
struct dt_iop_dither_params_t::@41 random
dt_iop_dither_type_t dither_type
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)