|
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
|