1 /* This file is an image processing operation for GEGL
2  *
3  * GEGL is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Lesser General Public
5  * License as published by the Free Software Foundation; either
6  * version 3 of the License, or (at your option) any later version.
7  *
8  * GEGL is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
15  *
16  * Copyright 2006 Øyvind Kolås <pippin@gimp.org>
17  */
18 
19 #include "config.h"
20 
21 #include <glib/gi18n-lib.h>
22 
23 #ifdef GEGL_PROPERTIES
24 
25 property_double (length, _("Length"), 10.0)
26     description (_("Length of blur in pixels"))
27     value_range (0.0, 1000.0)
28     ui_range    (0.0, 300.0)
29     ui_gamma    (1.5)
30     ui_meta     ("unit", "pixel-distance")
31 
32 property_double (angle, _("Angle"), 0.0)
33     description (_("Angle of blur in degrees"))
34     value_range (-180, 180)
35     ui_meta     ("unit", "degree")
36     ui_meta     ("direction", "cw")
37 
38 #else
39 
40 #define GEGL_OP_AREA_FILTER
41 #define GEGL_OP_NAME     motion_blur_linear
42 #define GEGL_OP_C_SOURCE motion-blur-linear.c
43 
44 #include "gegl-op.h"
45 
46 static void
47 prepare (GeglOperation *operation)
48 {
49   const Babl *space = gegl_operation_get_source_space (operation, "input");
50   GeglOperationAreaFilter *op_area = GEGL_OPERATION_AREA_FILTER (operation);
51   GeglProperties          *o       = GEGL_PROPERTIES (operation);
52   gdouble                  theta   = o->angle * G_PI / 180.0;
53   gdouble                  offset_x;
54   gdouble                  offset_y;
55 
56   while (theta < 0.0)
57     theta += 2 * G_PI;
58 
59   offset_x = fabs (o->length * cos (theta));
60   offset_y = fabs (o->length * sin (theta));
61 
62   op_area->left   =
63   op_area->right  = (gint) ceil (0.5 * offset_x);
64   op_area->top    =
65   op_area->bottom = (gint) ceil (0.5 * offset_y);
66 
67   gegl_operation_set_format (operation, "input",  babl_format_with_space ("RaGaBaA float", space));
68   gegl_operation_set_format (operation, "output", babl_format_with_space ("RaGaBaA float", space));
69 }
70 
71 #include "opencl/gegl-cl.h"
72 #include "gegl-buffer-cl-iterator.h"
73 
74 #include "opencl/motion-blur-linear.cl.h"
75 
76 static GeglClRunData *cl_data = NULL;
77 
78 static gboolean
79 cl_motion_blur_linear (cl_mem                in_tex,
80                        cl_mem                out_tex,
81                        size_t                global_worksize,
82                        const GeglRectangle  *roi,
83                        const GeglRectangle  *src_rect,
84                        gint                  num_steps,
85                        gfloat                offset_x,
86                        gfloat                offset_y)
87 {
88   cl_int cl_err = 0;
89   size_t global_ws[2];
90 
91   if (!cl_data)
92     {
93       const char *kernel_name[] = {"motion_blur_linear", NULL};
94       cl_data = gegl_cl_compile_and_build (motion_blur_linear_cl_source, kernel_name);
95     }
96 
97   if (!cl_data)
98     return TRUE;
99 
100   global_ws[0] = roi->width;
101   global_ws[1] = roi->height;
102 
103   cl_err = gegl_clSetKernelArg(cl_data->kernel[0],  0, sizeof(cl_mem),   (void*)&in_tex);
104   CL_CHECK;
105   cl_err = gegl_clSetKernelArg(cl_data->kernel[0],  1, sizeof(cl_int),   (void*)&src_rect->width);
106   CL_CHECK;
107   cl_err = gegl_clSetKernelArg(cl_data->kernel[0],  2, sizeof(cl_int),   (void*)&src_rect->height);
108   CL_CHECK;
109   cl_err = gegl_clSetKernelArg(cl_data->kernel[0],  3, sizeof(cl_int),   (void*)&src_rect->x);
110   CL_CHECK;
111   cl_err = gegl_clSetKernelArg(cl_data->kernel[0],  4, sizeof(cl_int),   (void*)&src_rect->y);
112   CL_CHECK;
113   cl_err = gegl_clSetKernelArg(cl_data->kernel[0],  5, sizeof(cl_mem),   (void*)&out_tex);
114   CL_CHECK;
115   cl_err = gegl_clSetKernelArg(cl_data->kernel[0],  6, sizeof(cl_int),   (void*)&roi->x);
116   CL_CHECK;
117   cl_err = gegl_clSetKernelArg(cl_data->kernel[0],  7, sizeof(cl_int),   (void*)&roi->y);
118   CL_CHECK;
119   cl_err = gegl_clSetKernelArg(cl_data->kernel[0],  8, sizeof(cl_int),   (void*)&num_steps);
120   CL_CHECK;
121   cl_err = gegl_clSetKernelArg(cl_data->kernel[0],  9, sizeof(cl_float), (void*)&offset_x);
122   CL_CHECK;
123   cl_err = gegl_clSetKernelArg(cl_data->kernel[0], 10, sizeof(cl_float), (void*)&offset_y);
124   CL_CHECK;
125 
126   cl_err = gegl_clEnqueueNDRangeKernel (gegl_cl_get_command_queue (),
127                                         cl_data->kernel[0], 2,
128                                         NULL, global_ws, NULL,
129                                         0, NULL, NULL);
130   CL_CHECK;
131 
132   return FALSE;
133 
134 error:
135   return TRUE;
136 }
137 
138 static gboolean
139 cl_process (GeglOperation       *operation,
140             GeglBuffer          *input,
141             GeglBuffer          *output,
142             const GeglRectangle *result,
143             const GeglRectangle *src_rect)
144 {
145   GeglOperationAreaFilter *op_area = GEGL_OPERATION_AREA_FILTER (operation);
146   GeglProperties              *o       = GEGL_PROPERTIES (operation);
147 
148   GeglBufferClIterator *i;
149   const Babl *in_format  = gegl_operation_get_format (operation, "input");
150   const Babl *out_format = gegl_operation_get_format (operation, "output");
151   gint        err;
152   gdouble     theta     = o->angle * G_PI / 180.0;
153   gint        num_steps = (gint)ceil(o->length) + 1;
154   gfloat      offset_x;
155   gfloat      offset_y;
156   gint        read;
157 
158   while (theta < 0.0)
159     theta += 2 * G_PI;
160 
161   offset_x = (gfloat) (o->length * cos (theta));
162   offset_y = (gfloat) (o->length * sin (theta));
163 
164   i = gegl_buffer_cl_iterator_new (output,
165                                    result,
166                                    out_format,
167                                    GEGL_CL_BUFFER_WRITE);
168 
169   read = gegl_buffer_cl_iterator_add_2 (i,
170                                         input,
171                                         result,
172                                         in_format,
173                                         GEGL_CL_BUFFER_READ,
174                                         op_area->left,
175                                         op_area->right,
176                                         op_area->top,
177                                         op_area->bottom,
178                                         GEGL_ABYSS_NONE);
179 
180   while (gegl_buffer_cl_iterator_next (i, &err))
181     {
182       if (err)
183         return FALSE;
184 
185       err = cl_motion_blur_linear (i->tex[read],
186                                    i->tex[0],
187                                    i->size[0],
188                                    &i->roi[0],
189                                    &i->roi[read],
190                                    num_steps,
191                                    offset_x,
192                                    offset_y);
193 
194       if (err)
195         return FALSE;
196     }
197 
198   return TRUE;
199 }
200 
201 static inline gfloat *
202 get_pixel_color (gfloat              *in_buf,
203                  const GeglRectangle *rect,
204                  gint                 x,
205                  gint                 y)
206 {
207   gint ix = x - rect->x;
208   gint iy = y - rect->y;
209 
210   ix = CLAMP (ix, 0, rect->width  - 1);
211   iy = CLAMP (iy, 0, rect->height - 1);
212 
213   return &in_buf[(iy * rect->width + ix) * 4];
214 }
215 
216 static gboolean
217 process (GeglOperation       *operation,
218          GeglBuffer          *input,
219          GeglBuffer          *output,
220          const GeglRectangle *roi,
221          gint                 level)
222 {
223   GeglOperationAreaFilter *op_area = GEGL_OPERATION_AREA_FILTER (operation);
224   GeglProperties          *o       = GEGL_PROPERTIES (operation);
225   const Babl *in_format  = gegl_operation_get_format (operation, "input");
226   const Babl *out_format = gegl_operation_get_format (operation, "output");
227   GeglRectangle            src_rect;
228   gfloat                  *in_buf;
229   gfloat                  *out_buf;
230   gfloat                  *out_pixel;
231   gint                     x, y;
232 
233   gdouble theta         = o->angle * G_PI / 180.0;
234   gint    num_steps     = (gint) ceil (o->length) + 1;
235   gfloat  inv_num_steps = 1.0f / num_steps;
236   gdouble offset_x;
237   gdouble offset_y;
238 
239   while (theta < 0.0)
240     theta += 2 * G_PI;
241 
242   offset_x = o->length * cos (theta);
243   offset_y = o->length * sin (theta);
244 
245   src_rect = *roi;
246   src_rect.x -= op_area->left;
247   src_rect.y -= op_area->top;
248   src_rect.width += op_area->left + op_area->right;
249   src_rect.height += op_area->top + op_area->bottom;
250 
251   if (gegl_operation_use_opencl (operation))
252     if (cl_process (operation, input, output, roi, &src_rect))
253       return TRUE;
254 
255   in_buf = g_new (gfloat, src_rect.width * src_rect.height * 4);
256   out_buf = g_new0 (gfloat, roi->width * roi->height * 4);
257   out_pixel = out_buf;
258 
259   gegl_buffer_get (input, &src_rect, 1.0, in_format,
260                    in_buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
261 
262   for (y = 0; y < roi->height; ++y)
263     {
264       for (x = 0; x < roi->width; ++x)
265         {
266           gint   step;
267           gint   c;
268           gint   px = x+roi->x;
269           gint   py = y+roi->y;
270           gfloat sum[4] = {0,0,0,0};
271 
272           for (step = 0; step < num_steps; ++step)
273             {
274               gdouble t = num_steps == 1 ? 0.0 : step / (gdouble)(num_steps-1) - 0.5;
275 
276               /* get the interpolated pixel position for this step */
277               gdouble xx = px + t * offset_x;
278               gdouble yy = py + t * offset_y;
279               gint    ix = (gint) floor (xx);
280               gint    iy = (gint) floor (yy);
281               gdouble dx = xx - floor (xx);
282               gdouble dy = yy - floor (yy);
283 
284               /* do bilinear interpolation to get a nice smooth result */
285               gfloat *pix0, *pix1, *pix2, *pix3;
286               gfloat  mixy0[4];
287               gfloat  mixy1[4];
288 
289               pix0 = get_pixel_color (in_buf, &src_rect, ix,     iy);
290               pix1 = get_pixel_color (in_buf, &src_rect, ix + 1, iy);
291               pix2 = get_pixel_color (in_buf, &src_rect, ix,     iy + 1);
292               pix3 = get_pixel_color (in_buf, &src_rect, ix + 1, iy + 1);
293 
294               for (c = 0; c < 4; ++c)
295                 {
296                   mixy0[c] = dy * (pix2[c] - pix0[c]) + pix0[c];
297                   mixy1[c] = dy * (pix3[c] - pix1[c]) + pix1[c];
298                   sum[c] += dx * (mixy1[c] - mixy0[c]) + mixy0[c];
299                 }
300             }
301 
302           for (c = 0; c < 4; ++c)
303             *out_pixel++ = sum[c] * inv_num_steps;
304         }
305     }
306 
307   gegl_buffer_set (output, roi, 0, out_format,
308                    out_buf, GEGL_AUTO_ROWSTRIDE);
309 
310   g_free (in_buf);
311   g_free (out_buf);
312 
313   return  TRUE;
314 }
315 
316 static void
317 gegl_op_class_init (GeglOpClass *klass)
318 {
319   GeglOperationClass       *operation_class;
320   GeglOperationFilterClass *filter_class;
321 
322   operation_class = GEGL_OPERATION_CLASS (klass);
323   filter_class = GEGL_OPERATION_FILTER_CLASS (klass);
324 
325   operation_class->prepare        = prepare;
326   operation_class->opencl_support = TRUE;
327 
328   filter_class->process           = process;
329 
330   gegl_operation_class_set_keys (operation_class,
331                                  "name",        "gegl:motion-blur-linear",
332                                  "title",       _("Linear Motion Blur"),
333                                  "compat-name", "gegl:motion-blur",
334                                  "categories",  "blur",
335                                  "reference-hash", "2bac2e03cd14f2aac805bbfac9581b59",
336                                  "reference-hashB", "e3ec2585f3acbdae5707a52b0a50c53b",
337                                  "description", _("Blur pixels in a direction, simulates blurring caused by moving camera in a straight line during exposure."),
338     NULL);
339 }
340 
341 #endif
342