oilify.c 14.3 KB
Newer Older
Hans Lo's avatar
Hans Lo committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/* 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 1995 Spencer Kimball and Peter Mattis
 * Copyright 1996 Torsten Martinsen
 * Copyright 2007 Daniel Richard G.
 * Copyright 2011 Hans Lo <hansshulo@gmail.com>
 */

#include "config.h"
#include <glib/gi18n-lib.h>

25
#ifdef GEGL_PROPERTIES
Hans Lo's avatar
Hans Lo committed
26

27 28 29 30 31 32 33 34 35 36 37 38 39 40
property_int    (mask_radius, _("Mask Radius"), 4)
    description (_("Radius of circle around pixel"))
    value_range (1, 25)
    ui_meta     ("unit", "pixel-distance")

property_int    (exponent, _("Exponent"), 8)
    value_range (1, 20)

property_int (intensities, _("Number of intensities"), 128)
    description(_("Histogram size"))
    value_range (8, 256)

property_boolean (use_inten, _("Intensity Mode"), TRUE)
    description(_("Use pixel luminance values"))
Hans Lo's avatar
Hans Lo committed
41 42 43

#else

44 45
#define GEGL_OP_AREA_FILTER
#define GEGL_OP_C_FILE       "oilify.c"
Hans Lo's avatar
Hans Lo committed
46

47
#include "gegl-op.h"
Hans Lo's avatar
Hans Lo committed
48 49 50 51
#include <math.h>

#define NUM_INTENSITIES       256

52 53
/* Get the pixel from x, y offset from the center pixel src_pix */
static void
54 55 56 57 58
get_pixel (gint    x,
           gint    y,
           gint    buf_width,
           gfloat *src_begin,
           gfloat *dst)
59 60 61 62 63 64 65 66 67
{
  gint b;
  gfloat* src = src_begin + 4*(x + buf_width*y);
  for (b = 0; b < 4; b++)
    {
      dst[b] = src[b];
    }
}
static void
68 69 70 71 72
get_pixel_inten (gint    x,
                 gint    y,
                 gint    buf_width,
                 gfloat *inten_begin,
                 gfloat *dst)
73 74 75 76
{
  *dst = *(inten_begin + (x + buf_width*y));
}
static void
77 78 79 80 81 82 83 84 85
oilify_pixel_inten (gint     x,
                    gint     y,
                    gdouble  radius,
                    gint     exponent,
                    gint     intensities,
                    gint     buf_width,
                    gfloat  *src_buf,
                    gfloat  *inten_buf,
                    gfloat  *dst_pixel)
Hans Lo's avatar
Hans Lo committed
86
{
87
  gfloat cumulative_rgb[4][NUM_INTENSITIES];
88
  gint   hist_inten[NUM_INTENSITIES];
89
  gfloat mult_inten;
Hans Lo's avatar
Hans Lo committed
90
  gfloat temp_pixel[4];
91
  gint   ceil_radius = ceil (radius);
Hans Lo's avatar
Hans Lo committed
92 93
  gdouble radius_sq = radius*radius;
  gint i, j, b;
94
  gint inten_max;
Hans Lo's avatar
Hans Lo committed
95
  gint intensity;
96
  gfloat ratio, temp_inten_pixel;
97
  gfloat weight;
98
  gfloat color[4];
99
  gfloat div;
100
  for (i = 0; i < intensities; i++)
Hans Lo's avatar
Hans Lo committed
101
    {
102 103
      hist_inten[i] = 0;
      for (b = 0; b < 4; b++)
104
          cumulative_rgb[b][i] = 0.0;
Hans Lo's avatar
Hans Lo committed
105
    }
106

107 108
  /* calculate histograms */
  for (i = -ceil_radius; i <= ceil_radius; i++)
Hans Lo's avatar
Hans Lo committed
109
    {
110 111 112 113
      for (j = -ceil_radius; j <= ceil_radius; j++)
        {
          if (i*i + j*j <= radius_sq)
            {
Hans Lo's avatar
Hans Lo committed
114
              get_pixel (x + i,
115 116 117 118
                         y + j,
                         buf_width,
                         src_buf,
                         temp_pixel);
119 120 121 122 123 124 125 126
              get_pixel_inten (x + i,
                               y + j,
                               buf_width,
                               inten_buf,
                               &temp_inten_pixel);
              intensity = temp_inten_pixel * (intensities - 1);
              hist_inten[intensity]++;
              for (b = 0; b < 4; b++)
127
                {
128
                  cumulative_rgb[b][intensity] += temp_pixel[b];
129 130 131
                }
            }
        }
Hans Lo's avatar
Hans Lo committed
132
    }
133

134 135 136
  inten_max = 1;

  /* calculated maximums */
137
  for (i = 0; i < intensities; i++) {
138 139
    inten_max = MAX (inten_max, hist_inten[i]);
  }
140

141 142 143
  /* calculate weight and use it to set the pixel */
  div = 0.0;

144
  for (b = 0; b < 4; b++)
145 146
    color[b] = 0.0;
  for (i = 0; i < intensities; i++)
Hans Lo's avatar
Hans Lo committed
147
    {
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
      if (hist_inten[i] > 0)
      {
        ratio = (gfloat) hist_inten[i] / (gfloat) inten_max;

        /* using this instead of pow function gives HUGE performance improvement
           but we cannot use floating point exponent... */
        weight = 1.;
        for(j = 0; j < exponent; j++)
          weight *= ratio;
        /* weight = powf(ratio, exponent); */
        mult_inten = weight / (gfloat) hist_inten[i];

        div += weight;
        for (b = 0; b < 4; b++)
          color[b] += mult_inten * cumulative_rgb[b][i];
Hans Lo's avatar
Hans Lo committed
163
      }
164
    }
165 166 167
  for (b = 0; b < 4; b++)
    dst_pixel[b] = color[b]/div;
}
168

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
static void
oilify_pixel (gint           x,
              gint           y,
              gdouble        radius,
              gint           exponent,
              gint           intensities,
              gint           buf_width,
              gfloat        *src_buf,
              gfloat        *dst_pixel)
{
  gint   hist[4][NUM_INTENSITIES];
  gfloat temp_pixel[4];
  gint   ceil_radius = ceil (radius);
  gdouble radius_sq = radius*radius;
  gint i, j, b;
  gint hist_max[4];
  gint intensity;
  gfloat sum[4];
  gfloat ratio;
  gfloat weight;
  gfloat result[4];
  gfloat div[4];
  for (i = 0; i < intensities; i++)
192
    {
193
      for (b = 0; b < 4; b++)
194
        {
195
          hist[b][i] = 0;
196 197
        }
    }
198 199 200

  /* calculate histograms */
  for (i = -ceil_radius; i <= ceil_radius; i++)
Hans Lo's avatar
Hans Lo committed
201
    {
202
      for (j = -ceil_radius; j <= ceil_radius; j++)
203
        {
204
          if (i*i + j*j <= radius_sq)
205
            {
206 207 208 209 210 211 212 213 214 215
              get_pixel (x + i,
                         y + j,
                         buf_width,
                         src_buf,
                         temp_pixel);
              for (b = 0; b < 4; b++)
                {
                  intensity = temp_pixel[b] * (intensities - 1);
                  hist[b][intensity]++;
                }
216 217
            }
        }
Hans Lo's avatar
Hans Lo committed
218
    }
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
    for (b = 0; b < 4; b++)
      hist_max[b] = 1;
    for (i = 0; i < intensities; i++) {
      for (b = 0; b < 4; b++)
        if(hist_max[b] < hist[b][i]) /* MAX macros too slow here */
          hist_max[b] = hist[b][i];
    }

  /* calculate weight and use it to set the pixel */

    for (b = 0; b < 4; b++)
      {
        sum[b] = 0.0;
        div[b] = 0.0;
      }
    for (i = 0; i < intensities; i++)
      {
        /* UNROLL this bottleneck loop, up to 50% faster */
        #define DO_HIST_STEP(b) if(hist[b][i] > 0)                          \
          {                                                                 \
            ratio = (gfloat) hist[b][i] / (gfloat) hist_max[b];             \
            weight = 1.;                                                    \
            for(j = 0; j < exponent; j++)                                   \
              weight *= ratio;                                              \
            sum[b] += weight * (gfloat) i;                                  \
            div[b] += weight;                                               \
          }

        DO_HIST_STEP(0)
        DO_HIST_STEP(1)
        DO_HIST_STEP(2)
        DO_HIST_STEP(3)
        #undef DO_HIST_STEP
      }

    for (b = 0; b < 4; b++)
      {
        result[b] = sum[b] / (gfloat) (intensities - 1);
        dst_pixel[b] = result[b]/div[b];
      }
Hans Lo's avatar
Hans Lo committed
259 260
}

261 262
static void
prepare (GeglOperation *operation)
Hans Lo's avatar
Hans Lo committed
263
{
264
  GeglProperties              *o;
Hans Lo's avatar
Hans Lo committed
265 266 267
  GeglOperationAreaFilter *op_area;

  op_area = GEGL_OPERATION_AREA_FILTER (operation);
268
  o       = GEGL_PROPERTIES (operation);
Hans Lo's avatar
Hans Lo committed
269

270
  op_area->left   =
271 272 273
  op_area->right  =
  op_area->top    =
  op_area->bottom = o->mask_radius;
Hans Lo's avatar
Hans Lo committed
274 275 276 277 278 279 280

  gegl_operation_set_format (operation, "input",
                             babl_format ("RGBA float"));
  gegl_operation_set_format (operation, "output",
                             babl_format ("RGBA float"));
}

281 282 283
#include "opencl/gegl-cl.h"
#include "buffer/gegl-buffer-cl-iterator.h"

Victor Oliveira's avatar
Victor Oliveira committed
284
#include "opencl/oilify.cl.h"
285 286 287

static GeglClRunData *cl_data = NULL;

Victor Oliveira's avatar
Victor Oliveira committed
288
static gboolean
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
cl_oilify (cl_mem              in_tex,
           cl_mem              out_tex,
           size_t              global_worksize,
           const GeglRectangle *roi,
           gint                mask_radius,
           gint                number_of_intensities,
           gint                exponent,
           gboolean            use_inten)
{

  const size_t gbl_size[2] = {roi->width,roi->height};
  cl_int radius      = mask_radius;
  cl_int intensities = number_of_intensities;
  cl_float exp       = (gfloat)exponent;
  cl_int cl_err      = 0;
  gint arg = 0;

Victor Oliveira's avatar
Victor Oliveira committed
306 307 308 309 310 311 312
  if (!cl_data)
    {
      const char *kernel_name[] = {"kernel_oilify", "kernel_oilify_inten", NULL};
      cl_data = gegl_cl_compile_and_build(oilify_cl_source, kernel_name);
    }
  if (!cl_data)  return TRUE;

313
  /* simple hack: select suitable kernel using boolean, 0 - no intensity mode, 1 - intensity mode */
Victor Oliveira's avatar
Victor Oliveira committed
314 315 316 317 318 319 320 321 322 323
  cl_err = gegl_clSetKernelArg(cl_data->kernel[use_inten], arg++, sizeof(cl_mem),   (void*)&in_tex);
  CL_CHECK;
  cl_err = gegl_clSetKernelArg(cl_data->kernel[use_inten], arg++, sizeof(cl_mem),   (void*)&out_tex);
  CL_CHECK;
  cl_err = gegl_clSetKernelArg(cl_data->kernel[use_inten], arg++, sizeof(cl_int),   (void*)&radius);
  CL_CHECK;
  cl_err = gegl_clSetKernelArg(cl_data->kernel[use_inten], arg++, sizeof(cl_int),   (void*)&intensities);
  CL_CHECK;
  cl_err = gegl_clSetKernelArg(cl_data->kernel[use_inten], arg++, sizeof(cl_float), (void*)&exp);
  CL_CHECK;
324 325 326 327 328

  cl_err = gegl_clEnqueueNDRangeKernel(gegl_cl_get_command_queue(),
                                       cl_data->kernel[use_inten], 2,
                                       NULL, gbl_size, NULL,
                                       0, NULL, NULL);
Victor Oliveira's avatar
Victor Oliveira committed
329
  CL_CHECK;
330

Victor Oliveira's avatar
Victor Oliveira committed
331 332 333 334
  return FALSE;

error:
  return TRUE;
335 336 337 338 339 340 341 342 343 344 345 346
}

static gboolean
cl_process (GeglOperation       *operation,
            GeglBuffer          *input,
            GeglBuffer          *output,
            const GeglRectangle *result)
{
  const Babl *in_format  = gegl_operation_get_format (operation, "input");
  const Babl *out_format = gegl_operation_get_format (operation, "output");
  gint err;

347
  GeglProperties *o = GEGL_PROPERTIES (operation);
348

349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
  GeglBufferClIterator *i = gegl_buffer_cl_iterator_new (output,
                                                         result,
                                                         out_format,
                                                         GEGL_CL_BUFFER_WRITE);

  gint read = gegl_buffer_cl_iterator_add_2 (i,
                                             input,
                                             result,
                                             in_format,
                                             GEGL_CL_BUFFER_READ,
                                             o->mask_radius,
                                             o->mask_radius,
                                             o->mask_radius,
                                             o->mask_radius,
                                             GEGL_ABYSS_CLAMP);

365 366
  while (gegl_buffer_cl_iterator_next (i, &err))
    {
Victor Oliveira's avatar
Victor Oliveira committed
367
      if (err) return FALSE;
368 369 370 371 372 373 374 375 376 377 378

      err = cl_oilify(i->tex[read],
                      i->tex[0],
                      i->size[0],
                      &i->roi[0],
                      o->mask_radius,
                      o->intensities,
                      o->exponent,
                      o->use_inten);

      if (err) return FALSE;
379
    }
Victor Oliveira's avatar
Victor Oliveira committed
380

381 382 383
  return TRUE;
}

Hans Lo's avatar
Hans Lo committed
384 385 386 387 388 389 390
static gboolean
process (GeglOperation       *operation,
         GeglBuffer          *input,
         GeglBuffer          *output,
         const GeglRectangle *result,
         gint                 level)
{
391
  GeglProperties *o                = GEGL_PROPERTIES (operation);
392 393 394 395 396 397 398 399 400 401 402
  GeglOperationAreaFilter *op_area = GEGL_OPERATION_AREA_FILTER (operation);

  gint x = o->mask_radius; /* initial x                   */
  gint y = o->mask_radius; /*           and y coordinates */
  gfloat *src_buf;
  gfloat *dst_buf;
  gfloat *inten_buf;
  gfloat *out_pixel;
  gint n_pixels = result->width * result->height;
  GeglRectangle src_rect;
  gint total_pixels;
Hans Lo's avatar
Hans Lo committed
403

404
  if (gegl_operation_use_opencl (operation))
405
    if (cl_process (operation, input, output, result))
406
      return TRUE;
Hans Lo's avatar
Hans Lo committed
407

408 409 410 411
  src_rect.x      = result->x - op_area->left;
  src_rect.width  = result->width + op_area->left + op_area->right;
  src_rect.y      = result->y - op_area->top;
  src_rect.height = result->height + op_area->top + op_area->bottom;
Hans Lo's avatar
Hans Lo committed
412

413
  total_pixels = src_rect.width * src_rect.height;
Hans Lo's avatar
Hans Lo committed
414

415 416
  src_buf = gegl_malloc (4 * total_pixels * sizeof (gfloat));
  dst_buf = gegl_malloc (4 * n_pixels * sizeof (gfloat));
417
  if (o->use_inten)
418
    inten_buf = gegl_malloc (total_pixels * sizeof (gfloat));
419 420
  else
    inten_buf = NULL;
421

422
  gegl_buffer_get (input, &src_rect, 1.0, babl_format ("RGBA float"),
423
                   src_buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP);
424

425
  if (inten_buf)
426 427
    gegl_buffer_get (input, &src_rect, 1.0, babl_format ("Y float"),
                   inten_buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP);
Hans Lo's avatar
Hans Lo committed
428

429
  out_pixel = dst_buf;
430

Hans Lo's avatar
Hans Lo committed
431 432
  while (n_pixels--)
    {
433
      if (inten_buf)
434
        oilify_pixel_inten (x, y, o->mask_radius, o->exponent, o->intensities,
435
                   src_rect.width, src_buf, inten_buf, out_pixel);
436 437 438
      else
        oilify_pixel (x, y, o->mask_radius, o->exponent, o->intensities,
                   src_rect.width, src_buf, out_pixel);
Hans Lo's avatar
Hans Lo committed
439 440 441 442
      out_pixel += 4;

      /* update x and y coordinates */
      x++;
443
      if (x >= result->width + o->mask_radius)
Hans Lo's avatar
Hans Lo committed
444
        {
445
          x=o->mask_radius;
Hans Lo's avatar
Hans Lo committed
446 447 448 449
          y++;
        }
    }

450

451 452 453
  gegl_buffer_set (output, result, 0,
                   babl_format ("RGBA float"),
                   dst_buf, GEGL_AUTO_ROWSTRIDE);
454 455
  gegl_free (src_buf);
  gegl_free (dst_buf);
456
  if (inten_buf)
457
    gegl_free (inten_buf);
Hans Lo's avatar
Hans Lo committed
458 459 460 461 462

  return  TRUE;
}

static void
463
gegl_op_class_init (GeglOpClass *klass)
Hans Lo's avatar
Hans Lo committed
464 465 466 467 468 469 470 471 472 473 474
{
  GeglOperationClass       *operation_class;
  GeglOperationFilterClass *filter_class;

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

  filter_class->process    = process;
  operation_class->prepare = prepare;

  gegl_operation_class_set_keys (operation_class,
475 476
                                 "categories" , "artistic",
                                 "name"       , "gegl:oilify",
477 478
                                 "title",      _("Oilify"),
                                 "license",     "GPL3+",
479
                                 "description",_("Emulate an oil painting"),
480
                                 NULL);
Hans Lo's avatar
Hans Lo committed
481 482
}
#endif