motion-blur-linear.c 10.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/* This file is an image processing operation for GEGL
 *
 * GEGL is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * GEGL is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
 *
 * Copyright 2006 Øyvind Kolås <pippin@gimp.org>
 */

#include "config.h"
20

21 22 23
#include <glib/gi18n-lib.h>
#include <math.h>

24 25
#ifdef GEGL_PROPERTIES

26 27 28 29 30 31 32 33 34 35 36
property_double (length, _("Length"), 10.0)
    description (_("Length of blur in pixels"))
    value_range (0.0, 1000.0)
    ui_range    (0.0, 300.0)
    ui_gamma    (1.5)
    ui_meta     ("unit", "pixel-distance")

property_double (angle, _("Angle"), 0.0)
    description (_("Angle of blur in degrees"))
    value_range (-180, 180)
    ui_meta     ("unit", "degree")
37 38 39

#else

40 41
#define GEGL_OP_AREA_FILTER
#define GEGL_OP_C_FILE "motion-blur-linear.c"
42

43
#include "gegl-op.h"
44 45 46 47

static void
prepare (GeglOperation *operation)
{
48
  GeglOperationAreaFilter *op_area = GEGL_OPERATION_AREA_FILTER (operation);
49
  GeglProperties          *o       = GEGL_PROPERTIES (operation);
50 51 52
  gdouble                  theta   = o->angle * G_PI / 180.0;
  gdouble                  offset_x;
  gdouble                  offset_y;
53

54 55 56 57 58
  while (theta < 0.0)
    theta += 2 * G_PI;

  offset_x = fabs (o->length * cos (theta));
  offset_y = fabs (o->length * sin (theta));
59 60

  op_area->left   =
61
  op_area->right  = (gint) ceil (0.5 * offset_x);
62
  op_area->top    =
63
  op_area->bottom = (gint) ceil (0.5 * offset_y);
64

Victor Oliveira's avatar
Victor Oliveira committed
65
  gegl_operation_set_format (operation, "input",  babl_format ("RaGaBaA float"));
66 67 68
  gegl_operation_set_format (operation, "output", babl_format ("RaGaBaA float"));
}

69 70 71
#include "opencl/gegl-cl.h"
#include "buffer/gegl-buffer-cl-iterator.h"

72
#include "opencl/motion-blur-linear.cl.h"
73

74
static GeglClRunData *cl_data = NULL;
75

Victor Oliveira's avatar
Victor Oliveira committed
76
static gboolean
77
cl_motion_blur_linear (cl_mem                in_tex,
78 79 80 81 82 83 84
                       cl_mem                out_tex,
                       size_t                global_worksize,
                       const GeglRectangle  *roi,
                       const GeglRectangle  *src_rect,
                       gint                  num_steps,
                       gfloat                offset_x,
                       gfloat                offset_y)
85 86 87 88 89
{
  cl_int cl_err = 0;
  size_t global_ws[2];

  if (!cl_data)
90 91 92 93 94 95 96
    {
      const char *kernel_name[] = {"motion_blur_linear", NULL};
      cl_data = gegl_cl_compile_and_build (motion_blur_linear_cl_source, kernel_name);
    }

  if (!cl_data)
    return TRUE;
97 98 99 100

  global_ws[0] = roi->width;
  global_ws[1] = roi->height;

Victor Oliveira's avatar
Victor Oliveira committed
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
  cl_err = gegl_clSetKernelArg(cl_data->kernel[0],  0, sizeof(cl_mem),   (void*)&in_tex);
  CL_CHECK;
  cl_err = gegl_clSetKernelArg(cl_data->kernel[0],  1, sizeof(cl_int),   (void*)&src_rect->width);
  CL_CHECK;
  cl_err = gegl_clSetKernelArg(cl_data->kernel[0],  2, sizeof(cl_int),   (void*)&src_rect->height);
  CL_CHECK;
  cl_err = gegl_clSetKernelArg(cl_data->kernel[0],  3, sizeof(cl_int),   (void*)&src_rect->x);
  CL_CHECK;
  cl_err = gegl_clSetKernelArg(cl_data->kernel[0],  4, sizeof(cl_int),   (void*)&src_rect->y);
  CL_CHECK;
  cl_err = gegl_clSetKernelArg(cl_data->kernel[0],  5, sizeof(cl_mem),   (void*)&out_tex);
  CL_CHECK;
  cl_err = gegl_clSetKernelArg(cl_data->kernel[0],  6, sizeof(cl_int),   (void*)&roi->x);
  CL_CHECK;
  cl_err = gegl_clSetKernelArg(cl_data->kernel[0],  7, sizeof(cl_int),   (void*)&roi->y);
  CL_CHECK;
  cl_err = gegl_clSetKernelArg(cl_data->kernel[0],  8, sizeof(cl_int),   (void*)&num_steps);
  CL_CHECK;
  cl_err = gegl_clSetKernelArg(cl_data->kernel[0],  9, sizeof(cl_float), (void*)&offset_x);
  CL_CHECK;
  cl_err = gegl_clSetKernelArg(cl_data->kernel[0], 10, sizeof(cl_float), (void*)&offset_y);
  CL_CHECK;
123

124 125 126 127
  cl_err = gegl_clEnqueueNDRangeKernel (gegl_cl_get_command_queue (),
                                        cl_data->kernel[0], 2,
                                        NULL, global_ws, NULL,
                                        0, NULL, NULL);
Victor Oliveira's avatar
Victor Oliveira committed
128
  CL_CHECK;
129

Victor Oliveira's avatar
Victor Oliveira committed
130 131 132 133
  return FALSE;

error:
  return TRUE;
134 135 136 137 138 139 140 141 142
}

static gboolean
cl_process (GeglOperation       *operation,
            GeglBuffer          *input,
            GeglBuffer          *output,
            const GeglRectangle *result,
            const GeglRectangle *src_rect)
{
143
  GeglOperationAreaFilter *op_area = GEGL_OPERATION_AREA_FILTER (operation);
144
  GeglProperties              *o       = GEGL_PROPERTIES (operation);
145

146
  GeglBufferClIterator *i;
147 148
  const Babl *in_format  = gegl_operation_get_format (operation, "input");
  const Babl *out_format = gegl_operation_get_format (operation, "output");
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
  gint        err;
  gdouble     theta     = o->angle * G_PI / 180.0;
  gint        num_steps = (gint)ceil(o->length) + 1;
  gfloat      offset_x;
  gfloat      offset_y;
  gint        read;

  while (theta < 0.0)
    theta += 2 * G_PI;

  offset_x = (gfloat) (o->length * cos (theta));
  offset_y = (gfloat) (o->length * sin (theta));

  i = gegl_buffer_cl_iterator_new (output,
                                   result,
                                   out_format,
                                   GEGL_CL_BUFFER_WRITE);

  read = gegl_buffer_cl_iterator_add_2 (i,
                                        input,
                                        result,
                                        in_format,
                                        GEGL_CL_BUFFER_READ,
                                        op_area->left,
                                        op_area->right,
                                        op_area->top,
                                        op_area->bottom,
                                        GEGL_ABYSS_NONE);
177

178 179
  while (gegl_buffer_cl_iterator_next (i, &err))
    {
180 181 182 183 184 185 186 187 188 189 190 191 192 193
      if (err)
        return FALSE;

      err = cl_motion_blur_linear (i->tex[read],
                                   i->tex[0],
                                   i->size[0],
                                   &i->roi[0],
                                   &i->roi[read],
                                   num_steps,
                                   offset_x,
                                   offset_y);

      if (err)
        return FALSE;
194
    }
195

196 197 198
  return TRUE;
}

199 200 201 202 203
static inline gfloat *
get_pixel_color (gfloat              *in_buf,
                 const GeglRectangle *rect,
                 gint                 x,
                 gint                 y)
204 205 206
{
  gint ix = x - rect->x;
  gint iy = y - rect->y;
207

208 209 210 211
  ix = CLAMP (ix, 0, rect->width  - 1);
  iy = CLAMP (iy, 0, rect->height - 1);

  return &in_buf[(iy * rect->width + ix) * 4];
212 213 214 215 216 217
}

static gboolean
process (GeglOperation       *operation,
         GeglBuffer          *input,
         GeglBuffer          *output,
218 219
         const GeglRectangle *roi,
         gint                 level)
220
{
221
  GeglOperationAreaFilter *op_area = GEGL_OPERATION_AREA_FILTER (operation);
222
  GeglProperties              *o       = GEGL_PROPERTIES (operation);
223 224 225 226 227 228 229 230 231
  GeglRectangle            src_rect;
  gfloat                  *in_buf;
  gfloat                  *out_buf;
  gfloat                  *out_pixel;
  gint                     x, y;

  gdouble theta         = o->angle * G_PI / 180.0;
  gint    num_steps     = (gint) ceil (o->length) + 1;
  gfloat  inv_num_steps = 1.0f / num_steps;
232 233 234 235 236 237 238 239
  gdouble offset_x;
  gdouble offset_y;

  while (theta < 0.0)
    theta += 2 * G_PI;

  offset_x = o->length * cos (theta);
  offset_y = o->length * sin (theta);
240 241 242 243 244 245 246

  src_rect = *roi;
  src_rect.x -= op_area->left;
  src_rect.y -= op_area->top;
  src_rect.width += op_area->left + op_area->right;
  src_rect.height += op_area->top + op_area->bottom;

247
  if (gegl_operation_use_opencl (operation))
248 249 250
    if (cl_process (operation, input, output, roi, &src_rect))
      return TRUE;

251 252 253 254
  in_buf = g_new (gfloat, src_rect.width * src_rect.height * 4);
  out_buf = g_new0 (gfloat, roi->width * roi->height * 4);
  out_pixel = out_buf;

255 256
  gegl_buffer_get (input, &src_rect, 1.0, babl_format ("RaGaBaA float"),
                   in_buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
257

258
  for (y = 0; y < roi->height; ++y)
259
    {
260
      for (x = 0; x < roi->width; ++x)
261
        {
262 263 264 265
          gint   step;
          gint   c;
          gint   px = x+roi->x;
          gint   py = y+roi->y;
266
          gfloat sum[4] = {0,0,0,0};
267 268

          for (step = 0; step < num_steps; ++step)
269 270
            {
              gdouble t = num_steps == 1 ? 0.0 : step / (gdouble)(num_steps-1) - 0.5;
271

272
              /* get the interpolated pixel position for this step */
273 274 275 276 277 278
              gdouble xx = px + t * offset_x;
              gdouble yy = py + t * offset_y;
              gint    ix = (gint) floor (xx);
              gint    iy = (gint) floor (yy);
              gdouble dx = xx - floor (xx);
              gdouble dy = yy - floor (yy);
279 280 281

              /* do bilinear interpolation to get a nice smooth result */
              gfloat *pix0, *pix1, *pix2, *pix3;
282 283 284 285 286 287 288 289 290
              gfloat  mixy0[4];
              gfloat  mixy1[4];

              pix0 = get_pixel_color (in_buf, &src_rect, ix,     iy);
              pix1 = get_pixel_color (in_buf, &src_rect, ix + 1, iy);
              pix2 = get_pixel_color (in_buf, &src_rect, ix,     iy + 1);
              pix3 = get_pixel_color (in_buf, &src_rect, ix + 1, iy + 1);

              for (c = 0; c < 4; ++c)
291
                {
292 293 294
                  mixy0[c] = dy * (pix2[c] - pix0[c]) + pix0[c];
                  mixy1[c] = dy * (pix3[c] - pix1[c]) + pix1[c];
                  sum[c] += dx * (mixy1[c] - mixy0[c]) + mixy0[c];
295 296
                }
            }
297

298
          for (c = 0; c < 4; ++c)
299 300 301
            *out_pixel++ = sum[c] * inv_num_steps;
        }
    }
302

303 304
  gegl_buffer_set (output, roi, 0, babl_format ("RaGaBaA float"),
                   out_buf, GEGL_AUTO_ROWSTRIDE);
305

306 307 308 309 310 311 312
  g_free (in_buf);
  g_free (out_buf);

  return  TRUE;
}

static void
313
gegl_op_class_init (GeglOpClass *klass)
314
{
315 316
  GeglOperationClass       *operation_class;
  GeglOperationFilterClass *filter_class;
317 318 319 320

  operation_class = GEGL_OPERATION_CLASS (klass);
  filter_class = GEGL_OPERATION_FILTER_CLASS (klass);

321
  operation_class->prepare        = prepare;
322
  operation_class->opencl_support = TRUE;
323 324

  filter_class->process           = process;
325

326
  gegl_operation_class_set_keys (operation_class,
327
                                 "name",        "gegl:motion-blur-linear",
328
                                 "title",       _("Linear Motion Blur"),
329
                                 "compat-name", "gegl:motion-blur",
330
                                 "categories",  "blur",
331
                                 "description", _("Blur pixels in a direction, simulates blurring caused by moving camera in a straight line during exposure."),
332
    NULL);
333 334 335
}

#endif