mirror of https://github.com/GNOME/gimp.git
1804 lines
54 KiB
C
1804 lines
54 KiB
C
/* GIMP - The GNU Image Manipulation Program
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/* IWarp a plug-in for GIMP
|
|
Version 0.1
|
|
|
|
IWarp is a gimp plug-in for interactive image warping. To apply the
|
|
selected deformation to the image, press the left mouse button and
|
|
move the mouse pointer in the preview image.
|
|
|
|
Copyright (C) 1997 Norbert Schmitz
|
|
nobert.schmitz@student.uni-tuebingen.de
|
|
|
|
Most of the gimp and gtk specific code is taken from other plug-ins
|
|
|
|
v0.11a
|
|
animation of non-alpha layers (background) creates now layers with
|
|
alpha channel. (thanks to Adrian Likins for reporting this bug)
|
|
|
|
v0.12
|
|
fixes a very bad bug.
|
|
(thanks to Arthur Hagen for reporting it)
|
|
v0.13
|
|
2005 - changed to scale preview with the window;
|
|
João S. O. Bueno Calligaris
|
|
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include <libgimp/gimp.h>
|
|
#include <libgimp/gimpui.h>
|
|
|
|
#include "libgimp/stdplugins-intl.h"
|
|
|
|
|
|
#define PLUG_IN_PROC "plug-in-iwarp"
|
|
#define PLUG_IN_BINARY "iwarp"
|
|
#define PLUG_IN_ROLE "gimp-iwarp"
|
|
#define RESPONSE_RESET 1
|
|
|
|
#define MAX_DEFORM_AREA_RADIUS 250
|
|
|
|
#define SCALE_WIDTH 100
|
|
#define MAX_NUM_FRAMES 100
|
|
|
|
typedef enum
|
|
{
|
|
GROW,
|
|
SHRINK,
|
|
MOVE,
|
|
REMOVE,
|
|
SWIRL_CCW,
|
|
SWIRL_CW
|
|
} DeformMode;
|
|
|
|
typedef struct
|
|
{
|
|
gboolean run;
|
|
} iwarp_interface;
|
|
|
|
typedef struct
|
|
{
|
|
gint deform_area_radius;
|
|
gdouble deform_amount;
|
|
DeformMode deform_mode;
|
|
gboolean do_bilinear;
|
|
gboolean do_supersample;
|
|
gdouble supersample_threshold;
|
|
gint max_supersample_depth;
|
|
} iwarp_vals_t;
|
|
|
|
|
|
/* Declare local functions.
|
|
*/
|
|
static void query (void);
|
|
static void run (const gchar *name,
|
|
gint nparams,
|
|
const GimpParam *param,
|
|
gint *nreturn_vals,
|
|
GimpParam **return_vals);
|
|
|
|
static void iwarp (void);
|
|
static void iwarp_frame (void);
|
|
|
|
static gboolean iwarp_dialog (void);
|
|
static void iwarp_animate_dialog (GtkWidget *dialog,
|
|
GtkWidget *notebook);
|
|
|
|
static void iwarp_settings_dialog (GtkWidget *dialog,
|
|
GtkWidget *notebook);
|
|
|
|
static void iwarp_response (GtkWidget *dialog,
|
|
gint response_id,
|
|
gpointer data);
|
|
|
|
static void iwarp_realize_callback (GtkWidget *widget);
|
|
static gboolean iwarp_event_callback (GtkWidget *widget,
|
|
GdkEvent *event,
|
|
GtkWidget *dialog);
|
|
static void iwarp_resize_callback (GtkWidget *widget);
|
|
|
|
static void iwarp_update_preview (gint x0,
|
|
gint y0,
|
|
gint x1,
|
|
gint y1);
|
|
|
|
static void iwarp_get_pixel (gint x,
|
|
gint y,
|
|
guchar *pixel);
|
|
|
|
static void iwarp_get_deform_vector (gdouble x,
|
|
gdouble y,
|
|
gdouble *xv,
|
|
gdouble *yv);
|
|
|
|
static void iwarp_get_point (gdouble x,
|
|
gdouble y,
|
|
guchar *color);
|
|
|
|
static gint iwarp_supersample_test (GimpVector2 *v0,
|
|
GimpVector2 *v1,
|
|
GimpVector2 *v2,
|
|
GimpVector2 *v3);
|
|
|
|
static void iwarp_getsample (GimpVector2 v0,
|
|
GimpVector2 v1,
|
|
GimpVector2 v2,
|
|
GimpVector2 v3,
|
|
gdouble x,
|
|
gdouble y,
|
|
gint *sample,
|
|
gint *cc,
|
|
gint depth,
|
|
gdouble scale);
|
|
|
|
static void iwarp_supersample (gint sxl,
|
|
gint syl,
|
|
gint sxr,
|
|
gint syr,
|
|
guchar *dest_data,
|
|
gint stride,
|
|
gint *progress,
|
|
gint max_progress);
|
|
|
|
static void iwarp_cpy_images (void);
|
|
|
|
static void iwarp_preview_get_pixel (gint x,
|
|
gint y,
|
|
guchar **color);
|
|
|
|
static void iwarp_preview_get_point (gdouble x,
|
|
gdouble y,
|
|
guchar *color);
|
|
|
|
static void iwarp_deform (gint x,
|
|
gint y,
|
|
gdouble vx,
|
|
gdouble vy);
|
|
|
|
static void iwarp_move (gint x,
|
|
gint y,
|
|
gint xx,
|
|
gint yy);
|
|
|
|
static void iwarp_scale_preview (gint new_width,
|
|
gint new_height,
|
|
gint old_width,
|
|
gint old_height);
|
|
static void iwarp_preview_build (GtkWidget *dialog,
|
|
GtkWidget *vbox);
|
|
|
|
static gboolean iwarp_init (void);
|
|
static void iwarp_preview_init (void);
|
|
|
|
|
|
const GimpPlugInInfo PLUG_IN_INFO =
|
|
{
|
|
NULL, /* init_proc */
|
|
NULL, /* quit_proc */
|
|
query, /* query_proc */
|
|
run, /* run_proc */
|
|
};
|
|
|
|
static iwarp_interface wint =
|
|
{
|
|
FALSE
|
|
};
|
|
|
|
static iwarp_vals_t iwarp_vals =
|
|
{
|
|
20,
|
|
0.3,
|
|
MOVE,
|
|
TRUE,
|
|
FALSE,
|
|
2.0,
|
|
2
|
|
};
|
|
|
|
|
|
static GimpDrawable *drawable = NULL;
|
|
static GimpDrawable *destdrawable = NULL;
|
|
static GtkWidget *preview = NULL;
|
|
static guchar *srcimage = NULL;
|
|
static guchar *dstimage = NULL;
|
|
static gint preview_width, preview_height;
|
|
static gint sel_width, sel_height;
|
|
static gint image_bpp;
|
|
static gint lock_alpha;
|
|
static GimpVector2 *deform_vectors = NULL;
|
|
static GimpVector2 *deform_area_vectors = NULL;
|
|
static gint lastx, lasty;
|
|
static gdouble filter[MAX_DEFORM_AREA_RADIUS];
|
|
static gboolean do_animate = FALSE;
|
|
static gboolean do_animate_reverse = FALSE;
|
|
static gboolean do_animate_ping_pong = FALSE;
|
|
static gdouble supersample_threshold_2;
|
|
static gint xl, yl, xh, yh;
|
|
static gint tile_width, tile_height;
|
|
static GimpTile *tile = NULL;
|
|
static gdouble pre2img, img2pre;
|
|
static gint preview_bpp;
|
|
static gdouble animate_deform_value = 1.0;
|
|
static gint32 imageID;
|
|
static gint animate_num_frames = 2;
|
|
static gint frame_number;
|
|
static gboolean layer_alpha;
|
|
static gint max_current_preview_width = 320;
|
|
static gint max_current_preview_height = 320;
|
|
static gint resize_idle = 0;
|
|
static gboolean iwarp_changed = FALSE;
|
|
|
|
|
|
MAIN ()
|
|
|
|
static void
|
|
query (void)
|
|
{
|
|
static const GimpParamDef args[] =
|
|
{
|
|
{ GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0) }" },
|
|
{ GIMP_PDB_IMAGE, "image", "Input image (unused)" },
|
|
{ GIMP_PDB_DRAWABLE, "drawable", "Input drawable" }
|
|
};
|
|
|
|
gimp_install_procedure (PLUG_IN_PROC,
|
|
N_("Use mouse control to warp image areas"),
|
|
"Interactive warping of the specified drawable",
|
|
"Norbert Schmitz",
|
|
"Norbert Schmitz",
|
|
"1997",
|
|
N_("_IWarp..."),
|
|
"RGB*, GRAY*",
|
|
GIMP_PLUGIN,
|
|
G_N_ELEMENTS (args), 0,
|
|
args, NULL);
|
|
|
|
gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Distorts");
|
|
}
|
|
|
|
static void
|
|
run (const gchar *name,
|
|
gint nparams,
|
|
const GimpParam *param,
|
|
gint *nreturn_vals,
|
|
GimpParam **return_vals)
|
|
{
|
|
static GimpParam values[1];
|
|
GimpRunMode run_mode;
|
|
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
|
|
|
|
run_mode = param[0].data.d_int32;
|
|
|
|
INIT_I18N ();
|
|
|
|
/* Get the specified drawable */
|
|
destdrawable = drawable = gimp_drawable_get (param[2].data.d_drawable);
|
|
imageID = param[1].data.d_int32;
|
|
|
|
/* Make sure that the drawable is grayscale or RGB color */
|
|
if (gimp_drawable_is_rgb (drawable->drawable_id) ||
|
|
gimp_drawable_is_gray (drawable->drawable_id))
|
|
{
|
|
switch (run_mode)
|
|
{
|
|
case GIMP_RUN_INTERACTIVE:
|
|
case GIMP_RUN_WITH_LAST_VALS:
|
|
gimp_get_data (PLUG_IN_PROC, &iwarp_vals);
|
|
if (iwarp_dialog ())
|
|
iwarp ();
|
|
gimp_set_data (PLUG_IN_PROC, &iwarp_vals, sizeof (iwarp_vals_t));
|
|
gimp_displays_flush ();
|
|
break;
|
|
|
|
case GIMP_RUN_NONINTERACTIVE:
|
|
status = GIMP_PDB_CALLING_ERROR;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = GIMP_PDB_EXECUTION_ERROR;
|
|
}
|
|
|
|
*nreturn_vals = 1;
|
|
*return_vals = values;
|
|
|
|
values[0].type = GIMP_PDB_STATUS;
|
|
values[0].data.d_status = status;
|
|
|
|
gimp_drawable_detach (drawable);
|
|
|
|
g_free (srcimage);
|
|
g_free (dstimage);
|
|
g_free (deform_vectors);
|
|
g_free (deform_area_vectors);
|
|
}
|
|
|
|
static void
|
|
iwarp_get_pixel (gint x,
|
|
gint y,
|
|
guchar *pixel)
|
|
{
|
|
static gint old_col = -1;
|
|
static gint old_row = -1;
|
|
guchar *data;
|
|
gint col, row;
|
|
gint i;
|
|
|
|
if (x >= xl && x < xh && y >= yl && y < yh)
|
|
{
|
|
col = x / tile_width;
|
|
row = y / tile_height;
|
|
|
|
if (col != old_col || row != old_row)
|
|
{
|
|
if (tile)
|
|
gimp_tile_unref (tile, FALSE);
|
|
|
|
tile = gimp_drawable_get_tile (drawable, FALSE, row, col);
|
|
gimp_tile_ref (tile);
|
|
|
|
old_col = col;
|
|
old_row = row;
|
|
}
|
|
|
|
data = tile->data + (tile->ewidth * (y % tile_height) +
|
|
x % tile_width) * image_bpp;
|
|
|
|
for (i = 0; i < image_bpp; i++)
|
|
*pixel++ = *data++;
|
|
}
|
|
else
|
|
{
|
|
pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
|
|
}
|
|
}
|
|
|
|
static void
|
|
iwarp_get_deform_vector (gdouble x,
|
|
gdouble y,
|
|
gdouble *xv,
|
|
gdouble *yv)
|
|
{
|
|
gint i, xi, yi;
|
|
gdouble dx, dy, my0, my1, mx0, mx1;
|
|
|
|
if (x >= 0 && x < (preview_width - 1) && y >= 0 && y < (preview_height - 1))
|
|
{
|
|
xi = (gint) x;
|
|
yi = (gint) y;
|
|
dx = x-xi;
|
|
dy = y-yi;
|
|
i = (yi * preview_width + xi);
|
|
mx0 =
|
|
deform_vectors[i].x +
|
|
(deform_vectors[i+1].x -
|
|
deform_vectors[i].x) * dx;
|
|
mx1 =
|
|
deform_vectors[i+preview_width].x +
|
|
(deform_vectors[i+preview_width+1].x -
|
|
deform_vectors[i+preview_width].x) * dx;
|
|
my0 =
|
|
deform_vectors[i].y +
|
|
dx * (deform_vectors[i+1].y -
|
|
deform_vectors[i].y);
|
|
my1 =
|
|
deform_vectors[i+preview_width].y +
|
|
dx * (deform_vectors[i+preview_width+1].y -
|
|
deform_vectors[i+preview_width].y);
|
|
*xv = mx0 + dy * (mx1 - mx0);
|
|
*yv = my0 + dy * (my1 - my0);
|
|
}
|
|
else
|
|
{
|
|
*xv = *yv = 0.0;
|
|
}
|
|
}
|
|
|
|
static void
|
|
iwarp_get_point (gdouble x,
|
|
gdouble y,
|
|
guchar *color)
|
|
{
|
|
gdouble dx, dy, m0, m1;
|
|
guchar p0[4], p1[4], p2[4], p3[4];
|
|
gint xi, yi, i;
|
|
|
|
xi = (gint) x;
|
|
yi = (gint) y;
|
|
dx = x - xi;
|
|
dy = y - yi;
|
|
iwarp_get_pixel (xi, yi, p0);
|
|
iwarp_get_pixel (xi + 1, yi, p1);
|
|
iwarp_get_pixel (xi, yi + 1, p2);
|
|
iwarp_get_pixel (xi + 1, yi + 1, p3);
|
|
if (layer_alpha)
|
|
{
|
|
gdouble a0 = p0[image_bpp-1];
|
|
gdouble a1 = p1[image_bpp-1];
|
|
gdouble a2 = p2[image_bpp-1];
|
|
gdouble a3 = p3[image_bpp-1];
|
|
gdouble alpha;
|
|
|
|
m0 = a0 + dx * (a1 - a0);
|
|
m1 = a2 + dx * (a3 - a2);
|
|
alpha = (m0 + dy * (m1 - m0));
|
|
color[image_bpp-1] = alpha;
|
|
if (color[image_bpp-1])
|
|
{
|
|
for (i = 0; i < image_bpp-1; i++)
|
|
{
|
|
m0 = a0*p0[i] + dx * (a1*p1[i] - a0*p0[i]);
|
|
m1 = a2*p2[i] + dx * (a3*p3[i] - a2*p2[i]);
|
|
color[i] = (m0 + dy * (m1 - m0))/alpha;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < image_bpp; i++)
|
|
{
|
|
m0 = p0[i] + dx * (p1[i] - p0[i]);
|
|
m1 = p2[i] + dx * (p3[i] - p2[i]);
|
|
color[i] = m0 + dy * (m1 - m0);
|
|
}
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
iwarp_supersample_test (GimpVector2 *v0,
|
|
GimpVector2 *v1,
|
|
GimpVector2 *v2,
|
|
GimpVector2 *v3)
|
|
{
|
|
gdouble dx, dy;
|
|
|
|
dx = 1.0 + v1->x - v0->x;
|
|
dy = v1->y - v0->y;
|
|
if (SQR(dx) + SQR(dy) > supersample_threshold_2)
|
|
return TRUE;
|
|
|
|
dx = 1.0 + v2->x - v3->x;
|
|
dy = v2->y - v3->y;
|
|
if (SQR(dx) + SQR(dy) > supersample_threshold_2)
|
|
return TRUE;
|
|
|
|
dx = v2->x - v0->x;
|
|
dy = 1.0 + v2->y - v0->y;
|
|
if (SQR(dx) + SQR(dy) > supersample_threshold_2)
|
|
return TRUE;
|
|
|
|
dx = v3->x - v1->x;
|
|
dy = 1.0 + v3->y - v1->y;
|
|
if (SQR(dx) + SQR(dy) > supersample_threshold_2)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
iwarp_getsample (GimpVector2 v0,
|
|
GimpVector2 v1,
|
|
GimpVector2 v2,
|
|
GimpVector2 v3,
|
|
gdouble x,
|
|
gdouble y,
|
|
gint *sample,
|
|
gint *cc,
|
|
gint depth,
|
|
gdouble scale)
|
|
{
|
|
gint i;
|
|
gdouble xv, yv;
|
|
GimpVector2 v01, v13, v23, v02, vm;
|
|
guchar c[4];
|
|
|
|
if ((depth >= iwarp_vals.max_supersample_depth) ||
|
|
(!iwarp_supersample_test (&v0, &v1, &v2, &v3)))
|
|
{
|
|
iwarp_get_deform_vector (img2pre * (x - xl),
|
|
img2pre * (y - yl),
|
|
&xv, &yv);
|
|
xv *= animate_deform_value;
|
|
yv *= animate_deform_value;
|
|
iwarp_get_point (pre2img * xv + x, pre2img * yv + y, c);
|
|
for (i = 0; i < image_bpp; i++)
|
|
sample[i] += c[i];
|
|
(*cc)++;
|
|
}
|
|
else
|
|
{
|
|
scale *= 0.5;
|
|
iwarp_get_deform_vector (img2pre * (x - xl),
|
|
img2pre * (y - yl),
|
|
&xv, &yv);
|
|
xv *= animate_deform_value;
|
|
yv *= animate_deform_value;
|
|
iwarp_get_point (pre2img * xv + x, pre2img * yv + y, c);
|
|
for (i = 0; i < image_bpp; i++)
|
|
sample[i] += c[i];
|
|
(*cc)++;
|
|
vm.x = xv;
|
|
vm.y = yv;
|
|
|
|
iwarp_get_deform_vector (img2pre * (x - xl),
|
|
img2pre * (y - yl - scale),
|
|
&xv, &yv);
|
|
xv *= animate_deform_value;
|
|
yv *= animate_deform_value;
|
|
v01.x = xv;
|
|
v01.y = yv;
|
|
|
|
iwarp_get_deform_vector (img2pre * (x - xl + scale),
|
|
img2pre * (y - yl),
|
|
&xv, &yv);
|
|
xv *= animate_deform_value;
|
|
yv *= animate_deform_value;
|
|
v13.x = xv;
|
|
v13.y = yv;
|
|
|
|
iwarp_get_deform_vector (img2pre * (x - xl),
|
|
img2pre * (y - yl + scale),
|
|
&xv, &yv);
|
|
xv *= animate_deform_value;
|
|
yv *= animate_deform_value;
|
|
v23.x = xv;
|
|
v23.y = yv;
|
|
|
|
iwarp_get_deform_vector (img2pre * (x - xl - scale),
|
|
img2pre * (y - yl),
|
|
&xv, &yv);
|
|
xv *= animate_deform_value;
|
|
yv *= animate_deform_value;
|
|
v02.x = xv;
|
|
v02.y = yv;
|
|
|
|
iwarp_getsample (v0, v01, vm, v02,
|
|
x-scale, y-scale,
|
|
sample, cc, depth + 1,
|
|
scale);
|
|
iwarp_getsample (v01, v1, v13, vm,
|
|
x + scale, y - scale,
|
|
sample, cc, depth + 1,
|
|
scale);
|
|
iwarp_getsample (v02, vm, v23, v2,
|
|
x - scale, y + scale,
|
|
sample, cc, depth + 1,
|
|
scale);
|
|
iwarp_getsample (vm, v13, v3, v23,
|
|
x + scale, y + scale,
|
|
sample, cc, depth + 1,
|
|
scale);
|
|
}
|
|
}
|
|
|
|
static void
|
|
iwarp_supersample (gint sxl,
|
|
gint syl,
|
|
gint sxr,
|
|
gint syr,
|
|
guchar *dest_data,
|
|
gint stride,
|
|
gint *progress,
|
|
gint max_progress)
|
|
{
|
|
gint i, col, row, cc;
|
|
GimpVector2 *srow, *srow_old, *vh;
|
|
gdouble xv, yv;
|
|
gint color[4];
|
|
guchar *dest;
|
|
|
|
srow = g_new (GimpVector2, sxr - sxl + 1);
|
|
srow_old = g_new (GimpVector2, sxr - sxl + 1);
|
|
|
|
for (i = sxl; i < (sxr + 1); i++)
|
|
{
|
|
iwarp_get_deform_vector (img2pre * (-0.5 + i - xl),
|
|
img2pre * (-0.5 + syl - yl),
|
|
&xv, &yv);
|
|
xv *= animate_deform_value;
|
|
yv *= animate_deform_value;
|
|
srow_old[i-sxl].x = xv;
|
|
srow_old[i-sxl].y = yv;
|
|
}
|
|
|
|
for (col = syl; col < syr; col++)
|
|
{
|
|
iwarp_get_deform_vector (img2pre * (-0.5 + sxl - xl),
|
|
img2pre * (0.5 + col - yl),
|
|
&xv, &yv);
|
|
xv *= animate_deform_value;
|
|
yv *= animate_deform_value;
|
|
srow[0].x = xv;
|
|
srow[0].y = yv;
|
|
for (row = sxl; row <sxr; row++)
|
|
{
|
|
iwarp_get_deform_vector (img2pre * (0.5 + row - xl),
|
|
img2pre * (0.5 + col - yl),
|
|
&xv, &yv);
|
|
xv *= animate_deform_value;
|
|
yv *= animate_deform_value;
|
|
srow[row-sxl+1].x = xv;
|
|
srow[row-sxl+1].y = yv;
|
|
cc = 0;
|
|
color[0] = color[1] = color[2] = color[3] = 0;
|
|
iwarp_getsample (srow_old[row-sxl], srow_old[row-sxl+1],
|
|
srow[row-sxl], srow[row-sxl+1],
|
|
row, col, color, &cc, 0, 1.0);
|
|
|
|
dest = dest_data + (col - syl) * stride + (row - sxl) * image_bpp;
|
|
|
|
for (i = 0; i < image_bpp; i++)
|
|
*dest++ = color[i] / cc;
|
|
|
|
(*progress)++;
|
|
}
|
|
|
|
vh = srow_old;
|
|
srow_old = srow;
|
|
srow = vh;
|
|
}
|
|
|
|
gimp_progress_update ((gdouble) (*progress) / max_progress);
|
|
|
|
g_free (srow);
|
|
g_free (srow_old);
|
|
}
|
|
|
|
static void
|
|
iwarp_frame (void)
|
|
{
|
|
GimpPixelRgn dest_rgn;
|
|
gpointer pr;
|
|
guchar *dest_row, *dest;
|
|
gint row, col;
|
|
gint i;
|
|
gint progress, max_progress;
|
|
guchar color[4];
|
|
gdouble xv, yv;
|
|
gboolean padding;
|
|
|
|
progress = 0;
|
|
max_progress = (yh-yl) * (xh-xl);
|
|
|
|
gimp_pixel_rgn_init (&dest_rgn, destdrawable,
|
|
xl, yl, xh-xl, yh-yl, TRUE, TRUE);
|
|
|
|
/* If the source drawable doesn't have an alpha channel but the
|
|
destination drawable has (happens with animations), we need
|
|
to pad the alpha channel of the destination drawable.
|
|
*/
|
|
padding = (!layer_alpha &&
|
|
gimp_drawable_has_alpha (destdrawable->drawable_id));
|
|
|
|
if (!do_animate)
|
|
gimp_progress_init (_("Warping"));
|
|
|
|
for (pr = gimp_pixel_rgns_register (1, &dest_rgn);
|
|
pr != NULL;
|
|
pr = gimp_pixel_rgns_process (pr))
|
|
{
|
|
dest_row = dest_rgn.data;
|
|
if (!iwarp_vals.do_supersample)
|
|
{
|
|
for (row = dest_rgn.y; row < (dest_rgn.y + dest_rgn.h); row++)
|
|
{
|
|
dest = dest_row;
|
|
for (col = dest_rgn.x; col < (dest_rgn.x + dest_rgn.w); col++)
|
|
{
|
|
progress++;
|
|
iwarp_get_deform_vector (img2pre * (col -xl),
|
|
img2pre * (row -yl),
|
|
&xv, &yv);
|
|
xv *= animate_deform_value;
|
|
yv *= animate_deform_value;
|
|
if (fabs(xv) > 0.0 || fabs(yv) > 0.0)
|
|
{
|
|
iwarp_get_point (pre2img * xv + col,
|
|
pre2img * yv + row,
|
|
color);
|
|
|
|
for (i = 0; i < image_bpp; i++)
|
|
*dest++ = color[i];
|
|
}
|
|
else
|
|
{
|
|
iwarp_get_pixel (col, row, color);
|
|
|
|
for (i = 0; i < image_bpp; i++)
|
|
*dest++ = color[i];
|
|
}
|
|
|
|
if (padding)
|
|
*dest++ = 255;
|
|
}
|
|
|
|
dest_row += dest_rgn.rowstride;
|
|
}
|
|
gimp_progress_update ((gdouble) (progress) / max_progress);
|
|
}
|
|
else
|
|
{
|
|
supersample_threshold_2 =
|
|
iwarp_vals.supersample_threshold * iwarp_vals.supersample_threshold;
|
|
iwarp_supersample (dest_rgn.x, dest_rgn.y,
|
|
dest_rgn.x + dest_rgn.w, dest_rgn.y + dest_rgn.h,
|
|
dest_rgn.data,
|
|
dest_rgn.rowstride,
|
|
&progress, max_progress);
|
|
}
|
|
}
|
|
gimp_progress_update (1.0);
|
|
|
|
gimp_drawable_flush (destdrawable);
|
|
gimp_drawable_merge_shadow (destdrawable->drawable_id, TRUE);
|
|
gimp_drawable_update (destdrawable->drawable_id, xl, yl, (xh - xl), (yh - yl));
|
|
}
|
|
|
|
static void
|
|
iwarp (void)
|
|
{
|
|
gint i;
|
|
gint32 layerID;
|
|
gint32 *animlayers;
|
|
gdouble delta;
|
|
|
|
if (image_bpp == 1 || image_bpp == 3)
|
|
layer_alpha = FALSE;
|
|
else
|
|
layer_alpha = TRUE;
|
|
|
|
if (animate_num_frames > 1 && do_animate)
|
|
{
|
|
animlayers = g_new (gint32, animate_num_frames);
|
|
if (do_animate_reverse)
|
|
{
|
|
animate_deform_value = 1.0;
|
|
delta = -1.0 / (animate_num_frames - 1);
|
|
|
|
gimp_image_undo_group_start (imageID);
|
|
}
|
|
else
|
|
{
|
|
animate_deform_value = 0.0;
|
|
delta = 1.0 / (animate_num_frames - 1);
|
|
}
|
|
layerID = gimp_image_get_active_layer (imageID);
|
|
frame_number = 0;
|
|
for (i = 0; i < animate_num_frames; i++)
|
|
{
|
|
gchar *st = g_strdup_printf (_("Frame %d"), i);
|
|
|
|
animlayers[i] = gimp_layer_copy (layerID);
|
|
gimp_layer_add_alpha (animlayers[i]);
|
|
gimp_item_set_name (animlayers[i], st);
|
|
g_free (st);
|
|
|
|
gimp_image_insert_layer (imageID, animlayers[i], -1, 0);
|
|
|
|
destdrawable = gimp_drawable_get (animlayers[i]);
|
|
|
|
gimp_progress_init_printf (_("Warping Frame %d"),
|
|
frame_number);
|
|
|
|
if (animate_deform_value > 0.0)
|
|
iwarp_frame ();
|
|
|
|
animate_deform_value = animate_deform_value + delta;
|
|
frame_number++;
|
|
}
|
|
|
|
if (do_animate_ping_pong)
|
|
{
|
|
gimp_progress_init (_("Ping pong"));
|
|
|
|
for (i = 0; i < animate_num_frames; i++)
|
|
{
|
|
gchar *st;
|
|
|
|
gimp_progress_update ((gdouble) i / (animate_num_frames - 1));
|
|
layerID = gimp_layer_copy (animlayers[animate_num_frames-i-1]);
|
|
|
|
gimp_image_undo_group_end (imageID);
|
|
|
|
gimp_layer_add_alpha (layerID);
|
|
st = g_strdup_printf (_("Frame %d"), i + animate_num_frames);
|
|
gimp_item_set_name (layerID, st);
|
|
g_free (st);
|
|
|
|
gimp_image_insert_layer (imageID, layerID, -1, 0);
|
|
}
|
|
gimp_progress_update (1.0);
|
|
}
|
|
g_free (animlayers);
|
|
}
|
|
else
|
|
{
|
|
animate_deform_value = 1.0;
|
|
iwarp_frame ();
|
|
}
|
|
|
|
if (tile)
|
|
{
|
|
gimp_tile_unref (tile, FALSE);
|
|
tile = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
iwarp_cpy_images (void)
|
|
{
|
|
memcpy (dstimage, srcimage, preview_width * preview_height * preview_bpp);
|
|
}
|
|
|
|
static void
|
|
iwarp_scale_preview (gint new_width,
|
|
gint new_height,
|
|
gint old_width,
|
|
gint old_height)
|
|
{
|
|
gint x, y, z;
|
|
gdouble ox, oy, dx, dy;
|
|
gint src1, src2, ix, iy;
|
|
gdouble in0, in1, in2;
|
|
guchar *new_data;
|
|
|
|
new_data = g_new (guchar, new_width * new_height * preview_bpp);
|
|
|
|
for (y = 0; y < new_height; y++)
|
|
for (x = 0; x < new_width; x++)
|
|
{
|
|
ox = ((gdouble) x / new_width) * old_width;
|
|
oy = ((gdouble) y / new_height) * old_height;
|
|
|
|
ix = (gint) ox;
|
|
iy = (gint) oy;
|
|
|
|
dx = ox - ix;
|
|
dy = oy - iy;
|
|
|
|
if (ix == old_width - 1)
|
|
dx = 0.0;
|
|
|
|
for (z = 0; z < preview_bpp; z++)
|
|
{
|
|
src1 = (iy * old_width + ix) * preview_bpp + z;
|
|
|
|
if (iy != old_height - 1)
|
|
src2 = src1 + old_width * preview_bpp;
|
|
else
|
|
src2 = src1;
|
|
|
|
in0 = dstimage [src1] + (dstimage [src1 + preview_bpp] -
|
|
dstimage [src1]) * dx;
|
|
in1 = dstimage [src2] + (dstimage [src2 + preview_bpp] -
|
|
dstimage [src2]) * dx;
|
|
in2 = in0 + (in1 - in0) * dy;
|
|
|
|
new_data[(y * new_width + x) * preview_bpp + z] = (guchar) in2;
|
|
}
|
|
}
|
|
|
|
g_free (dstimage);
|
|
dstimage = new_data;
|
|
}
|
|
|
|
static void
|
|
iwarp_preview_init (void)
|
|
{
|
|
gint y, x, xi, i;
|
|
GimpPixelRgn srcrgn;
|
|
guchar *pts;
|
|
guchar *linebuffer = NULL;
|
|
gdouble dx, dy;
|
|
|
|
dx = (gdouble) sel_width / max_current_preview_width;
|
|
dy = (gdouble) sel_height / max_current_preview_height;
|
|
|
|
if (dx > dy)
|
|
pre2img = dx;
|
|
else
|
|
pre2img = dy;
|
|
|
|
if (dx <= 1.0 && dy <= 1.0)
|
|
pre2img = 1.0;
|
|
|
|
img2pre = 1.0 / pre2img;
|
|
|
|
preview_width = (gint) (sel_width / pre2img);
|
|
preview_height = (gint) (sel_height / pre2img);
|
|
|
|
if (srcimage)
|
|
{
|
|
srcimage = g_renew (guchar,
|
|
srcimage, preview_width * preview_height * image_bpp);
|
|
}
|
|
else
|
|
{
|
|
srcimage = g_new (guchar, preview_width * preview_height * image_bpp);
|
|
dstimage = g_new (guchar, preview_width * preview_height * preview_bpp);
|
|
}
|
|
|
|
linebuffer = g_new (guchar, sel_width * image_bpp);
|
|
|
|
gimp_pixel_rgn_init (&srcrgn, drawable,
|
|
xl, yl, sel_width, sel_height, FALSE, FALSE);
|
|
|
|
for (y = 0; y < preview_height; y++)
|
|
{
|
|
gimp_pixel_rgn_get_row (&srcrgn, linebuffer,
|
|
xl, (gint) (pre2img * y) + yl, sel_width);
|
|
for (x = 0; x < preview_width; x++)
|
|
{
|
|
pts = srcimage + (y * preview_width + x) * image_bpp;
|
|
xi = (gint) (pre2img * x);
|
|
|
|
for (i = 0; i < image_bpp; i++)
|
|
*pts++ = linebuffer[xi * image_bpp + i];
|
|
}
|
|
}
|
|
|
|
g_free (linebuffer);
|
|
}
|
|
|
|
static gboolean
|
|
iwarp_init (void)
|
|
{
|
|
gint i;
|
|
|
|
if (! gimp_drawable_mask_intersect (drawable->drawable_id,
|
|
&xl, &yl, &sel_width, &sel_height))
|
|
{
|
|
g_message (_("Region affected by plug-in is empty"));
|
|
return FALSE;
|
|
}
|
|
|
|
xh = xl + sel_width;
|
|
yh = yl + sel_height;
|
|
|
|
image_bpp = gimp_drawable_bpp (drawable->drawable_id);
|
|
|
|
if (gimp_item_is_layer (drawable->drawable_id))
|
|
lock_alpha = gimp_layer_get_lock_alpha (drawable->drawable_id);
|
|
else
|
|
lock_alpha = FALSE;
|
|
|
|
preview_bpp = image_bpp;
|
|
|
|
tile_width = gimp_tile_width ();
|
|
tile_height = gimp_tile_height ();
|
|
|
|
gimp_tile_cache_ntiles (sel_width / tile_width + 1);
|
|
|
|
iwarp_preview_init ();
|
|
iwarp_cpy_images ();
|
|
|
|
deform_vectors = g_new0 (GimpVector2, preview_width * preview_height);
|
|
deform_area_vectors = g_new (GimpVector2,
|
|
(MAX_DEFORM_AREA_RADIUS * 2 + 1) *
|
|
(MAX_DEFORM_AREA_RADIUS * 2 + 1));
|
|
|
|
for (i = 0; i < MAX_DEFORM_AREA_RADIUS; i++)
|
|
{
|
|
filter[i] =
|
|
pow ((cos (sqrt((gdouble) i / MAX_DEFORM_AREA_RADIUS) * G_PI) + 1) *
|
|
0.5, 0.7); /*0.7*/
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
iwarp_animate_dialog (GtkWidget *dialog,
|
|
GtkWidget *notebook)
|
|
{
|
|
GtkWidget *frame;
|
|
GtkWidget *vbox;
|
|
GtkWidget *table;
|
|
GtkWidget *button;
|
|
GtkObject *scale_data;
|
|
|
|
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
|
|
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
|
|
|
|
frame = gimp_frame_new (NULL);
|
|
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
|
|
gtk_widget_show (frame);
|
|
|
|
button = gtk_check_button_new_with_mnemonic (_("A_nimate"));
|
|
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), do_animate);
|
|
gtk_frame_set_label_widget (GTK_FRAME (frame), button);
|
|
gtk_widget_show (button);
|
|
|
|
g_signal_connect (button, "toggled",
|
|
G_CALLBACK (gimp_toggle_button_update),
|
|
&do_animate);
|
|
|
|
table = gtk_table_new (3, 3, FALSE);
|
|
gtk_table_set_row_spacings (GTK_TABLE (table), 6);
|
|
gtk_table_set_col_spacings (GTK_TABLE (table), 6);
|
|
gtk_container_add (GTK_CONTAINER (frame), table);
|
|
gtk_widget_show (table);
|
|
|
|
g_object_bind_property (button, "active",
|
|
table, "sensitive",
|
|
G_BINDING_SYNC_CREATE);
|
|
|
|
scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
|
|
_("Number of _frames:"), SCALE_WIDTH, 0,
|
|
animate_num_frames,
|
|
2, MAX_NUM_FRAMES, 1, 10, 0,
|
|
TRUE, 0, 0,
|
|
NULL, NULL);
|
|
g_signal_connect (scale_data, "value-changed",
|
|
G_CALLBACK (gimp_int_adjustment_update),
|
|
&animate_num_frames);
|
|
|
|
button = gtk_check_button_new_with_mnemonic (_("R_everse"));
|
|
gtk_table_attach (GTK_TABLE (table), button, 0, 3, 1, 2,
|
|
GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
|
|
gtk_widget_show (button);
|
|
|
|
g_signal_connect (button, "clicked",
|
|
G_CALLBACK (gimp_toggle_button_update),
|
|
&do_animate_reverse);
|
|
|
|
button = gtk_check_button_new_with_mnemonic (_("_Ping pong"));
|
|
gtk_table_attach (GTK_TABLE (table), button, 0, 3, 2, 3,
|
|
GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
|
|
gtk_widget_show (button);
|
|
|
|
g_signal_connect (button, "clicked",
|
|
G_CALLBACK (gimp_toggle_button_update),
|
|
&do_animate_ping_pong);
|
|
|
|
gtk_widget_show (vbox);
|
|
|
|
gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
|
|
vbox,
|
|
gtk_label_new_with_mnemonic (_("_Animate")));
|
|
}
|
|
|
|
static void
|
|
iwarp_settings_dialog (GtkWidget *dialog,
|
|
GtkWidget *notebook)
|
|
{
|
|
GtkWidget *vbox;
|
|
GtkWidget *vbox2;
|
|
GtkWidget *vbox3;
|
|
GtkWidget *hbox;
|
|
GtkWidget *frame;
|
|
GtkWidget *button;
|
|
GtkWidget *table;
|
|
GtkObject *scale_data;
|
|
GtkWidget *widget[3];
|
|
gint i;
|
|
|
|
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
|
|
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
|
|
|
|
frame = gimp_frame_new (_("Deform Mode"));
|
|
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
|
|
gtk_widget_show (frame);
|
|
|
|
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
|
|
gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
|
|
gtk_container_add (GTK_CONTAINER (frame), hbox);
|
|
gtk_widget_show (hbox);
|
|
|
|
vbox2 = gimp_int_radio_group_new (FALSE, NULL,
|
|
G_CALLBACK (gimp_radio_button_update),
|
|
&iwarp_vals.deform_mode,
|
|
iwarp_vals.deform_mode,
|
|
|
|
_("_Move"), MOVE, NULL,
|
|
_("_Grow"), GROW, NULL,
|
|
_("S_wirl CCW"), SWIRL_CCW, NULL,
|
|
_("Remo_ve"), REMOVE, &widget[0],
|
|
_("S_hrink"), SHRINK, &widget[1],
|
|
_("Sw_irl CW"), SWIRL_CW, &widget[2],
|
|
|
|
NULL);
|
|
|
|
gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 0);
|
|
gtk_widget_show (vbox2);
|
|
|
|
vbox3 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
|
|
gtk_box_pack_start (GTK_BOX (hbox), vbox3, TRUE, TRUE, 0);
|
|
gtk_widget_show (vbox3);
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
g_object_ref (widget[i]);
|
|
gtk_widget_hide (widget[i]);
|
|
gtk_container_remove (GTK_CONTAINER (vbox2), widget[i]);
|
|
gtk_box_pack_start (GTK_BOX (vbox3), widget[i],
|
|
FALSE, FALSE, 0);
|
|
gtk_widget_show (widget[i]);
|
|
g_object_unref (widget[i]);
|
|
}
|
|
|
|
table = gtk_table_new (2, 3, FALSE);
|
|
gtk_table_set_row_spacings (GTK_TABLE (table), 6);
|
|
gtk_table_set_col_spacings (GTK_TABLE (table), 6);
|
|
gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
|
|
gtk_widget_show (table);
|
|
|
|
scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
|
|
_("_Deform radius:"), SCALE_WIDTH, 4,
|
|
iwarp_vals.deform_area_radius,
|
|
5.0, MAX_DEFORM_AREA_RADIUS, 1.0, 10.0, 0,
|
|
TRUE, 0, 0,
|
|
NULL, NULL);
|
|
g_signal_connect (scale_data, "value-changed",
|
|
G_CALLBACK (gimp_int_adjustment_update),
|
|
&iwarp_vals.deform_area_radius);
|
|
|
|
scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
|
|
_("D_eform amount:"), SCALE_WIDTH, 4,
|
|
iwarp_vals.deform_amount,
|
|
0.0, 1.0, 0.01, 0.1, 2,
|
|
TRUE, 0, 0,
|
|
NULL, NULL);
|
|
g_signal_connect (scale_data, "value-changed",
|
|
G_CALLBACK (gimp_double_adjustment_update),
|
|
&iwarp_vals.deform_amount);
|
|
|
|
button = gtk_check_button_new_with_mnemonic (_("_Bilinear"));
|
|
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
|
|
iwarp_vals.do_bilinear);
|
|
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
|
|
gtk_widget_show (button);
|
|
|
|
g_signal_connect (button, "toggled",
|
|
G_CALLBACK (gimp_toggle_button_update),
|
|
&iwarp_vals.do_bilinear);
|
|
|
|
frame = gimp_frame_new (NULL);
|
|
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
|
|
gtk_widget_show (frame);
|
|
|
|
button = gtk_check_button_new_with_mnemonic (_("Adaptive s_upersample"));
|
|
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
|
|
iwarp_vals.do_supersample);
|
|
gtk_frame_set_label_widget (GTK_FRAME (frame), button);
|
|
gtk_widget_show (button);
|
|
|
|
g_signal_connect (button, "toggled",
|
|
G_CALLBACK (gimp_toggle_button_update),
|
|
&iwarp_vals.do_supersample);
|
|
|
|
table = gtk_table_new (2, 3, FALSE);
|
|
gtk_table_set_row_spacings (GTK_TABLE (table), 6);
|
|
gtk_table_set_col_spacings (GTK_TABLE (table), 6);
|
|
gtk_container_add (GTK_CONTAINER (frame), table);
|
|
gtk_widget_show (table);
|
|
|
|
g_object_bind_property (button, "active",
|
|
table, "sensitive",
|
|
G_BINDING_SYNC_CREATE);
|
|
|
|
scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
|
|
_("Ma_x depth:"), SCALE_WIDTH, 5,
|
|
iwarp_vals.max_supersample_depth,
|
|
1.0, 5.0, 1.1, 1.0, 0,
|
|
TRUE, 0, 0,
|
|
NULL, NULL);
|
|
g_signal_connect (scale_data, "value-changed",
|
|
G_CALLBACK (gimp_int_adjustment_update),
|
|
&iwarp_vals.max_supersample_depth);
|
|
|
|
scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
|
|
_("Thresho_ld:"), SCALE_WIDTH, 5,
|
|
iwarp_vals.supersample_threshold,
|
|
1.0, 10.0, 0.01, 0.1, 2,
|
|
TRUE, 0, 0,
|
|
NULL, NULL);
|
|
g_signal_connect (scale_data, "value-changed",
|
|
G_CALLBACK (gimp_double_adjustment_update),
|
|
&iwarp_vals.supersample_threshold);
|
|
|
|
gtk_widget_show (vbox);
|
|
|
|
gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
|
|
vbox,
|
|
gtk_label_new_with_mnemonic (_("_Settings")));
|
|
}
|
|
|
|
static void
|
|
iwarp_preview_build (GtkWidget *dialog,
|
|
GtkWidget *vbox)
|
|
{
|
|
GtkWidget *frame;
|
|
|
|
frame = gtk_aspect_frame_new (NULL, 0.0, 0.0, 1.0, TRUE);
|
|
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
|
|
gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
|
|
gtk_widget_show (frame);
|
|
|
|
preview = gimp_preview_area_new ();
|
|
gtk_widget_set_size_request (preview, preview_width, preview_height);
|
|
gtk_container_add (GTK_CONTAINER (frame), preview);
|
|
gtk_widget_show (preview);
|
|
|
|
gtk_widget_add_events (preview,
|
|
GDK_BUTTON_PRESS_MASK |
|
|
GDK_BUTTON_RELEASE_MASK |
|
|
GDK_BUTTON1_MOTION_MASK |
|
|
GDK_POINTER_MOTION_HINT_MASK);
|
|
|
|
g_signal_connect (preview, "realize",
|
|
G_CALLBACK (iwarp_realize_callback),
|
|
NULL);
|
|
g_signal_connect (preview, "event",
|
|
G_CALLBACK (iwarp_event_callback),
|
|
dialog);
|
|
g_signal_connect (preview, "size-allocate",
|
|
G_CALLBACK (iwarp_resize_callback),
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
iwarp_dialog_response_update (GtkWidget *dialog)
|
|
{
|
|
gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),
|
|
RESPONSE_RESET, iwarp_changed);
|
|
gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),
|
|
GTK_RESPONSE_OK, iwarp_changed);
|
|
}
|
|
|
|
static gboolean
|
|
iwarp_dialog (void)
|
|
{
|
|
GtkWidget *dialog;
|
|
GtkWidget *main_hbox;
|
|
GtkWidget *vbox;
|
|
GtkWidget *hint;
|
|
GtkWidget *notebook;
|
|
|
|
gimp_ui_init (PLUG_IN_BINARY, TRUE);
|
|
|
|
if (! iwarp_init ())
|
|
return FALSE;
|
|
|
|
dialog = gimp_dialog_new (_("IWarp"), PLUG_IN_ROLE,
|
|
NULL, 0,
|
|
gimp_standard_help_func, PLUG_IN_PROC,
|
|
|
|
GIMP_STOCK_RESET, RESPONSE_RESET,
|
|
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
|
GTK_STOCK_OK, GTK_RESPONSE_OK,
|
|
|
|
NULL);
|
|
|
|
gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
|
|
RESPONSE_RESET,
|
|
GTK_RESPONSE_OK,
|
|
GTK_RESPONSE_CANCEL,
|
|
-1);
|
|
|
|
gimp_window_set_transient (GTK_WINDOW (dialog));
|
|
|
|
iwarp_dialog_response_update (dialog);
|
|
|
|
g_signal_connect (dialog, "response",
|
|
G_CALLBACK (iwarp_response),
|
|
NULL);
|
|
g_signal_connect (dialog, "destroy",
|
|
G_CALLBACK (gtk_main_quit),
|
|
NULL);
|
|
|
|
main_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
|
|
gtk_container_set_border_width (GTK_CONTAINER (main_hbox), 12);
|
|
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
|
|
main_hbox, TRUE, TRUE, 0);
|
|
gtk_widget_show (main_hbox);
|
|
|
|
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
|
|
gtk_box_pack_start (GTK_BOX (main_hbox), vbox, TRUE, TRUE, 0);
|
|
gtk_widget_show (vbox);
|
|
|
|
iwarp_preview_build (dialog, vbox);
|
|
hint = gimp_hint_box_new (_("Click and drag in the preview to define "
|
|
"the distortions to apply to the image."));
|
|
gtk_box_pack_end (GTK_BOX (vbox), hint, FALSE, FALSE, 0);
|
|
gtk_widget_show (hint);
|
|
|
|
notebook = gtk_notebook_new ();
|
|
gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
|
|
gtk_box_pack_start (GTK_BOX (main_hbox), notebook, FALSE, FALSE, 0);
|
|
gtk_widget_show (notebook);
|
|
|
|
iwarp_settings_dialog (dialog, notebook);
|
|
iwarp_animate_dialog (dialog, notebook);
|
|
|
|
gtk_widget_show (dialog);
|
|
|
|
iwarp_update_preview (0, 0, preview_width, preview_height);
|
|
|
|
gtk_main ();
|
|
|
|
return wint.run;
|
|
}
|
|
|
|
static void
|
|
iwarp_update_preview (gint x0,
|
|
gint y0,
|
|
gint x1,
|
|
gint y1)
|
|
{
|
|
x0 = CLAMP (x0, 0, preview_width);
|
|
y0 = CLAMP (y0, 0, preview_height);
|
|
x1 = CLAMP (x1, x0, preview_width);
|
|
y1 = CLAMP (y1, y0, preview_height);
|
|
|
|
if (x1 > x0 && y1 > y0)
|
|
gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview),
|
|
x0, y0,
|
|
x1 - x0, y1 - y0,
|
|
gimp_drawable_type(drawable->drawable_id),
|
|
dstimage + (y0 * preview_width + x0) * preview_bpp,
|
|
preview_width * preview_bpp);
|
|
}
|
|
|
|
static void
|
|
iwarp_preview_get_pixel (gint x,
|
|
gint y,
|
|
guchar **color)
|
|
{
|
|
static guchar black[4] = { 0, 0, 0, 0 };
|
|
|
|
if (x < 0 || x >= preview_width || y<0 || y >= preview_height)
|
|
{
|
|
*color = black;
|
|
return;
|
|
}
|
|
|
|
*color = srcimage + (y * preview_width + x) * image_bpp;
|
|
}
|
|
|
|
static void
|
|
iwarp_preview_get_point (gdouble x,
|
|
gdouble y,
|
|
guchar *color)
|
|
{
|
|
gint xi, yi, j;
|
|
gdouble dx, dy, m0, m1;
|
|
guchar *p0, *p1, *p2, *p3;
|
|
|
|
xi = (gint) x;
|
|
yi = (gint) y;
|
|
|
|
if (iwarp_vals.do_bilinear)
|
|
{
|
|
dx = x-xi;
|
|
dy = y-yi;
|
|
|
|
iwarp_preview_get_pixel (xi, yi, &p0);
|
|
iwarp_preview_get_pixel (xi + 1, yi, &p1);
|
|
iwarp_preview_get_pixel (xi, yi + 1, &p2);
|
|
iwarp_preview_get_pixel (xi + 1, yi + 1, &p3);
|
|
|
|
for (j = 0; j < image_bpp; j++)
|
|
{
|
|
m0 = p0[j] + dx * (p1[j] - p0[j]);
|
|
m1 = p2[j] + dx * (p3[j] - p2[j]);
|
|
color[j] = (guchar) (m0 + dy * (m1 - m0));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
iwarp_preview_get_pixel (xi, yi, &p0);
|
|
for (j = 0; j < image_bpp; j++)
|
|
color[j] = p0[j];
|
|
}
|
|
}
|
|
|
|
static void
|
|
iwarp_deform (gint x,
|
|
int y,
|
|
gdouble vx,
|
|
gdouble vy)
|
|
{
|
|
gint xi, yi, ptr, fptr, x0, x1, y0, y1, radius2, length2;
|
|
gdouble deform_value, xn, yn, nvx=0, nvy=0, emh, em, edge_width, xv, yv;
|
|
guchar color[4], alpha = 255;
|
|
|
|
x0 = (x < iwarp_vals.deform_area_radius) ?
|
|
-x : -iwarp_vals.deform_area_radius;
|
|
|
|
x1 = (x + iwarp_vals.deform_area_radius >= preview_width) ?
|
|
preview_width - x - 1 : iwarp_vals.deform_area_radius;
|
|
|
|
y0 = (y < iwarp_vals.deform_area_radius) ?
|
|
-y : -iwarp_vals.deform_area_radius;
|
|
|
|
y1 = (y + iwarp_vals.deform_area_radius >= preview_height) ?
|
|
preview_height-y-1 : iwarp_vals.deform_area_radius;
|
|
|
|
radius2 = SQR (iwarp_vals.deform_area_radius);
|
|
|
|
for (yi = y0; yi <= y1; yi++)
|
|
for (xi = x0; xi <= x1; xi++)
|
|
{
|
|
length2 = (xi * xi + yi * yi) * MAX_DEFORM_AREA_RADIUS / radius2;
|
|
if (length2 < MAX_DEFORM_AREA_RADIUS)
|
|
{
|
|
ptr = (y + yi) * preview_width + x + xi;
|
|
fptr =
|
|
(yi + iwarp_vals.deform_area_radius) *
|
|
(iwarp_vals.deform_area_radius * 2 + 1) +
|
|
xi +
|
|
iwarp_vals.deform_area_radius;
|
|
|
|
switch (iwarp_vals.deform_mode)
|
|
{
|
|
case GROW:
|
|
deform_value = filter[length2] * 0.1 * iwarp_vals.deform_amount;
|
|
nvx = -deform_value * xi;
|
|
nvy = -deform_value * yi;
|
|
break;
|
|
|
|
case SHRINK:
|
|
deform_value = filter[length2] * 0.1 * iwarp_vals.deform_amount;
|
|
nvx = deform_value * xi;
|
|
nvy = deform_value * yi;
|
|
break;
|
|
|
|
case SWIRL_CW:
|
|
deform_value = filter[length2] * iwarp_vals.deform_amount * 0.5;
|
|
nvx = deform_value * yi;
|
|
nvy = -deform_value * xi;
|
|
break;
|
|
|
|
case SWIRL_CCW:
|
|
deform_value = filter[length2] *iwarp_vals.deform_amount * 0.5;
|
|
nvx = -deform_value * yi;
|
|
nvy = deform_value * xi;
|
|
break;
|
|
|
|
case MOVE:
|
|
deform_value = filter[length2] * iwarp_vals.deform_amount;
|
|
nvx = deform_value * vx;
|
|
nvy = deform_value * vy;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (iwarp_vals.deform_mode == REMOVE)
|
|
{
|
|
deform_value =
|
|
1.0 - 0.5 * iwarp_vals.deform_amount * filter[length2];
|
|
deform_area_vectors[fptr].x =
|
|
deform_value * deform_vectors[ptr].x ;
|
|
deform_area_vectors[fptr].y =
|
|
deform_value * deform_vectors[ptr].y ;
|
|
}
|
|
else
|
|
{
|
|
edge_width = 0.2 * iwarp_vals.deform_area_radius;
|
|
emh = em = 1.0;
|
|
if (x+xi < edge_width)
|
|
em = (gdouble) (x + xi) / edge_width;
|
|
if (y + yi < edge_width)
|
|
emh = (gdouble) (y + yi) / edge_width;
|
|
if (emh <em)
|
|
em = emh;
|
|
if (preview_width - x - xi - 1 < edge_width)
|
|
emh = (gdouble) (preview_width - x - xi - 1) / edge_width;
|
|
if (emh < em)
|
|
em = emh;
|
|
if (preview_height-y-yi-1 < edge_width)
|
|
emh = (gdouble) (preview_height - y - yi - 1) / edge_width;
|
|
if (emh <em)
|
|
em = emh;
|
|
|
|
nvx = nvx * em;
|
|
nvy = nvy * em;
|
|
|
|
iwarp_get_deform_vector (nvx + x+ xi, nvy + y + yi, &xv, &yv);
|
|
xv += nvx;
|
|
if (xv +x+xi <0.0)
|
|
xv = -x - xi;
|
|
else if (xv + x +xi > (preview_width-1))
|
|
xv = preview_width - x -xi-1;
|
|
yv += nvy;
|
|
if (yv + y + yi < 0.0)
|
|
yv = -y - yi;
|
|
else if (yv + y + yi > (preview_height-1))
|
|
yv = preview_height - y -yi - 1;
|
|
deform_area_vectors[fptr].x = xv;
|
|
deform_area_vectors[fptr].y = yv;
|
|
}
|
|
|
|
xn = deform_area_vectors[fptr].x + x + xi;
|
|
yn = deform_area_vectors[fptr].y + y + yi;
|
|
|
|
/* Yeah, it is ugly but since color is a pointer into image-data
|
|
* I must not change color[image_bpp - 1] ...
|
|
*/
|
|
if (lock_alpha && (image_bpp == 4 || image_bpp == 2))
|
|
{
|
|
iwarp_preview_get_point (x + xi, y + yi, color);
|
|
alpha = color[image_bpp - 1];
|
|
}
|
|
|
|
iwarp_preview_get_point (xn, yn, color);
|
|
|
|
if (!lock_alpha && (image_bpp == 4 || image_bpp == 2))
|
|
{
|
|
alpha = color[image_bpp - 1];
|
|
}
|
|
|
|
switch (preview_bpp)
|
|
{
|
|
case 4:
|
|
dstimage[ptr*4 + 0] = color[0];
|
|
dstimage[ptr*4 + 1] = color[1];
|
|
dstimage[ptr*4 + 2] = color[2];
|
|
dstimage[ptr*4 + 3] = alpha;
|
|
break;
|
|
|
|
case 3:
|
|
dstimage[ptr*3 + 0] = color[0];
|
|
dstimage[ptr*3 + 1] = color[1];
|
|
dstimage[ptr*3 + 2] = color[2];
|
|
break;
|
|
|
|
case 2:
|
|
dstimage[ptr*2 + 0] = color[0];
|
|
dstimage[ptr*2 + 1] = alpha;
|
|
break;
|
|
|
|
case 1:
|
|
dstimage[ptr] = color[0];
|
|
}
|
|
}
|
|
}
|
|
|
|
for (yi = y0; yi <= y1; yi++)
|
|
for (xi = x0; xi <= x1; xi++)
|
|
{
|
|
length2 = (xi*xi+yi*yi) * MAX_DEFORM_AREA_RADIUS / radius2;
|
|
if (length2 < MAX_DEFORM_AREA_RADIUS)
|
|
{
|
|
ptr = (yi +y) * preview_width + xi +x;
|
|
fptr =
|
|
(yi+iwarp_vals.deform_area_radius) *
|
|
(iwarp_vals.deform_area_radius*2+1) +
|
|
xi +
|
|
iwarp_vals.deform_area_radius;
|
|
deform_vectors[ptr] = deform_area_vectors[fptr];
|
|
}
|
|
}
|
|
|
|
iwarp_changed = TRUE;
|
|
|
|
iwarp_update_preview (x + x0, y + y0, x + x1 + 1, y + y1 + 1);
|
|
}
|
|
|
|
static void
|
|
iwarp_move (gint x,
|
|
gint y,
|
|
gint xx,
|
|
gint yy)
|
|
{
|
|
gdouble l, dx, dy, xf, yf;
|
|
gint num, i, x0, y0;
|
|
|
|
dx = x - xx;
|
|
dy = y - yy;
|
|
l= sqrt (dx * dx + dy * dy);
|
|
num = (gint) (l * 2 / iwarp_vals.deform_area_radius) + 1;
|
|
dx /= num;
|
|
dy /= num;
|
|
xf = xx + dx; yf = yy + dy;
|
|
|
|
for (i=0; i< num; i++)
|
|
{
|
|
x0 = (gint) xf;
|
|
y0 = (gint) yf;
|
|
|
|
iwarp_deform (x0, y0, -dx, -dy);
|
|
|
|
xf += dx;
|
|
yf += dy;
|
|
}
|
|
}
|
|
|
|
static void
|
|
iwarp_response (GtkWidget *dialog,
|
|
gint response_id,
|
|
gpointer data)
|
|
{
|
|
switch (response_id)
|
|
{
|
|
case RESPONSE_RESET:
|
|
{
|
|
gint i;
|
|
|
|
iwarp_cpy_images ();
|
|
|
|
for (i = 0; i < preview_width * preview_height; i++)
|
|
deform_vectors[i].x = deform_vectors[i].y = 0.0;
|
|
|
|
iwarp_changed = FALSE;
|
|
|
|
iwarp_update_preview (0, 0, preview_width, preview_height);
|
|
|
|
iwarp_dialog_response_update (dialog);
|
|
}
|
|
break;
|
|
|
|
case GTK_RESPONSE_OK:
|
|
wint.run = TRUE;
|
|
|
|
default:
|
|
gtk_widget_destroy (dialog);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
iwarp_realize_callback (GtkWidget *widget)
|
|
{
|
|
GdkDisplay *display = gtk_widget_get_display (widget);
|
|
GdkCursor *cursor = gdk_cursor_new_for_display (display, GDK_CROSSHAIR);
|
|
|
|
gdk_window_set_cursor (gtk_widget_get_window (widget), cursor);
|
|
gdk_cursor_unref (cursor);
|
|
}
|
|
|
|
static gboolean
|
|
iwarp_event_callback (GtkWidget *widget,
|
|
GdkEvent *event,
|
|
GtkWidget *dialog)
|
|
{
|
|
switch (event->type)
|
|
{
|
|
case GDK_BUTTON_PRESS:
|
|
{
|
|
GdkEventButton *bevent = (GdkEventButton *) event;
|
|
|
|
lastx = bevent->x;
|
|
lasty = bevent->y;
|
|
}
|
|
break;
|
|
|
|
case GDK_BUTTON_RELEASE:
|
|
{
|
|
GdkEventButton *bevent = (GdkEventButton *) event;
|
|
|
|
if (bevent->state & GDK_BUTTON1_MASK)
|
|
{
|
|
if (iwarp_vals.deform_mode == MOVE)
|
|
iwarp_move (bevent->x, bevent->y, lastx, lasty);
|
|
else
|
|
iwarp_deform (bevent->x, bevent->y, 0.0, 0.0);
|
|
|
|
iwarp_dialog_response_update (dialog);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case GDK_MOTION_NOTIFY:
|
|
{
|
|
GdkEventMotion *mevent = (GdkEventMotion *) event;
|
|
|
|
if (mevent->state & GDK_BUTTON1_MASK)
|
|
{
|
|
if (iwarp_vals.deform_mode == MOVE)
|
|
iwarp_move (mevent->x, mevent->y, lastx, lasty);
|
|
else
|
|
iwarp_deform (mevent->x, mevent->y, 0.0, 0.0);
|
|
|
|
lastx = mevent->x;
|
|
lasty = mevent->y;
|
|
}
|
|
|
|
gdk_event_request_motions (mevent);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
iwarp_resize_idle (GtkWidget *widget)
|
|
{
|
|
GtkAllocation allocation;
|
|
GimpVector2 *new_deform_vectors;
|
|
gint old_preview_width, old_preview_height;
|
|
gint new_preview_width, new_preview_height;
|
|
gint x, y;
|
|
gdouble new2old;
|
|
|
|
resize_idle = 0;
|
|
|
|
gtk_widget_get_allocation (widget, &allocation);
|
|
|
|
old_preview_width = preview_width;
|
|
old_preview_height = preview_height;
|
|
|
|
max_current_preview_width = allocation.width;
|
|
max_current_preview_height = allocation.height;
|
|
|
|
/* preview width and height get updated here: */
|
|
iwarp_preview_init ();
|
|
new_preview_width = preview_width;
|
|
new_preview_height = preview_height;
|
|
|
|
new_deform_vectors = g_new0 (GimpVector2, preview_width * preview_height);
|
|
new2old = (gdouble) old_preview_width / preview_width;
|
|
|
|
/* preview_width and height are used as global variables inside
|
|
* iwarp_get_deform_factor(). In the following call to the function,
|
|
* I need it to run with the old values. Adding a width and height
|
|
* to these function parameters would be an option for cleaner code,
|
|
* but that would also mean pushing 16 extra parameter bytes several
|
|
* times over for each pixel processed.
|
|
*/
|
|
|
|
preview_width = old_preview_width;
|
|
preview_height = old_preview_height;
|
|
|
|
for (y = 0; y < new_preview_height; y++)
|
|
for (x = 0; x < new_preview_width; x++)
|
|
iwarp_get_deform_vector (new2old * x,
|
|
new2old * y,
|
|
&new_deform_vectors[x + new_preview_width * y].x,
|
|
&new_deform_vectors[x + new_preview_width * y].y);
|
|
|
|
preview_width = new_preview_width;
|
|
preview_height = new_preview_height;
|
|
|
|
g_free (deform_vectors);
|
|
deform_vectors = new_deform_vectors;
|
|
|
|
iwarp_scale_preview (new_preview_width, new_preview_height,
|
|
old_preview_width, old_preview_height);
|
|
|
|
iwarp_update_preview (0, 0, preview_width, preview_height);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
iwarp_resize_callback (GtkWidget *widget)
|
|
{
|
|
if (resize_idle)
|
|
g_source_remove (resize_idle);
|
|
|
|
resize_idle = g_idle_add_full (G_PRIORITY_LOW,
|
|
(GSourceFunc) iwarp_resize_idle, widget,
|
|
NULL);
|
|
}
|