Blame operations/workshop/warp.c

Packit bc1512
/* This file is an image processing operation for GEGL
Packit bc1512
 *
Packit bc1512
 * GEGL is free software; you can redistribute it and/or
Packit bc1512
 * modify it under the terms of the GNU Lesser General Public
Packit bc1512
 * License as published by the Free Software Foundation; either
Packit bc1512
 * version 3 of the License, or (at your option) any later version.
Packit bc1512
 *
Packit bc1512
 * GEGL is distributed in the hope that it will be useful,
Packit bc1512
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit bc1512
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit bc1512
 * Lesser General Public License for more details.
Packit bc1512
 *
Packit bc1512
 * You should have received a copy of the GNU Lesser General Public
Packit bc1512
 * License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
Packit bc1512
 *
Packit bc1512
 * Copyright 2011 Michael Muré <batolettre@gmail.com>
Packit bc1512
 */
Packit bc1512
Packit bc1512
#include "config.h"
Packit bc1512
#include <glib/gi18n-lib.h>
Packit bc1512
#include <math.h>
Packit bc1512
#include <stdio.h> /* for test only */
Packit bc1512
Packit bc1512
#ifdef GEGL_CHANT_PROPERTIES
Packit bc1512
Packit bc1512
gegl_chant_double (strength, _("Strength"), 0.0, 100.0, 50,
Packit bc1512
                   _("Effect Strength"))
Packit bc1512
gegl_chant_double (size, _("Size"), 1.0, 10000.0, 40.0,
Packit bc1512
                   _("Effect Size"))
Packit bc1512
gegl_chant_double (hardness, _("Hardness"), 0.0, 1.0, 0.5,
Packit bc1512
                   _("Effect Hardness"))
Packit bc1512
gegl_chant_path (stroke, _("Stroke"), _("Effect Strength"))
Packit bc1512
gegl_chant_enum (behavior, _("Behavior"), GeglWarpBehavior, GEGL_TYPE_WARP_BEHAVIOR,
Packit bc1512
                 GEGL_WARP_BEHAVIOR_MOVE, _("Behavior of the op"))
Packit bc1512
Packit bc1512
#else
Packit bc1512
Packit bc1512
#define GEGL_CHANT_TYPE_FILTER
Packit bc1512
#define GEGL_CHANT_C_FILE       "warp.c"
Packit bc1512
Packit bc1512
#include "gegl-plugin.h"
Packit bc1512
#include "gegl-path.h"
Packit bc1512
static void path_changed (GeglPath            *path,
Packit bc1512
                          const GeglRectangle *roi,
Packit bc1512
                          gpointer             userdata);
Packit bc1512
#include "gegl-chant.h"
Packit bc1512
Packit bc1512
typedef struct {
Packit bc1512
  gdouble     *lookup;
Packit bc1512
  GeglBuffer  *buffer;
Packit bc1512
  gdouble      last_x;
Packit bc1512
  gdouble      last_y;
Packit bc1512
  gboolean     last_point_set;
Packit bc1512
} WarpPrivate;
Packit bc1512
Packit bc1512
static void
Packit bc1512
path_changed (GeglPath            *path,
Packit bc1512
              const GeglRectangle *roi,
Packit bc1512
              gpointer             userdata)
Packit bc1512
{
Packit bc1512
  GeglRectangle rect = *roi;
Packit bc1512
  GeglChantO    *o   = GEGL_CHANT_PROPERTIES (userdata);
Packit bc1512
  /* invalidate the incoming rectangle */
Packit bc1512
Packit bc1512
  rect.x -= o->size/2;
Packit bc1512
  rect.y -= o->size/2;
Packit bc1512
  rect.width += o->size;
Packit bc1512
  rect.height += o->size;
Packit bc1512
Packit bc1512
  gegl_operation_invalidate (userdata, &rect, FALSE);
Packit bc1512
}
Packit bc1512
Packit bc1512
static void
Packit bc1512
prepare (GeglOperation *operation)
Packit bc1512
{
Packit bc1512
  GeglChantO  *o = GEGL_CHANT_PROPERTIES (operation);
Packit bc1512
  WarpPrivate *priv;
Packit bc1512
Packit bc1512
  const Babl *format = babl_format_n (babl_type ("float"), 2);
Packit bc1512
  gegl_operation_set_format (operation, "input", format);
Packit bc1512
  gegl_operation_set_format (operation, "output", format);
Packit bc1512
Packit bc1512
  if (!o->chant_data)
Packit bc1512
    {
Packit bc1512
      o->chant_data = g_slice_new (WarpPrivate);
Packit bc1512
    }
Packit bc1512
Packit bc1512
  priv = (WarpPrivate*) o->chant_data;
Packit bc1512
  priv->last_point_set = FALSE;
Packit bc1512
  priv->lookup = NULL;
Packit bc1512
  priv->buffer = NULL;
Packit bc1512
}
Packit bc1512
Packit bc1512
static void
Packit bc1512
finalize (GObject *object)
Packit bc1512
{
Packit bc1512
  GeglChantO *o = GEGL_CHANT_PROPERTIES (object);
Packit bc1512
Packit bc1512
  if (o->chant_data)
Packit bc1512
    {
Packit bc1512
      g_slice_free (WarpPrivate, o->chant_data);
Packit bc1512
      o->chant_data = NULL;
Packit bc1512
    }
Packit bc1512
Packit bc1512
  G_OBJECT_CLASS (gegl_chant_parent_class)->finalize (object);
Packit bc1512
}
Packit bc1512
Packit bc1512
static gdouble
Packit bc1512
gauss (gdouble f)
Packit bc1512
{
Packit bc1512
  /* This is not a real gauss function. */
Packit bc1512
  /* Approximation is valid if -1 < f < 1 */
Packit bc1512
  if (f < -0.5)
Packit bc1512
    {
Packit bc1512
      f = -1.0 - f;
Packit bc1512
      return (2.0 * f*f);
Packit bc1512
    }
Packit bc1512
Packit bc1512
  if (f < 0.5)
Packit bc1512
    return (1.0 - 2.0 * f*f);
Packit bc1512
Packit bc1512
  f = 1.0 - f;
Packit bc1512
  return (2.0 * f*f);
Packit bc1512
}
Packit bc1512
Packit bc1512
/* set up lookup table */
Packit bc1512
static void
Packit bc1512
calc_lut (GeglChantO  *o)
Packit bc1512
{
Packit bc1512
  WarpPrivate  *priv = (WarpPrivate*) o->chant_data;
Packit bc1512
  gint          length;
Packit bc1512
  gint          x;
Packit bc1512
  gdouble       exponent;
Packit bc1512
Packit bc1512
  length = ceil (0.5 * o->size + 1.0);
Packit bc1512
Packit bc1512
  priv->lookup = g_malloc (length * sizeof (gdouble));
Packit bc1512
Packit bc1512
  if ((1.0 - o->hardness) < 0.0000004)
Packit bc1512
    exponent = 1000000.0;
Packit bc1512
  else
Packit bc1512
    exponent = 0.4 / (1.0 - o->hardness);
Packit bc1512
Packit bc1512
  for (x = 0; x < length; x++)
Packit bc1512
    {
Packit bc1512
      priv->lookup[x] = gauss (pow (2.0 * x / o->size, exponent));
Packit bc1512
    }
Packit bc1512
}
Packit bc1512
Packit bc1512
static gdouble
Packit bc1512
get_stamp_force (GeglChantO *o,
Packit bc1512
                 gdouble     x,
Packit bc1512
                 gdouble     y)
Packit bc1512
{
Packit bc1512
  WarpPrivate  *priv = (WarpPrivate*) o->chant_data;
Packit bc1512
  gfloat        radius;
Packit bc1512
Packit bc1512
  if (!priv->lookup)
Packit bc1512
    {
Packit bc1512
      calc_lut (o);
Packit bc1512
    }
Packit bc1512
Packit bc1512
  radius = sqrt(x*x+y*y);
Packit bc1512
Packit bc1512
  if (radius < 0.5 * o->size + 1)
Packit bc1512
    {
Packit bc1512
      /* linear interpolation */
Packit bc1512
      gdouble a, ratio;
Packit bc1512
      gdouble before, after;
Packit bc1512
Packit bc1512
      a = floor (radius);
Packit bc1512
      ratio = (radius - a);
Packit bc1512
Packit bc1512
      before = priv->lookup[(gint) a];
Packit bc1512
      after = priv->lookup[(gint) a + 1];
Packit bc1512
Packit bc1512
      return ratio * before + (1.0 - ratio) * after;
Packit bc1512
    }
Packit bc1512
Packit bc1512
  return 0.0;
Packit bc1512
}
Packit bc1512
Packit bc1512
static void
Packit bc1512
stamp (GeglChantO          *o,
Packit bc1512
       const GeglRectangle *result,
Packit bc1512
       gdouble              x,
Packit bc1512
       gdouble              y)
Packit bc1512
{
Packit bc1512
  WarpPrivate         *priv = (WarpPrivate*) o->chant_data;
Packit bc1512
  GeglBufferIterator  *it;
Packit bc1512
  const Babl          *format;
Packit bc1512
  gdouble              influence;
Packit bc1512
  gdouble              x_mean = 0.0;
Packit bc1512
  gdouble              y_mean = 0.0;
Packit bc1512
  gint                 x_iter, y_iter;
Packit bc1512
  GeglRectangle        area = {x - o->size / 2.0,
Packit bc1512
                               y - o->size / 2.0,
Packit bc1512
                               o->size,
Packit bc1512
                               o->size};
Packit bc1512
Packit bc1512
  /* first point of the stroke */
Packit bc1512
  if (!priv->last_point_set)
Packit bc1512
    {
Packit bc1512
      priv->last_x = x;
Packit bc1512
      priv->last_y = y;
Packit bc1512
      priv->last_point_set = TRUE;
Packit bc1512
      return;
Packit bc1512
    }
Packit bc1512
Packit bc1512
  /* don't stamp if outside the roi treated */
Packit bc1512
  if (!gegl_rectangle_intersect (NULL, result, &area))
Packit bc1512
    return;
Packit bc1512
Packit bc1512
  format = babl_format_n (babl_type ("float"), 2);
Packit bc1512
Packit bc1512
  /* If needed, compute the mean deformation */
Packit bc1512
  if (o->behavior == GEGL_WARP_BEHAVIOR_SMOOTH)
Packit bc1512
    {
Packit bc1512
      gint pixel_count = 0;
Packit bc1512
Packit bc1512
      it = gegl_buffer_iterator_new (priv->buffer, &area, 0, format, GEGL_BUFFER_READ, GEGL_ABYSS_NONE);
Packit bc1512
Packit bc1512
      while (gegl_buffer_iterator_next (it))
Packit bc1512
        {
Packit bc1512
          gint    n_pixels    = it->length;
Packit bc1512
          gfloat *coords      = it->data[0];
Packit bc1512
Packit bc1512
          while (n_pixels--)
Packit bc1512
            {
Packit bc1512
              x_mean += coords[0];
Packit bc1512
              y_mean += coords[1];
Packit bc1512
              coords += 2;
Packit bc1512
            }
Packit bc1512
          pixel_count += it->roi->width * it->roi->height;
Packit bc1512
        }
Packit bc1512
      x_mean /= pixel_count;
Packit bc1512
      y_mean /= pixel_count;
Packit bc1512
    }
Packit bc1512
Packit bc1512
  it = gegl_buffer_iterator_new (priv->buffer, &area, 0, format, GEGL_BUFFER_READWRITE, GEGL_ABYSS_NONE);
Packit bc1512
Packit bc1512
  while (gegl_buffer_iterator_next (it))
Packit bc1512
    {
Packit bc1512
      /* iterate inside the stamp roi */
Packit bc1512
      gint    n_pixels = it->length;
Packit bc1512
      gfloat *coords   = it->data[0];
Packit bc1512
Packit bc1512
      x_iter = it->roi->x; /* initial x         */
Packit bc1512
      y_iter = it->roi->y; /* and y coordinates */
Packit bc1512
Packit bc1512
      while (n_pixels--)
Packit bc1512
        {
Packit bc1512
          influence = 0.01 * o->strength * get_stamp_force (o,
Packit bc1512
                                                            x_iter - x,
Packit bc1512
                                                            y_iter - y);
Packit bc1512
Packit bc1512
          switch (o->behavior)
Packit bc1512
            {
Packit bc1512
              case GEGL_WARP_BEHAVIOR_MOVE:
Packit bc1512
                coords[0] += influence * (priv->last_x - x);
Packit bc1512
                coords[1] += influence * (priv->last_y - y);
Packit bc1512
                break;
Packit bc1512
              case GEGL_WARP_BEHAVIOR_GROW:
Packit bc1512
                coords[0] -= 2.0 * influence * (x_iter - x) / o->size;
Packit bc1512
                coords[1] -= 2.0 * influence * (y_iter - y) / o->size;
Packit bc1512
                break;
Packit bc1512
              case GEGL_WARP_BEHAVIOR_SHRINK:
Packit bc1512
                coords[0] += 2.0 * influence * (x_iter - x) / o->size;
Packit bc1512
                coords[1] += 2.0 * influence * (y_iter - y) / o->size;
Packit bc1512
                break;
Packit bc1512
              case GEGL_WARP_BEHAVIOR_SWIRL_CW:
Packit bc1512
                coords[0] += 3.0 * influence * (y_iter - y) / o->size;
Packit bc1512
                coords[1] -= 5.0 * influence * (x_iter - x) / o->size;
Packit bc1512
                break;
Packit bc1512
              case GEGL_WARP_BEHAVIOR_SWIRL_CCW:
Packit bc1512
                coords[0] -= 3.0 * influence * (y_iter - y) / o->size;
Packit bc1512
                coords[1] += 5.0 * influence * (x_iter - x) / o->size;
Packit bc1512
                break;
Packit bc1512
              case GEGL_WARP_BEHAVIOR_ERASE:
Packit bc1512
                coords[0] *= 1.0 - MIN (influence, 1.0);
Packit bc1512
                coords[1] *= 1.0 - MIN (influence, 1.0);
Packit bc1512
                break;
Packit bc1512
              case GEGL_WARP_BEHAVIOR_SMOOTH:
Packit bc1512
                coords[0] -= influence * (coords[0] - x_mean);
Packit bc1512
                coords[1] -= influence * (coords[1] - y_mean);
Packit bc1512
                break;
Packit bc1512
            }
Packit bc1512
Packit bc1512
          coords += 2;
Packit bc1512
Packit bc1512
          /* update x and y coordinates */
Packit bc1512
          x_iter++;
Packit bc1512
          if (x_iter >= (it->roi->x + it->roi->width))
Packit bc1512
            {
Packit bc1512
              x_iter = it->roi->x;
Packit bc1512
              y_iter++;
Packit bc1512
            }
Packit bc1512
        }
Packit bc1512
    }
Packit bc1512
Packit bc1512
  /* Memorize the stamp location for movement dependant behavior like move */
Packit bc1512
  priv->last_x = x;
Packit bc1512
  priv->last_y = y;
Packit bc1512
}
Packit bc1512
Packit bc1512
static gboolean
Packit bc1512
process (GeglOperation       *operation,
Packit bc1512
         GeglBuffer          *input,
Packit bc1512
         GeglBuffer          *output,
Packit bc1512
         const GeglRectangle *result,
Packit bc1512
         gint                 level)
Packit bc1512
{
Packit bc1512
  GeglChantO          *o = GEGL_CHANT_PROPERTIES (operation);
Packit bc1512
  WarpPrivate         *priv = (WarpPrivate*) o->chant_data;
Packit bc1512
  gdouble              dist;
Packit bc1512
  gdouble              stamps;
Packit bc1512
  gdouble              spacing = MAX (o->size * 0.01, 0.5); /*1% spacing for starters*/
Packit bc1512
Packit bc1512
  GeglPathPoint        prev, next, lerp;
Packit bc1512
  gulong               i;
Packit bc1512
  GeglPathList        *event;
Packit bc1512
Packit bc1512
  printf("Process %p\n", operation);
Packit bc1512
Packit bc1512
  priv->buffer = gegl_buffer_dup (input);
Packit bc1512
Packit bc1512
  event = gegl_path_get_path (o->stroke);
Packit bc1512
Packit bc1512
  prev = *(event->d.point);
Packit bc1512
Packit bc1512
  while (event->next)
Packit bc1512
    {
Packit bc1512
      event = event->next;
Packit bc1512
      next = *(event->d.point);
Packit bc1512
      dist = gegl_path_point_dist (&next, &prev;;
Packit bc1512
      stamps = dist / spacing;
Packit bc1512
Packit bc1512
      if (stamps < 1)
Packit bc1512
        {
Packit bc1512
          stamp (o, result, next.x, next.y);
Packit bc1512
          prev = next;
Packit bc1512
        }
Packit bc1512
      else
Packit bc1512
        {
Packit bc1512
          for (i = 0; i < stamps; i++)
Packit bc1512
            {
Packit bc1512
              gegl_path_point_lerp (&lerp, &prev, &next, (i * spacing) / dist);
Packit bc1512
              stamp (o, result, lerp.x, lerp.y);
Packit bc1512
            }
Packit bc1512
          prev = lerp;
Packit bc1512
        }
Packit bc1512
    }
Packit bc1512
Packit bc1512
  /* Affect the output buffer */
Packit bc1512
  gegl_buffer_copy (priv->buffer, result, output, result);
Packit bc1512
  gegl_buffer_set_extent (output, gegl_buffer_get_extent (input));
Packit bc1512
  g_object_unref (priv->buffer);
Packit bc1512
Packit bc1512
  /* prepare for the recomputing of the op */
Packit bc1512
  priv->last_point_set = FALSE;
Packit bc1512
Packit bc1512
  return TRUE;
Packit bc1512
}
Packit bc1512
Packit bc1512
static void
Packit bc1512
gegl_chant_class_init (GeglChantClass *klass)
Packit bc1512
{
Packit bc1512
  GObjectClass               *object_class    = G_OBJECT_CLASS (klass);
Packit bc1512
  GeglOperationClass         *operation_class = GEGL_OPERATION_CLASS (klass);
Packit bc1512
  GeglOperationFilterClass   *filter_class    = GEGL_OPERATION_FILTER_CLASS (klass);
Packit bc1512
Packit bc1512
  object_class->finalize = finalize;
Packit bc1512
  operation_class->prepare = prepare;
Packit bc1512
  filter_class->process = process;
Packit bc1512
Packit bc1512
  gegl_operation_class_set_keys (operation_class,
Packit bc1512
  "name"       , "gegl:warp",
Packit bc1512
  "categories"  , "transform",
Packit bc1512
  "description" , _("Compute a relative displacement mapping from a stroke"),
Packit bc1512
  NULL);
Packit bc1512
}
Packit bc1512
#endif