Blame operations/workshop/whirl-pinch.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 2010 Barak Itkin <lightningismyname@gmail.org>
Packit bc1512
 *
Packit bc1512
 * Based on "Whirl and Pinch" GIMP plugin
Packit bc1512
 * Copyright (C) 1997 Federico Mena Quintero
Packit bc1512
 * Copyright (C) 1997 Scott Goehring
Packit bc1512
 *
Packit bc1512
 * The workshop/mirrors.c operation by Alexia Death and Øyvind Kolås
Packit bc1512
 * was used as a template for this op file.
Packit bc1512
 */
Packit bc1512
Packit bc1512
/* TODO: Find some better algorithm to calculate the roi for each dest
Packit bc1512
 *       rectangle. Right now it simply asks for the entire image...
Packit bc1512
 */
Packit bc1512
Packit bc1512
#include "config.h"
Packit bc1512
#include <glib/gi18n-lib.h>
Packit bc1512
Packit bc1512
Packit bc1512
#ifdef GEGL_CHANT_PROPERTIES
Packit bc1512
Packit bc1512
gegl_chant_double (whirl,  _("Whirl"), -G_MAXDOUBLE, G_MAXDOUBLE, 90, _("Whirl angle (degrees)"))
Packit bc1512
Packit bc1512
gegl_chant_double (pinch,  _("Pinch"), -1, 1, 0, _("Pinch amount"))
Packit bc1512
Packit bc1512
gegl_chant_double (radius,  _("Radius"), 0, 2, 1, _("Radius (1.0 is the largest circle that fits in the image, and 2.0 goes all the way to the corners)"))
Packit bc1512
Packit bc1512
#else
Packit bc1512
Packit bc1512
#define GEGL_CHANT_TYPE_FILTER
Packit bc1512
#define GEGL_CHANT_C_FILE       "whirl-pinch.c"
Packit bc1512
Packit bc1512
#include "gegl-chant.h"
Packit bc1512
#include <math.h>
Packit bc1512
Packit bc1512
/* This function is a slightly modified version from the one in the original plugin */
Packit bc1512
static gboolean
Packit bc1512
calc_undistorted_coords (gdouble  wx,      gdouble  wy,
Packit bc1512
                         gdouble  cen_x,   gdouble  cen_y,
Packit bc1512
                         gdouble  scale_x, gdouble  scale_y,
Packit bc1512
                         gdouble  whirl,   gdouble  pinch,   gdouble  wpradius,
Packit bc1512
                         gdouble *x,       gdouble *y)
Packit bc1512
{
Packit bc1512
  gdouble  dx, dy;
Packit bc1512
  gdouble  d, factor;
Packit bc1512
  gdouble  dist;
Packit bc1512
  gdouble  ang, sina, cosa;
Packit bc1512
  gboolean inside;
Packit bc1512
Packit bc1512
  gdouble  radius = MAX(cen_x, cen_y);
Packit bc1512
  gdouble  radius2 = radius * radius * wpradius;
Packit bc1512
  /* Distances to center, scaled */
Packit bc1512
Packit bc1512
  dx = (wx - cen_x) * scale_x;
Packit bc1512
  dy = (wy - cen_y) * scale_y;
Packit bc1512
Packit bc1512
  /* Distance^2 to center of *circle* (scaled ellipse) */
Packit bc1512
Packit bc1512
  d = dx * dx + dy * dy;
Packit bc1512
Packit bc1512
  /*  If we are inside circle, then distort.
Packit bc1512
   *  Else, just return the same position
Packit bc1512
   */
Packit bc1512
Packit bc1512
  inside = (d < radius2);
Packit bc1512
Packit bc1512
  /* If d is 0, then we are exactly at the center, so the transform has
Packit bc1512
   * no effect. The original version of the gimp plugin simply created
Packit bc1512
   * an offset of the original points to avoid this issue, but that is
Packit bc1512
   * less accurate...
Packit bc1512
   */
Packit bc1512
  if (inside && d > 0)
Packit bc1512
    {
Packit bc1512
      dist = sqrt(d / wpradius) / radius;
Packit bc1512
Packit bc1512
      /* Pinch */
Packit bc1512
Packit bc1512
      factor = pow (sin (G_PI_2 * dist), -pinch);
Packit bc1512
Packit bc1512
      dx *= factor;
Packit bc1512
      dy *= factor;
Packit bc1512
Packit bc1512
      /* Whirl */
Packit bc1512
Packit bc1512
      factor = 1.0 - dist;
Packit bc1512
Packit bc1512
      ang = whirl * factor * factor;
Packit bc1512
Packit bc1512
      sina = sin (ang);
Packit bc1512
      cosa = cos (ang);
Packit bc1512
Packit bc1512
      *x = (cosa * dx - sina * dy) / scale_x + cen_x;
Packit bc1512
      *y = (sina * dx + cosa * dy) / scale_y + cen_y;
Packit bc1512
    }
Packit bc1512
  else
Packit bc1512
    {
Packit bc1512
      *x = wx;
Packit bc1512
      *y = wy;
Packit bc1512
    }
Packit bc1512
Packit bc1512
  return inside;
Packit bc1512
}
Packit bc1512
Packit bc1512
/* Apply the actual transform */
Packit bc1512
Packit bc1512
static void
Packit bc1512
apply_whirl_pinch (gdouble whirl, gdouble pinch, gdouble radius,
Packit bc1512
                   gdouble cen_x, gdouble cen_y,
Packit bc1512
                   const Babl    *format,
Packit bc1512
                   GeglBuffer *src,
Packit bc1512
                   GeglRectangle *in_boundary,
Packit bc1512
                   GeglBuffer *dst,
Packit bc1512
                   GeglRectangle *boundary,
Packit bc1512
                   const GeglRectangle *roi)
Packit bc1512
{
Packit bc1512
  gfloat *dst_buf;
Packit bc1512
  gint row, col;
Packit bc1512
  gdouble scale_x, scale_y;
Packit bc1512
  gdouble cx, cy;
Packit bc1512
  GeglSampler *sampler;
Packit bc1512
Packit bc1512
  /* Get buffer in which to place dst pixels. */
Packit bc1512
  dst_buf = g_new0 (gfloat, roi->width * roi->height * 4);
Packit bc1512
Packit bc1512
  whirl = whirl * G_PI / 180;
Packit bc1512
Packit bc1512
  scale_x = 1.0;
Packit bc1512
  scale_y = roi->width / (gdouble) roi->height;
Packit bc1512
  sampler = gegl_buffer_sampler_new (src, babl_format ("RaGaBaA float"),
Packit bc1512
                                     GEGL_SAMPLER_LOHALO);
Packit bc1512
Packit bc1512
  for (row = 0; row < roi->height; row++) {
Packit bc1512
    for (col = 0; col < roi->width; col++) {
Packit bc1512
        GeglMatrix2 scale;
Packit bc1512
#define gegl_unmap(u,v,du,dv) \
Packit bc1512
        { \
Packit bc1512
          calc_undistorted_coords (u, v,\
Packit bc1512
                                   cen_x, cen_y,\
Packit bc1512
                                   scale_x, scale_y,\
Packit bc1512
                                   whirl, pinch, radius,\
Packit bc1512
                                   &cx, &cy);\
Packit bc1512
          du=cx;dv=cy;\
Packit bc1512
        }
Packit bc1512
        gegl_sampler_compute_scale (scale, roi->x + col, roi->y + row);
Packit bc1512
        gegl_unmap (roi->x + col, roi->y + row, cx, cy);
Packit bc1512
Packit bc1512
        gegl_sampler_get (sampler, cx, cy, &scale, &dst_buf[(row * roi->width + col) * 4]);
Packit bc1512
    } /* for */
Packit bc1512
  } /* for */
Packit bc1512
Packit bc1512
  /* Store dst pixels. */
Packit bc1512
  gegl_buffer_set (dst, roi, 0, format, dst_buf, GEGL_AUTO_ROWSTRIDE);
Packit bc1512
Packit bc1512
  gegl_buffer_flush(dst);
Packit bc1512
Packit bc1512
  g_free (dst_buf);
Packit bc1512
  g_object_unref (sampler);
Packit bc1512
}
Packit bc1512
Packit bc1512
/*****************************************************************************/
Packit bc1512
Packit bc1512
/* Compute the region for which this operation is defined.
Packit bc1512
 */
Packit bc1512
static GeglRectangle
Packit bc1512
get_bounding_box (GeglOperation *operation)
Packit bc1512
{
Packit bc1512
  GeglRectangle  result = {0,0,0,0};
Packit bc1512
  GeglRectangle *in_rect = gegl_operation_source_get_bounding_box (operation, "input");
Packit bc1512
Packit bc1512
  if (!in_rect){
Packit bc1512
    return result;
Packit bc1512
  }
Packit bc1512
  else
Packit bc1512
    return *in_rect;
Packit bc1512
}
Packit bc1512
Packit bc1512
/* Compute the input rectangle required to compute the specified region of interest (roi).
Packit bc1512
 */
Packit bc1512
static GeglRectangle
Packit bc1512
get_required_for_output (GeglOperation       *operation,
Packit bc1512
                         const gchar         *input_pad,
Packit bc1512
                         const GeglRectangle *roi)
Packit bc1512
{
Packit bc1512
  GeglRectangle *result = gegl_operation_source_get_bounding_box (operation, "input");
Packit bc1512
Packit bc1512
  return *result;
Packit bc1512
}
Packit bc1512
Packit bc1512
/* Specify the input and output buffer formats.
Packit bc1512
 */
Packit bc1512
static void
Packit bc1512
prepare (GeglOperation *operation)
Packit bc1512
{
Packit bc1512
  gegl_operation_set_format (operation, "input", babl_format ("RaGaBaA float"));
Packit bc1512
  gegl_operation_set_format (operation, "output", babl_format ("RaGaBaA float"));
Packit bc1512
}
Packit bc1512
Packit bc1512
/* Perform the specified operation.
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
  GeglRectangle boundary = gegl_operation_get_bounding_box (operation);
Packit bc1512
  const Babl *format = babl_format ("RaGaBaA float");
Packit bc1512
Packit bc1512
  apply_whirl_pinch (o->whirl,
Packit bc1512
                     o->pinch,
Packit bc1512
                     o->radius,
Packit bc1512
                     boundary.width / 2.0,
Packit bc1512
                     boundary.height / 2.0,
Packit bc1512
                     format,
Packit bc1512
                     input,
Packit bc1512
                    &boundary,
Packit bc1512
                     output,
Packit bc1512
                    &boundary,
Packit bc1512
                     result);
Packit bc1512
  return TRUE;
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
static void
Packit bc1512
gegl_chant_class_init (GeglChantClass *klass)
Packit bc1512
{
Packit bc1512
  GeglOperationClass       *operation_class;
Packit bc1512
  GeglOperationFilterClass *filter_class;
Packit bc1512
Packit bc1512
  operation_class = GEGL_OPERATION_CLASS (klass);
Packit bc1512
  filter_class    = GEGL_OPERATION_FILTER_CLASS (klass);
Packit bc1512
Packit bc1512
  filter_class->process = process;
Packit bc1512
  operation_class->prepare = prepare;
Packit bc1512
  operation_class->get_bounding_box = get_bounding_box;
Packit bc1512
  operation_class->get_required_for_output = get_required_for_output;
Packit bc1512
Packit bc1512
  gegl_operation_class_set_keys (operation_class,
Packit bc1512
  "name"        , "gegl:whirl-pinch",
Packit bc1512
  "categories"  , "distort",
Packit bc1512
  "description" ,
Packit bc1512
        _("Applies whirling and pinching on the image"),
Packit bc1512
        NULL);
Packit bc1512
}
Packit bc1512
Packit bc1512
#endif