101 return _(
"blur|lens|motion");
107 _(
"simulate physically-accurate lens and motion blurs"),
108 _(
"creative"), _(
"linear, RGB, scene-referred"), _(
"linear, RGB"),
109 _(
"linear, RGB, scene-referred"));
144 for(
size_t j = 0; j <
width; j++)
146 const size_t index = (
i *
width + j);
149 for(
size_t ii = 0; ii <
FSIZE; ++ii)
150 for(
size_t jj = 0; jj <
FSIZE; ++jj)
152 const size_t row = CLAMP((
int)
i + (
int)(ii - (
FSIZE - 1) / 2), (
int)0, (
int)
height - 1);
153 const size_t col = CLAMP((
int)j + (
int)(jj - (
FSIZE - 1) / 2), (
int)0, (
int)
width - 1);
154 const size_t k_index = (
row *
width + col);
157 = { 1.0f / 16.0f, 4.0f / 16.0f, 6.0f / 16.0f, 4.0f / 16.0f, 1.0f / 16.0f };
159 acc += filter[ii] * filter[jj] * in[k_index];
179 const size_t height,
const float n,
const float m,
180 const float k,
const float rotation)
189 const float eps = 1.f / (float)
width;
190 const float radius = (float)(
width - 1) / 2.f - 1;
193 for(
size_t j = 0; j <
width; j++)
196 const float x = (float)(
i - 1) / radius - 1;
197 const float y = (float)(j - 1) / radius - 1;
200 const float r = dt_fast_hypotf(
x, y);
203 const float M = cosf((2.f * asinf(
k) +
M_PI_F *
m) / (2.f *
n))
204 / cosf((2.f * asinf(
k * cosf(
n * (atan2f(y,
x) + rotation))) +
M_PI_F *
m) / (2.f *
n));
214 const size_t height,
const float angle,
215 const float curvature,
const float offset)
218 const float A = curvature / 2.f;
220 const float C = -
A * offset * offset +
B * offset;
226 const float eps = 1.f / (float)
width;
228 const float radius = (float)(
width - 1) / 2.f - 1;
229 const float corr_angle = -
M_PI_F / 4.f - angle;
232 const float M[2][2] = { { cosf(corr_angle), -sinf(corr_angle) },
233 { sinf(corr_angle), cosf(corr_angle) } };
235 for(
size_t i = 0;
i < 8 *
width;
i++)
242 const float x = (float)(
i / 8.f - 1) / radius - 1;
246 const float X =
x - offset;
247 const float y =
X *
X *
A +
X *
B +
C;
250 const float rot_x =
x *
M[0][0] + y *
M[0][1];
251 const float rot_y =
x *
M[1][0] + y *
M[1][1];
254 const int y_f[2] = { roundf((rot_y + 1) * radius -
eps),
255 roundf((rot_y + 1) * radius +
eps) };
256 const int x_f[2] = { roundf((rot_x + 1) * radius -
eps),
257 roundf((rot_x + 1) * radius +
eps) };
261 for(
int l = 0; l < 2; l++)
262 for(
int m = 0;
m < 2;
m++)
264 if(x_f[l] > 0 && x_f[l] <
width - 1 && y_f[
m] > 0 && y_f[
m] <
width - 1)
265 buffer[y_f[
m] *
width + x_f[l]] = 1.f;
277 const float radius = (
width - 1) / 2.f - 1;
280 for(
size_t j = 0; j <
width; j++)
283 const float x = (float)(
i - 1) / radius - 1;
284 const float y = (float)(j - 1) / radius - 1;
287 const float r_2 =
x *
x + y * y;
288 buffer[
i *
width + j] = expf(-4.f * r_2);
326 buffer[
k * 4] = buffer[
k * 4 + 1] = buffer[
k * 4 + 2] = buffer[
k * 4 + 3] = roundf(255.f * kernel_2[
k]);
405 const void *
const restrict ivoid,
void *
const restrict
ovoid,
411 const float *
const restrict in = __builtin_assume_aligned(ivoid, 64);
415 const int is_width_even = (roi_in->width % 2 == 0);
416 const int is_height_even = (roi_in->height % 2 == 0);
417 const size_t padded_width = roi_in->width + 1 * is_width_even;
418 const size_t padded_height = roi_in->height + 1 * is_height_even;
425 for(size_t
i = 0;
i < roi_in->height;
i++)
426 for(
size_t j = 0; j < roi_in->width; j++)
428 const size_t index_in = (
i * roi_in->width + j) * 4;
429 const size_t index_out = (
i * padded_width + j) * 4;
430 for_four_channels(c, aligned(in, padded_in : 64)) padded_in[index_out +
c] = in[index_in +
c];
434 if(padded_width > roi_in->width)
437 for(size_t
i = 0;
i < roi_in->height;
i++)
439 const size_t index_in = (
i * (roi_in->width - 1)) * 4;
440 const size_t index_out = (
i * (padded_width - 1)) * 4;
441 for_four_channels(c, aligned(in, padded_in : 64)) padded_in[index_out +
c] = in[index_in +
c];
445 if(padded_height > roi_in->height)
448 for(size_t j = 0; j < roi_in->width; j++)
450 const size_t index_in = ((roi_in->height - 1) * roi_in->width + j) * 4;
451 const size_t index_out = ((padded_height - 1) * padded_width + j) * 4;
452 for_four_channels(c, aligned(in, padded_in : 64)) padded_in[index_out +
c] = in[index_in +
c];
457 const size_t radius =
MAX(roundf(
p->radius / scale), 1);
458 const size_t kernel_width = 2 * radius + 1;
465 const size_t offset_i = (padded_height - 1) / 2 - (kernel_width - 1) / 2;
466 const size_t offset_j = (padded_width - 1) / 2 - (kernel_width - 1) / 2;
467 const size_t i_reach = offset_i + kernel_width;
468 const size_t j_reach = offset_j + kernel_width;
470 for(size_t
i = 0;
i < padded_width;
i++)
471 for(
size_t j = 0; j < padded_width; j++)
473 const size_t padded_idx = (
i * padded_width) + j;
475 if(
i >= offset_i && i < i_reach && j >= offset_j && j < j_reach)
477 const size_t i_kern =
i - offset_i;
478 const size_t j_kern = j - offset_j;
479 const size_t kern_idx = i_kern * kernel_width + j_kern;
480 padded_kernel[padded_idx] =
kernel[kern_idx];
483 padded_kernel[padded_idx] = 0.f;
487 int threads = fftw_init_threads();
488 fftw_plan_with_nthreads(threads);
495 int n[2] = { padded_width, padded_height };
504 fftwf_complex *
const restrict kernel_fft = fftwf_alloc_complex(padded_height * padded_height * 4);
505 fftwf_complex *
const restrict image_fft = fftwf_alloc_complex(padded_height * padded_height * 4);
508 fftwf_plan kernel_plan = fftwf_plan_many_dft_r2c(rank,
n, howmany,
509 padded_kernel, inembed,
514 fftwf_execute(kernel_plan);
517 fftwf_destroy_plan(kernel_plan);
518 fftwf_free(kernel_fft);
519 fftwf_free(image_fft);
520 fftw_cleanup_threads();
534 const void *
const restrict ivoid,
void *
const restrict
ovoid)
541 const float *
const restrict in = __builtin_assume_aligned(ivoid, 64);
542 float *
const restrict
out = __builtin_assume_aligned(
ovoid, 64);
545 const int radius =
MAX(roundf(
p->radius / scale), 2);
546 const size_t kernel_width = 2 * radius + 1;
568 for(
int j = 0; j < roi_out->
width; j++)
570 const size_t index = ((
i * roi_out->
width) + j) * 4;
573 if(
i >= radius && j >= radius && i < roi_out->
height - radius && j < roi_out->
width - radius)
576 for(
int l = -radius; l <= radius; l++)
577 for(
int m = -radius;
m <= radius;
m++)
579 const int ii =
i + l;
580 const int jj = j +
m;
581 const size_t idx_shift = ((ii * roi_out->
width) + jj) * 4;
583 const int ik = l + radius;
584 const int jk =
m + radius;
585 const size_t idx_kernel = (ik * kernel_width) + jk;
586 const float k =
kernel[idx_kernel];
595 for(
int l = -radius; l <= radius; l++)
596 for(
int m = -radius;
m <= radius;
m++)
598 const int ii = CLAMP((
int)
i + l, (
int)0, (
int)roi_out->
height - 1);
599 const int jj = CLAMP((
int)j +
m, (
int)0, (
int)roi_out->
width - 1);
600 const size_t idx_shift = ((ii * roi_out->
width) + jj) * 4;
602 const int ik = l + radius;
603 const int jk =
m + radius;
604 const size_t idx_kernel = (ik * kernel_width) + jk;
605 const float k =
kernel[idx_kernel];
614 out[index + 3] = in[index + 3];
640 const int radius =
MAX(roundf(
p->radius / scale), 2);
641 const size_t kernel_width = 2 * radius + 1;
661 if(err != CL_SUCCESS)
goto error;
677 const int program = 34;
702 gtk_widget_hide(
g->angle);
703 gtk_widget_hide(
g->curvature);
704 gtk_widget_hide(
g->offset);
706 gtk_widget_show(
g->blades);
707 gtk_widget_show(
g->concavity);
708 gtk_widget_show(
g->rotation);
709 gtk_widget_show(
g->linearity);
713 gtk_widget_show(
g->angle);
714 gtk_widget_show(
g->curvature);
715 gtk_widget_show(
g->offset);
717 gtk_widget_hide(
g->blades);
718 gtk_widget_hide(
g->concavity);
719 gtk_widget_hide(
g->rotation);
720 gtk_widget_hide(
g->linearity);
724 gtk_widget_hide(
g->angle);
725 gtk_widget_hide(
g->curvature);
726 gtk_widget_hide(
g->offset);
728 gtk_widget_hide(
g->blades);
729 gtk_widget_hide(
g->concavity);
730 gtk_widget_hide(
g->rotation);
731 gtk_widget_hide(
g->linearity);
739 gtk_widget_queue_draw(GTK_WIDGET(
g->area));
749 GtkAllocation allocation;
750 GtkStyleContext *context = gtk_widget_get_style_context(widget);
751 gtk_widget_get_allocation(widget, &allocation);
752 gtk_render_background(context, crf, 0, 0, allocation.width, allocation.height);
754 if(allocation.width !=
g->img_width)
764 g->img =
dt_alloc_align(
sizeof(
unsigned char) * 4 * allocation.width * allocation.width);
766 g->img_width = allocation.width;
767 g->img_cached =
TRUE;
777 const int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32,
g->img_width);
778 cairo_surface_t *cst = cairo_image_surface_create_for_data(
g->img, CAIRO_FORMAT_ARGB32,
779 g->img_width,
g->img_width, stride);
781 cairo_set_source_surface(crf, cst, 0, 0);
783 cairo_surface_destroy(cst);
793#define DEG_TO_RAD 180.f / M_PI_F
static void error(char *msg)
void dt_atomic_set_int(dt_atomic_int *var, int value)
int dt_atomic_get_int(dt_atomic_int *var)
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
void dt_bauhaus_slider_set_factor(GtkWidget *widget, float factor)
__DT_CLONE_TARGETS__ int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const restrict ivoid, void *const restrict ovoid)
const char ** description(struct dt_iop_module_t *self)
static gboolean dt_iop_tonecurve_draw(GtkWidget *widget, cairo_t *crf, gpointer user_data)
void gui_update(dt_iop_module_t *self)
static __DT_CLONE_TARGETS__ void create_gauss_kernel(float *const restrict buffer, const size_t width, const size_t height)
static __DT_CLONE_TARGETS__ void normalize(float *const buffer, const size_t width, const size_t height, const float norm)
static __DT_CLONE_TARGETS__ int build_gui_kernel(unsigned char *const buffer, const size_t width, const size_t height, dt_iop_blurs_params_t *p)
static __DT_CLONE_TARGETS__ void init_kernel(float *const restrict buffer, const size_t width, const size_t height)
void gui_init(dt_iop_module_t *self)
static void blur_2D_Bspline(const float *const restrict in, float *const restrict out, const size_t width, const size_t height)
void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
void commit_params(dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
void gui_cleanup(dt_iop_module_t *self)
void cleanup_global(dt_iop_module_so_t *module)
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
static __DT_CLONE_TARGETS__ float compute_norm(float *const buffer, const size_t width, const size_t height)
static __DT_CLONE_TARGETS__ void create_motion_kernel(float *const restrict buffer, const size_t width, const size_t height, const float angle, const float curvature, const float offset)
static int build_pixel_kernel(float *const buffer, const size_t width, const size_t height, dt_iop_blurs_params_t *p)
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 __DT_CLONE_TARGETS__ void create_lens_kernel(float *const restrict buffer, const size_t width, const size_t height, const float n, const float m, const float k, const float rotation)
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
const dt_colormatrix_t dt_aligned_pixel_t out
static const float const float C
for(size_t c=0;c< 3;c++) sRGB[c]
static const dt_colormatrix_t M
void * dt_alloc_align(size_t size)
Allocate cacheline-aligned memory.
GtkWidget * dtgtk_drawing_area_new_with_aspect_ratio(double aspect)
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)
float dt_dev_get_module_scale(const dt_dev_pixelpipe_t *const pipe, const dt_iop_roi_t *const roi_in)
@ IOP_FLAGS_INCLUDE_IN_STYLES
@ IOP_FLAGS_SUPPORTS_BLENDING
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 float kernel(const float *x, const float *y)
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_ALIGNED_PIXEL
Align a 4-float pixel on 16 bytes, enough for SSE. Same struct-member caveat as DT_ALIGNED_ARRAY,...
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
#define DT_ALIGNED_ARRAY
Align an object on a cacheline boundary, so AVX2 can load it whole.
static float * dt_alloc_align_float(size_t pixels)
Allocate pixels floats, cacheline-aligned and marked as such.
#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_copy_host_to_device(const int devid, void *host, const int width, const int height, const int bpp)
void dt_opencl_release_mem_object(cl_mem mem)
#define __OMP_PARALLEL_FOR__(...)
#define __OMP_PARALLEL_FOR_SIMD__(...)
static gboolean dt_dev_pixelpipe_has_shutdown(const dt_dev_pixelpipe_t *pipe)
#define for_each_channel(_var,...)
#define for_four_channels(_var,...)
int kernel_blurs_convolve
dt_iop_global_data_t * data
struct dt_iop_module_gui_t * gui
dt_iop_global_data_t * global_data
Region of interest passed through the pixelpipe.
#define __DT_CLONE_TARGETS__