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