100 return _(
"blur|lens|motion");
106 _(
"simulate physically-accurate lens and motion blurs"),
107 _(
"creative"), _(
"linear, RGB, scene-referred"), _(
"linear, RGB"),
108 _(
"linear, RGB, scene-referred"));
143 for(
size_t j = 0; j <
width; j++)
145 const size_t index = (
i *
width + j);
148 for(
size_t ii = 0; ii <
FSIZE; ++ii)
149 for(
size_t jj = 0; jj <
FSIZE; ++jj)
151 const size_t row = CLAMP((
int)
i + (
int)(ii - (
FSIZE - 1) / 2), (
int)0, (
int)
height - 1);
152 const size_t col = CLAMP((
int)j + (
int)(jj - (
FSIZE - 1) / 2), (
int)0, (
int)
width - 1);
153 const size_t k_index = (
row *
width + col);
156 = { 1.0f / 16.0f, 4.0f / 16.0f, 6.0f / 16.0f, 4.0f / 16.0f, 1.0f / 16.0f };
158 acc += filter[ii] * filter[jj] * in[k_index];
178 const size_t height,
const float n,
const float m,
179 const float k,
const float rotation)
188 const float eps = 1.f / (float)
width;
189 const float radius = (float)(
width - 1) / 2.f - 1;
192 for(
size_t j = 0; j <
width; j++)
195 const float x = (float)(
i - 1) / radius - 1;
196 const float y = (float)(j - 1) / radius - 1;
199 const float r = dt_fast_hypotf(
x, y);
202 const float M = cosf((2.f * asinf(
k) +
M_PI_F *
m) / (2.f *
n))
203 / cosf((2.f * asinf(
k * cosf(
n * (atan2f(y,
x) + rotation))) +
M_PI_F *
m) / (2.f *
n));
213 const size_t height,
const float angle,
214 const float curvature,
const float offset)
217 const float A = curvature / 2.f;
219 const float C = -
A * offset * offset +
B * offset;
225 const float eps = 1.f / (float)
width;
227 const float radius = (float)(
width - 1) / 2.f - 1;
228 const float corr_angle = -
M_PI_F / 4.f - angle;
231 const float M[2][2] = { { cosf(corr_angle), -sinf(corr_angle) },
232 { sinf(corr_angle), cosf(corr_angle) } };
234 for(
size_t i = 0;
i < 8 *
width;
i++)
241 const float x = (float)(
i / 8.f - 1) / radius - 1;
245 const float X =
x - offset;
246 const float y = X * X *
A + X *
B +
C;
249 const float rot_x =
x *
M[0][0] + y *
M[0][1];
250 const float rot_y =
x *
M[1][0] + y *
M[1][1];
253 const int y_f[2] = { roundf((rot_y + 1) * radius -
eps),
254 roundf((rot_y + 1) * radius +
eps) };
255 const int x_f[2] = { roundf((rot_x + 1) * radius -
eps),
256 roundf((rot_x + 1) * radius +
eps) };
260 for(
int l = 0; l < 2; l++)
261 for(
int m = 0;
m < 2;
m++)
263 if(x_f[l] > 0 && x_f[l] <
width - 1 && y_f[
m] > 0 && y_f[
m] <
width - 1)
264 buffer[y_f[
m] *
width + x_f[l]] = 1.f;
276 const float radius = (
width - 1) / 2.f - 1;
279 for(
size_t j = 0; j <
width; j++)
282 const float x = (float)(
i - 1) / radius - 1;
283 const float y = (float)(j - 1) / radius - 1;
286 const float r_2 =
x *
x + y * y;
287 buffer[
i *
width + j] = expf(-4.f * r_2);
325 buffer[
k * 4] = buffer[
k * 4 + 1] = buffer[
k * 4 + 2] = buffer[
k * 4 + 3] = roundf(255.f * kernel_2[
k]);
404 const void *
const restrict ivoid,
void *
const restrict
ovoid,
410 const float *
const restrict in = __builtin_assume_aligned(ivoid, 64);
414 const int is_width_even = (roi_in->width % 2 == 0);
415 const int is_height_even = (roi_in->height % 2 == 0);
416 const size_t padded_width = roi_in->width + 1 * is_width_even;
417 const size_t padded_height = roi_in->height + 1 * is_height_even;
424 for(size_t
i = 0;
i < roi_in->height;
i++)
425 for(
size_t j = 0; j < roi_in->width; j++)
427 const size_t index_in = (
i * roi_in->width + j) * 4;
428 const size_t index_out = (
i * padded_width + j) * 4;
429 for_four_channels(c, aligned(in, padded_in : 64)) padded_in[index_out +
c] = in[index_in +
c];
433 if(padded_width > roi_in->width)
436 for(size_t
i = 0;
i < roi_in->height;
i++)
438 const size_t index_in = (
i * (roi_in->width - 1)) * 4;
439 const size_t index_out = (
i * (padded_width - 1)) * 4;
440 for_four_channels(c, aligned(in, padded_in : 64)) padded_in[index_out +
c] = in[index_in +
c];
444 if(padded_height > roi_in->height)
447 for(size_t j = 0; j < roi_in->width; j++)
449 const size_t index_in = ((roi_in->height - 1) * roi_in->width + j) * 4;
450 const size_t index_out = ((padded_height - 1) * padded_width + j) * 4;
451 for_four_channels(c, aligned(in, padded_in : 64)) padded_in[index_out +
c] = in[index_in +
c];
456 const size_t radius =
MAX(roundf(
p->radius / scale), 1);
457 const size_t kernel_width = 2 * radius + 1;
464 const size_t offset_i = (padded_height - 1) / 2 - (kernel_width - 1) / 2;
465 const size_t offset_j = (padded_width - 1) / 2 - (kernel_width - 1) / 2;
466 const size_t i_reach = offset_i + kernel_width;
467 const size_t j_reach = offset_j + kernel_width;
469 for(size_t
i = 0;
i < padded_width;
i++)
470 for(
size_t j = 0; j < padded_width; j++)
472 const size_t padded_idx = (
i * padded_width) + j;
474 if(
i >= offset_i && i < i_reach && j >= offset_j && j < j_reach)
476 const size_t i_kern =
i - offset_i;
477 const size_t j_kern = j - offset_j;
478 const size_t kern_idx = i_kern * kernel_width + j_kern;
479 padded_kernel[padded_idx] =
kernel[kern_idx];
482 padded_kernel[padded_idx] = 0.f;
486 int threads = fftw_init_threads();
487 fftw_plan_with_nthreads(threads);
494 int n[2] = { padded_width, padded_height };
503 fftwf_complex *
const restrict kernel_fft = fftwf_alloc_complex(padded_height * padded_height * 4);
504 fftwf_complex *
const restrict image_fft = fftwf_alloc_complex(padded_height * padded_height * 4);
507 fftwf_plan kernel_plan = fftwf_plan_many_dft_r2c(rank,
n, howmany,
508 padded_kernel, inembed,
513 fftwf_execute(kernel_plan);
516 fftwf_destroy_plan(kernel_plan);
517 fftwf_free(kernel_fft);
518 fftwf_free(image_fft);
519 fftw_cleanup_threads();
533 const void *
const restrict ivoid,
void *
const restrict
ovoid)
540 const float *
const restrict in = __builtin_assume_aligned(ivoid, 64);
541 float *
const restrict
out = __builtin_assume_aligned(
ovoid, 64);
544 const int radius =
MAX(roundf(
p->radius / scale), 2);
545 const size_t kernel_width = 2 * radius + 1;
556 for(
int j = 0; j < roi_out->
width; j++)
558 const size_t index = ((
i * roi_out->
width) + j) * 4;
561 if(
i >= radius && j >= radius && i < roi_out->
height - radius && j < roi_out->
width - radius)
564 for(
int l = -radius; l <= radius; l++)
565 for(
int m = -radius;
m <= radius;
m++)
567 const int ii =
i + l;
568 const int jj = j +
m;
569 const size_t idx_shift = ((ii * roi_out->
width) + jj) * 4;
571 const int ik = l + radius;
572 const int jk =
m + radius;
573 const size_t idx_kernel = (ik * kernel_width) + jk;
574 const float k =
kernel[idx_kernel];
583 for(
int l = -radius; l <= radius; l++)
584 for(
int m = -radius;
m <= radius;
m++)
586 const int ii = CLAMP((
int)
i + l, (
int)0, (
int)roi_out->
height - 1);
587 const int jj = CLAMP((
int)j +
m, (
int)0, (
int)roi_out->
width - 1);
588 const size_t idx_shift = ((ii * roi_out->
width) + jj) * 4;
590 const int ik = l + radius;
591 const int jk =
m + radius;
592 const size_t idx_kernel = (ik * kernel_width) + jk;
593 const float k =
kernel[idx_kernel];
602 out[index + 3] = in[index + 3];
619 const int devid = pipe->
devid;
627 const int radius =
MAX(roundf(
p->radius / scale), 2);
628 const size_t kernel_width = 2 * radius + 1;
648 if(err != CL_SUCCESS)
goto error;
664 const int program = 34;
689 gtk_widget_hide(
g->angle);
690 gtk_widget_hide(
g->curvature);
691 gtk_widget_hide(
g->offset);
693 gtk_widget_show(
g->blades);
694 gtk_widget_show(
g->concavity);
695 gtk_widget_show(
g->rotation);
696 gtk_widget_show(
g->linearity);
700 gtk_widget_show(
g->angle);
701 gtk_widget_show(
g->curvature);
702 gtk_widget_show(
g->offset);
704 gtk_widget_hide(
g->blades);
705 gtk_widget_hide(
g->concavity);
706 gtk_widget_hide(
g->rotation);
707 gtk_widget_hide(
g->linearity);
711 gtk_widget_hide(
g->angle);
712 gtk_widget_hide(
g->curvature);
713 gtk_widget_hide(
g->offset);
715 gtk_widget_hide(
g->blades);
716 gtk_widget_hide(
g->concavity);
717 gtk_widget_hide(
g->rotation);
718 gtk_widget_hide(
g->linearity);
726 gtk_widget_queue_draw(GTK_WIDGET(
g->area));
736 GtkAllocation allocation;
737 GtkStyleContext *context = gtk_widget_get_style_context(widget);
738 gtk_widget_get_allocation(widget, &allocation);
739 gtk_render_background(context, crf, 0, 0, allocation.width, allocation.height);
741 if(allocation.width !=
g->img_width)
751 g->img =
dt_alloc_align(
sizeof(
unsigned char) * 4 * allocation.width * allocation.width);
753 g->img_width = allocation.width;
754 g->img_cached =
TRUE;
764 const int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32,
g->img_width);
765 cairo_surface_t *cst = cairo_image_surface_create_for_data(
g->img, CAIRO_FORMAT_ARGB32,
766 g->img_width,
g->img_width, stride);
768 cairo_set_source_surface(crf, cst, 0, 0);
770 cairo_surface_destroy(cst);
780#define DEG_TO_RAD 180.f / M_PI_F
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)
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)
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__(...)
#define for_each_channel(_var,...)
#define for_four_channels(_var,...)
struct dt_iop_module_t *void * data
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__