96 return _(
"blur|lens|motion");
102 _(
"simulate physically-accurate lens and motion blurs"),
103 _(
"creative"), _(
"linear, RGB, scene-referred"), _(
"linear, RGB"),
104 _(
"linear, RGB, scene-referred"));
139 for(
size_t j = 0; j <
width; j++)
141 const size_t index = (
i *
width + j);
144 for(
size_t ii = 0; ii <
FSIZE; ++ii)
145 for(
size_t jj = 0; jj <
FSIZE; ++jj)
147 const size_t row = CLAMP((
int)
i + (
int)(ii - (
FSIZE - 1) / 2), (
int)0, (
int)
height - 1);
148 const size_t col = CLAMP((
int)j + (
int)(jj - (
FSIZE - 1) / 2), (
int)0, (
int)
width - 1);
149 const size_t k_index = (
row *
width + col);
152 = { 1.0f / 16.0f, 4.0f / 16.0f, 6.0f / 16.0f, 4.0f / 16.0f, 1.0f / 16.0f };
154 acc += filter[ii] * filter[jj] * in[k_index];
174 const size_t height,
const float n,
const float m,
175 const float k,
const float rotation)
184 const float eps = 1.f / (float)
width;
185 const float radius = (float)(
width - 1) / 2.f - 1;
188 for(
size_t j = 0; j <
width; j++)
191 const float x = (float)(
i - 1) / radius - 1;
192 const float y = (float)(j - 1) / radius - 1;
195 const float r = dt_fast_hypotf(
x, y);
198 const float M = cosf((2.f * asinf(
k) +
M_PI_F *
m) / (2.f *
n))
199 / cosf((2.f * asinf(
k * cosf(
n * (atan2f(y,
x) + rotation))) +
M_PI_F *
m) / (2.f *
n));
209 const size_t height,
const float angle,
210 const float curvature,
const float offset)
213 const float A = curvature / 2.f;
215 const float C = -
A * offset * offset +
B * offset;
221 const float eps = 1.f / (float)
width;
223 const float radius = (float)(
width - 1) / 2.f - 1;
224 const float corr_angle = -
M_PI_F / 4.f - angle;
227 const float M[2][2] = { { cosf(corr_angle), -sinf(corr_angle) },
228 { sinf(corr_angle), cosf(corr_angle) } };
230 for(
size_t i = 0;
i < 8 *
width;
i++)
237 const float x = (float)(
i / 8.f - 1) / radius - 1;
241 const float X =
x - offset;
242 const float y = X * X *
A + X *
B +
C;
245 const float rot_x =
x *
M[0][0] + y *
M[0][1];
246 const float rot_y =
x *
M[1][0] + y *
M[1][1];
249 const int y_f[2] = { roundf((rot_y + 1) * radius -
eps),
250 roundf((rot_y + 1) * radius +
eps) };
251 const int x_f[2] = { roundf((rot_x + 1) * radius -
eps),
252 roundf((rot_x + 1) * radius +
eps) };
256 for(
int l = 0; l < 2; l++)
257 for(
int m = 0;
m < 2;
m++)
259 if(x_f[l] > 0 && x_f[l] <
width - 1 && y_f[
m] > 0 && y_f[
m] <
width - 1)
260 buffer[y_f[
m] *
width + x_f[l]] = 1.f;
272 const float radius = (
width - 1) / 2.f - 1;
275 for(
size_t j = 0; j <
width; j++)
278 const float x = (float)(
i - 1) / radius - 1;
279 const float y = (float)(j - 1) / radius - 1;
282 const float r_2 =
x *
x + y * y;
283 buffer[
i *
width + j] = expf(-4.f * r_2);
321 buffer[
k * 4] = buffer[
k * 4 + 1] = buffer[
k * 4 + 2] = buffer[
k * 4 + 3] = roundf(255.f * kernel_2[
k]);
400 const void *
const restrict ivoid,
void *
const restrict
ovoid,
406 const float *
const restrict in = __builtin_assume_aligned(ivoid, 64);
410 const int is_width_even = (roi_in->width % 2 == 0);
411 const int is_height_even = (roi_in->height % 2 == 0);
412 const size_t padded_width = roi_in->width + 1 * is_width_even;
413 const size_t padded_height = roi_in->height + 1 * is_height_even;
420 for(size_t
i = 0;
i < roi_in->height;
i++)
421 for(
size_t j = 0; j < roi_in->width; j++)
423 const size_t index_in = (
i * roi_in->width + j) * 4;
424 const size_t index_out = (
i * padded_width + j) * 4;
425 for_four_channels(c, aligned(in, padded_in : 64)) padded_in[index_out +
c] = in[index_in +
c];
429 if(padded_width > roi_in->width)
432 for(size_t
i = 0;
i < roi_in->height;
i++)
434 const size_t index_in = (
i * (roi_in->width - 1)) * 4;
435 const size_t index_out = (
i * (padded_width - 1)) * 4;
436 for_four_channels(c, aligned(in, padded_in : 64)) padded_in[index_out +
c] = in[index_in +
c];
440 if(padded_height > roi_in->height)
443 for(size_t j = 0; j < roi_in->width; j++)
445 const size_t index_in = ((roi_in->height - 1) * roi_in->width + j) * 4;
446 const size_t index_out = ((padded_height - 1) * padded_width + j) * 4;
447 for_four_channels(c, aligned(in, padded_in : 64)) padded_in[index_out +
c] = in[index_in +
c];
452 const size_t radius =
MAX(roundf(
p->radius / scale), 1);
453 const size_t kernel_width = 2 * radius + 1;
460 const size_t offset_i = (padded_height - 1) / 2 - (kernel_width - 1) / 2;
461 const size_t offset_j = (padded_width - 1) / 2 - (kernel_width - 1) / 2;
462 const size_t i_reach = offset_i + kernel_width;
463 const size_t j_reach = offset_j + kernel_width;
465 for(size_t
i = 0;
i < padded_width;
i++)
466 for(
size_t j = 0; j < padded_width; j++)
468 const size_t padded_idx = (
i * padded_width) + j;
470 if(
i >= offset_i && i < i_reach && j >= offset_j && j < j_reach)
472 const size_t i_kern =
i - offset_i;
473 const size_t j_kern = j - offset_j;
474 const size_t kern_idx = i_kern * kernel_width + j_kern;
475 padded_kernel[padded_idx] =
kernel[kern_idx];
478 padded_kernel[padded_idx] = 0.f;
482 int threads = fftw_init_threads();
483 fftw_plan_with_nthreads(threads);
490 int n[2] = { padded_width, padded_height };
499 fftwf_complex *
const restrict kernel_fft = fftwf_alloc_complex(padded_height * padded_height * 4);
500 fftwf_complex *
const restrict image_fft = fftwf_alloc_complex(padded_height * padded_height * 4);
503 fftwf_plan kernel_plan = fftwf_plan_many_dft_r2c(rank,
n, howmany,
504 padded_kernel, inembed,
509 fftwf_execute(kernel_plan);
512 fftwf_destroy_plan(kernel_plan);
513 fftwf_free(kernel_fft);
514 fftwf_free(image_fft);
515 fftw_cleanup_threads();
529 const void *
const restrict ivoid,
void *
const restrict
ovoid)
536 const float *
const restrict in = __builtin_assume_aligned(ivoid, 64);
537 float *
const restrict
out = __builtin_assume_aligned(
ovoid, 64);
540 const int radius =
MAX(roundf(
p->radius / scale), 2);
541 const size_t kernel_width = 2 * radius + 1;
552 for(
int j = 0; j < roi_out->
width; j++)
554 const size_t index = ((
i * roi_out->
width) + j) * 4;
557 if(
i >= radius && j >= radius && i < roi_out->
height - radius && j < roi_out->
width - radius)
560 for(
int l = -radius; l <= radius; l++)
561 for(
int m = -radius;
m <= radius;
m++)
563 const int ii =
i + l;
564 const int jj = j +
m;
565 const size_t idx_shift = ((ii * roi_out->
width) + jj) * 4;
567 const int ik = l + radius;
568 const int jk =
m + radius;
569 const size_t idx_kernel = (ik * kernel_width) + jk;
570 const float k =
kernel[idx_kernel];
579 for(
int l = -radius; l <= radius; l++)
580 for(
int m = -radius;
m <= radius;
m++)
582 const int ii = CLAMP((
int)
i + l, (
int)0, (
int)roi_out->
height - 1);
583 const int jj = CLAMP((
int)j +
m, (
int)0, (
int)roi_out->
width - 1);
584 const size_t idx_shift = ((ii * roi_out->
width) + jj) * 4;
586 const int ik = l + radius;
587 const int jk =
m + radius;
588 const size_t idx_kernel = (ik * kernel_width) + jk;
589 const float k =
kernel[idx_kernel];
598 out[index + 3] = in[index + 3];
615 const int devid = pipe->
devid;
623 const int radius =
MAX(roundf(
p->radius / scale), 2);
624 const size_t kernel_width = 2 * radius + 1;
644 if(err != CL_SUCCESS)
goto error;
660 const int program = 34;
685 gtk_widget_hide(
g->angle);
686 gtk_widget_hide(
g->curvature);
687 gtk_widget_hide(
g->offset);
689 gtk_widget_show(
g->blades);
690 gtk_widget_show(
g->concavity);
691 gtk_widget_show(
g->rotation);
692 gtk_widget_show(
g->linearity);
696 gtk_widget_show(
g->angle);
697 gtk_widget_show(
g->curvature);
698 gtk_widget_show(
g->offset);
700 gtk_widget_hide(
g->blades);
701 gtk_widget_hide(
g->concavity);
702 gtk_widget_hide(
g->rotation);
703 gtk_widget_hide(
g->linearity);
707 gtk_widget_hide(
g->angle);
708 gtk_widget_hide(
g->curvature);
709 gtk_widget_hide(
g->offset);
711 gtk_widget_hide(
g->blades);
712 gtk_widget_hide(
g->concavity);
713 gtk_widget_hide(
g->rotation);
714 gtk_widget_hide(
g->linearity);
722 gtk_widget_queue_draw(GTK_WIDGET(
g->area));
732 GtkAllocation allocation;
733 GtkStyleContext *context = gtk_widget_get_style_context(widget);
734 gtk_widget_get_allocation(widget, &allocation);
735 gtk_render_background(context, crf, 0, 0, allocation.width, allocation.height);
737 if(allocation.width !=
g->img_width)
747 g->img =
dt_alloc_align(
sizeof(
unsigned char) * 4 * allocation.width * allocation.width);
749 g->img_width = allocation.width;
750 g->img_cached =
TRUE;
760 const int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32,
g->img_width);
761 cairo_surface_t *cst = cairo_image_surface_create_for_data(
g->img, CAIRO_FORMAT_ARGB32,
762 g->img_width,
g->img_width, stride);
764 cairo_set_source_surface(crf, cst, 0, 0);
766 cairo_surface_destroy(cst);
776#define DEG_TO_RAD 180.f / M_PI_F
793 gtk_box_pack_start(GTK_BOX(self->
widget), GTK_WIDGET(
g->area),
TRUE,
TRUE, 0);
static void error(char *msg)
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)
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
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)
void dt_print(dt_debug_thread_t thread, const char *msg,...)
#define dt_free_align(ptr)
#define for_each_channel(_var,...)
static float * dt_alloc_align_float(size_t pixels)
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
#define __DT_CLONE_TARGETS__
#define for_four_channels(_var,...)
#define __OMP_PARALLEL_FOR__(...)
#define __OMP_PARALLEL_FOR_SIMD__(...)
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
GtkWidget * dtgtk_drawing_area_new_with_aspect_ratio(double aspect)
#define DT_GUI_BOX_SPACING
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
#define IOP_GUI_ALLOC(module)
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 float kernel(const float *x, const float *y)
float *const restrict const size_t k
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)
struct _GtkWidget GtkWidget
struct dt_iop_module_t *void * data
int kernel_blurs_convolve
dt_iop_global_data_t * data
dt_iop_gui_data_t * gui_data
dt_iop_global_data_t * global_data
Region of interest passed through the pixelpipe.