mirror of https://github.com/GNOME/gimp.git
1435 lines
39 KiB
C
1435 lines
39 KiB
C
/* The GIMP -- an 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 2 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, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include "appenv.h"
|
|
#include "actionarea.h"
|
|
#include "drawable.h"
|
|
#include "errors.h"
|
|
#include "floating_sel.h"
|
|
#include "general.h"
|
|
#include "gdisplay.h"
|
|
#include "gimage_mask.h"
|
|
#include "gimprc.h"
|
|
#include "info_dialog.h"
|
|
#include "interface.h"
|
|
#include "layers_dialog.h"
|
|
#include "palette.h"
|
|
#include "transform_core.h"
|
|
#include "transform_tool.h"
|
|
#include "temp_buf.h"
|
|
#include "tools.h"
|
|
#include "undo.h"
|
|
#include "layer_pvt.h"
|
|
#include "drawable_pvt.h"
|
|
#include "tile_manager_pvt.h"
|
|
#include "tile.h" /* ick. */
|
|
|
|
#include "libgimp/gimpintl.h"
|
|
|
|
#define SQR(x) ((x) * (x))
|
|
|
|
#ifndef M_PI
|
|
#define M_PI 3.14159265358979323846
|
|
#endif /* M_PI */
|
|
|
|
/* variables */
|
|
static TranInfo old_trans_info;
|
|
InfoDialog * transform_info = NULL;
|
|
|
|
/* forward function declarations */
|
|
static int transform_core_bounds (Tool *, void *);
|
|
static void * transform_core_recalc (Tool *, void *);
|
|
static void transform_core_doit (Tool *, gpointer);
|
|
static double cubic (double, int, int, int, int);
|
|
static void transform_core_setup_grid (Tool *);
|
|
|
|
#define BILINEAR(jk,j1k,jk1,j1k1,dx,dy) \
|
|
((1-dy) * ((1-dx)*jk + dx*j1k) + \
|
|
dy * ((1-dx)*jk1 + dx*j1k1))
|
|
|
|
#define REF_TILE(i,x,y) \
|
|
tile[i] = tile_manager_get_tile (float_tiles, x, y, TRUE, FALSE); \
|
|
src[i] = tile_data_pointer (tile[i], (x) % TILE_WIDTH, (y) % TILE_HEIGHT);
|
|
|
|
|
|
static void
|
|
transform_ok_callback (GtkWidget *w,
|
|
gpointer client_data)
|
|
{
|
|
Tool *tool;
|
|
|
|
tool = (Tool *) client_data;
|
|
transform_core_doit (tool, tool->gdisp_ptr);
|
|
}
|
|
|
|
static void
|
|
transform_reset_callback (GtkWidget *w,
|
|
gpointer client_data)
|
|
{
|
|
Tool *tool;
|
|
TransformCore *transform_core;
|
|
int i;
|
|
|
|
tool = (Tool *) client_data;
|
|
transform_core = (TransformCore *) tool->private;
|
|
|
|
/* stop the current tool drawing process */
|
|
draw_core_pause (transform_core->core, tool);
|
|
|
|
/* Restore the previous transformation info */
|
|
for (i = 0; i < TRAN_INFO_SIZE; i++)
|
|
transform_core->trans_info [i] = old_trans_info [i];
|
|
/* recalculate the tool's transformation matrix */
|
|
transform_core_recalc (tool, tool->gdisp_ptr);
|
|
|
|
/* resume drawing the current tool */
|
|
draw_core_resume (transform_core->core, tool);
|
|
}
|
|
|
|
|
|
static ActionAreaItem action_items[2] =
|
|
{
|
|
{ NULL, transform_ok_callback, NULL, NULL },
|
|
{ N_("Reset"), transform_reset_callback, NULL, NULL },
|
|
};
|
|
|
|
static char *action_labels[4] =
|
|
{
|
|
N_("Rotate"),
|
|
N_("Scale"),
|
|
N_("Shear"),
|
|
N_("Transform")
|
|
};
|
|
|
|
void
|
|
transform_core_button_press (tool, bevent, gdisp_ptr)
|
|
Tool *tool;
|
|
GdkEventButton *bevent;
|
|
gpointer gdisp_ptr;
|
|
{
|
|
TransformCore * transform_core;
|
|
GDisplay * gdisp;
|
|
Layer * layer;
|
|
int dist;
|
|
int closest_dist;
|
|
int x, y;
|
|
int i;
|
|
int off_x, off_y;
|
|
|
|
gdisp = (GDisplay *) gdisp_ptr;
|
|
transform_core = (TransformCore *) tool->private;
|
|
|
|
transform_core->bpressed = TRUE; /* ALT */
|
|
|
|
tool->drawable = gimage_active_drawable (gdisp->gimage);
|
|
|
|
if (transform_core->function == CREATING && tool->state == ACTIVE)
|
|
{
|
|
/* Save the current transformation info */
|
|
for (i = 0; i < TRAN_INFO_SIZE; i++)
|
|
old_trans_info [i] = transform_core->trans_info [i];
|
|
}
|
|
|
|
|
|
/* if we have already displayed the bounding box and handles,
|
|
* check to make sure that the display which currently owns the
|
|
* tool is the one which just received the button pressed event
|
|
*/
|
|
if ((transform_core->function >= CREATING) && (gdisp_ptr == tool->gdisp_ptr) &&
|
|
transform_core->interactive)
|
|
{
|
|
/* start drawing the bounding box and handles... */
|
|
draw_core_start (transform_core->core, gdisp->canvas->window, tool);
|
|
|
|
x = bevent->x;
|
|
y = bevent->y;
|
|
|
|
closest_dist = SQR (x - transform_core->sx1) + SQR (y - transform_core->sy1);
|
|
transform_core->function = HANDLE_1;
|
|
|
|
dist = SQR (x - transform_core->sx2) + SQR (y - transform_core->sy2);
|
|
if (dist < closest_dist)
|
|
{
|
|
closest_dist = dist;
|
|
transform_core->function = HANDLE_2;
|
|
}
|
|
|
|
dist = SQR (x - transform_core->sx3) + SQR (y - transform_core->sy3);
|
|
if (dist < closest_dist)
|
|
{
|
|
closest_dist = dist;
|
|
transform_core->function = HANDLE_3;
|
|
}
|
|
|
|
dist = SQR (x - transform_core->sx4) + SQR (y - transform_core->sy4);
|
|
if (dist < closest_dist)
|
|
{
|
|
closest_dist = dist;
|
|
transform_core->function = HANDLE_4;
|
|
}
|
|
|
|
if (tool->type == ROTATE
|
|
&& (SQR (x - transform_core->scx) +
|
|
SQR (y - transform_core->scy)) <= 100)
|
|
{
|
|
transform_core->function = HANDLE_CENTER;
|
|
}
|
|
|
|
/* Save the current pointer position */
|
|
gdisplay_untransform_coords (gdisp, bevent->x, bevent->y,
|
|
&transform_core->startx,
|
|
&transform_core->starty, TRUE, 0);
|
|
transform_core->lastx = transform_core->startx;
|
|
transform_core->lasty = transform_core->starty;
|
|
|
|
gdk_pointer_grab (gdisp->canvas->window, FALSE,
|
|
GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
|
|
NULL, NULL, bevent->time);
|
|
|
|
tool->state = ACTIVE;
|
|
return;
|
|
}
|
|
|
|
/* if the cursor is clicked inside the current selection, show the
|
|
* bounding box and handles...
|
|
*/
|
|
gdisplay_untransform_coords (gdisp, bevent->x, bevent->y, &x, &y, FALSE, FALSE);
|
|
if ((layer = gimage_get_active_layer (gdisp->gimage)))
|
|
{
|
|
drawable_offsets (GIMP_DRAWABLE(layer), &off_x, &off_y);
|
|
if (x >= off_x && y >= off_y &&
|
|
x < (off_x + drawable_width (GIMP_DRAWABLE(layer))) &&
|
|
y < (off_y + drawable_height (GIMP_DRAWABLE(layer))))
|
|
if (gimage_mask_is_empty (gdisp->gimage) ||
|
|
gimage_mask_value (gdisp->gimage, x, y))
|
|
{
|
|
if (layer->mask != NULL && GIMP_DRAWABLE(layer->mask))
|
|
{
|
|
g_message (_("Transformations do not work on\nlayers that contain layer masks."));
|
|
tool->state = INACTIVE;
|
|
return;
|
|
}
|
|
|
|
/* If the tool is already active, clear the current state and reset */
|
|
if (tool->state == ACTIVE)
|
|
transform_core_reset (tool, gdisp_ptr);
|
|
|
|
/* Set the pointer to the gdisplay that owns this tool */
|
|
tool->gdisp_ptr = gdisp_ptr;
|
|
tool->state = ACTIVE;
|
|
|
|
/* Grab the pointer if we're in non-interactive mode */
|
|
if (!transform_core->interactive)
|
|
gdk_pointer_grab (gdisp->canvas->window, FALSE,
|
|
(GDK_POINTER_MOTION_HINT_MASK |
|
|
GDK_BUTTON1_MOTION_MASK |
|
|
GDK_BUTTON_RELEASE_MASK),
|
|
NULL, NULL, bevent->time);
|
|
|
|
/* Find the transform bounds for some tools (like scale, perspective)
|
|
* that actually need the bounds for initializing
|
|
*/
|
|
transform_core_bounds (tool, gdisp_ptr);
|
|
|
|
/* Calculate the grid line endpoints */
|
|
if (transform_tool_show_grid ())
|
|
transform_core_setup_grid (tool);
|
|
|
|
/* Initialize the transform tool */
|
|
(* transform_core->trans_func) (tool, gdisp_ptr, INIT);
|
|
|
|
if (transform_info != NULL)
|
|
{
|
|
action_items[0].label = action_labels[tool->type - ROTATE];
|
|
action_items[0].user_data = tool;
|
|
action_items[1].user_data = tool;
|
|
build_action_area (GTK_DIALOG (transform_info->shell),
|
|
action_items, 2, 0);
|
|
}
|
|
|
|
/* Recalculate the transform tool */
|
|
transform_core_recalc (tool, gdisp_ptr);
|
|
|
|
/* recall this function to find which handle we're dragging */
|
|
if (transform_core->interactive)
|
|
transform_core_button_press (tool, bevent, gdisp_ptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
transform_core_button_release (tool, bevent, gdisp_ptr)
|
|
Tool *tool;
|
|
GdkEventButton *bevent;
|
|
gpointer gdisp_ptr;
|
|
{
|
|
GDisplay *gdisp;
|
|
TransformCore *transform_core;
|
|
int i;
|
|
|
|
gdisp = (GDisplay *) gdisp_ptr;
|
|
transform_core = (TransformCore *) tool->private;
|
|
|
|
transform_core->bpressed = FALSE; /* ALT */
|
|
|
|
/* if we are creating, there is nothing to be done...exit */
|
|
if (transform_core->function == CREATING && transform_core->interactive)
|
|
return;
|
|
|
|
/* release of the pointer grab */
|
|
gdk_pointer_ungrab (bevent->time);
|
|
gdk_flush ();
|
|
|
|
/* if the 3rd button isn't pressed, transform the selected mask */
|
|
if (! (bevent->state & GDK_BUTTON3_MASK))
|
|
{
|
|
/* Shift-clicking is another way to approve the transform */
|
|
if ((bevent->state & GDK_SHIFT_MASK)
|
|
|| (tool->type == FLIP_HORZ)
|
|
|| (tool->type == FLIP_VERT))
|
|
{
|
|
transform_core_doit (tool, gdisp_ptr);
|
|
}
|
|
else
|
|
{
|
|
/* Do nothing, keep the grid visible */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* stop the current tool drawing process */
|
|
draw_core_pause (transform_core->core, tool);
|
|
|
|
/* Restore the previous transformation info */
|
|
for (i = 0; i < TRAN_INFO_SIZE; i++)
|
|
transform_core->trans_info [i] = old_trans_info [i];
|
|
/* recalculate the tool's transformation matrix */
|
|
transform_core_recalc (tool, gdisp_ptr);
|
|
|
|
/* resume drawing the current tool */
|
|
draw_core_resume (transform_core->core, tool);
|
|
}
|
|
|
|
/* if this tool is non-interactive, make it inactive after use */
|
|
if (!transform_core->interactive)
|
|
tool->state = INACTIVE;
|
|
}
|
|
|
|
void
|
|
transform_core_doit (tool, gdisp_ptr)
|
|
Tool *tool;
|
|
gpointer gdisp_ptr;
|
|
{
|
|
GDisplay *gdisp;
|
|
TransformCore *transform_core;
|
|
TileManager *new_tiles;
|
|
TransformUndo *tu;
|
|
int new_layer;
|
|
int i, x, y;
|
|
|
|
gdisp = (GDisplay *) gdisp_ptr;
|
|
transform_core = (TransformCore *) tool->private;
|
|
|
|
/* We're going to dirty this image, but we want to keep the tool
|
|
around
|
|
*/
|
|
|
|
tool->preserve = TRUE;
|
|
|
|
/* Start a transform undo group */
|
|
undo_push_group_start (gdisp->gimage, TRANSFORM_CORE_UNDO);
|
|
|
|
/* With the old UI, if original is NULL, then this is the
|
|
first transformation. In the new UI, it is always so, yes? */
|
|
g_assert (transform_core->original == NULL);
|
|
|
|
/* If we're in interactive mode, we need to copy the current
|
|
* selection to the transform tool's private selection pointer, so
|
|
* that the original source can be repeatedly modified.
|
|
*/
|
|
transform_core->original = transform_core_cut (gdisp->gimage,
|
|
gimage_active_drawable (gdisp->gimage),
|
|
&new_layer);
|
|
|
|
/* Send the request for the transformation to the tool...
|
|
*/
|
|
new_tiles = (* transform_core->trans_func) (tool, gdisp_ptr, FINISH);
|
|
|
|
(* transform_core->trans_func) (tool, gdisp_ptr, INIT);
|
|
|
|
transform_core_recalc (tool, gdisp_ptr);
|
|
|
|
if (new_tiles)
|
|
{
|
|
/* paste the new transformed image to the gimage...also implement
|
|
* undo...
|
|
*/
|
|
transform_core_paste (gdisp->gimage, gimage_active_drawable (gdisp->gimage),
|
|
new_tiles, new_layer);
|
|
|
|
/* create and initialize the transform_undo structure */
|
|
tu = (TransformUndo *) g_malloc (sizeof (TransformUndo));
|
|
tu->tool_ID = tool->ID;
|
|
tu->tool_type = tool->type;
|
|
for (i = 0; i < TRAN_INFO_SIZE; i++)
|
|
tu->trans_info[i] = old_trans_info[i];
|
|
tu->original = NULL;
|
|
|
|
/* Make a note of the new current drawable (since we may have
|
|
a floating selection, etc now. */
|
|
|
|
tool->drawable = gimage_active_drawable (gdisp->gimage);
|
|
|
|
undo_push_transform (gdisp->gimage, (void *) tu);
|
|
}
|
|
|
|
/* push the undo group end */
|
|
undo_push_group_end (gdisp->gimage);
|
|
|
|
/* We're done dirtying the image, and would like to be restarted
|
|
if the image gets dirty while the tool exists
|
|
*/
|
|
|
|
tool->preserve = FALSE;
|
|
|
|
/* Flush the gdisplays */
|
|
if (gdisp->disp_xoffset || gdisp->disp_yoffset)
|
|
{
|
|
gdk_window_get_size (gdisp->canvas->window, &x, &y);
|
|
if (gdisp->disp_yoffset)
|
|
{
|
|
gdisplay_expose_area (gdisp, 0, 0, gdisp->disp_width,
|
|
gdisp->disp_yoffset);
|
|
gdisplay_expose_area (gdisp, 0, gdisp->disp_yoffset + y,
|
|
gdisp->disp_width, gdisp->disp_height);
|
|
}
|
|
if (gdisp->disp_xoffset)
|
|
{
|
|
gdisplay_expose_area (gdisp, 0, 0, gdisp->disp_xoffset,
|
|
gdisp->disp_height);
|
|
gdisplay_expose_area (gdisp, gdisp->disp_xoffset + x, 0,
|
|
gdisp->disp_width, gdisp->disp_height);
|
|
}
|
|
}
|
|
gdisplays_flush ();
|
|
|
|
transform_core_reset (tool, gdisp_ptr);
|
|
|
|
/* if this tool is non-interactive, make it inactive after use */
|
|
if (!transform_core->interactive)
|
|
tool->state = INACTIVE;
|
|
}
|
|
|
|
|
|
void
|
|
transform_core_motion (tool, mevent, gdisp_ptr)
|
|
Tool *tool;
|
|
GdkEventMotion *mevent;
|
|
gpointer gdisp_ptr;
|
|
{
|
|
GDisplay *gdisp;
|
|
TransformCore *transform_core;
|
|
|
|
|
|
gdisp = (GDisplay *) gdisp_ptr;
|
|
transform_core = (TransformCore *) tool->private;
|
|
|
|
if(transform_core->bpressed == FALSE)
|
|
{
|
|
/* hey we have not got the button press yet
|
|
* so go away.
|
|
*/
|
|
return;
|
|
}
|
|
|
|
/* if we are creating or this tool is non-interactive, there is
|
|
* nothing to be done so exit.
|
|
*/
|
|
if (transform_core->function == CREATING || !transform_core->interactive)
|
|
return;
|
|
|
|
/* stop the current tool drawing process */
|
|
draw_core_pause (transform_core->core, tool);
|
|
|
|
gdisplay_untransform_coords (gdisp, mevent->x, mevent->y, &transform_core->curx,
|
|
&transform_core->cury, TRUE, 0);
|
|
transform_core->state = mevent->state;
|
|
|
|
/* recalculate the tool's transformation matrix */
|
|
(* transform_core->trans_func) (tool, gdisp_ptr, MOTION);
|
|
|
|
transform_core->lastx = transform_core->curx;
|
|
transform_core->lasty = transform_core->cury;
|
|
|
|
/* resume drawing the current tool */
|
|
draw_core_resume (transform_core->core, tool);
|
|
}
|
|
|
|
|
|
void
|
|
transform_core_cursor_update (tool, mevent, gdisp_ptr)
|
|
Tool *tool;
|
|
GdkEventMotion *mevent;
|
|
gpointer gdisp_ptr;
|
|
{
|
|
GDisplay *gdisp;
|
|
TransformCore *transform_core;
|
|
Layer *layer;
|
|
int use_transform_cursor = FALSE;
|
|
GdkCursorType ctype = GDK_TOP_LEFT_ARROW;
|
|
int x, y;
|
|
|
|
gdisp = (GDisplay *) gdisp_ptr;
|
|
transform_core = (TransformCore *) tool->private;
|
|
|
|
gdisplay_untransform_coords (gdisp, mevent->x, mevent->y, &x, &y, FALSE, FALSE);
|
|
if ((layer = gimage_get_active_layer (gdisp->gimage)))
|
|
if (x >= GIMP_DRAWABLE(layer)->offset_x && y >= GIMP_DRAWABLE(layer)->offset_y &&
|
|
x < (GIMP_DRAWABLE(layer)->offset_x + GIMP_DRAWABLE(layer)->width) &&
|
|
y < (GIMP_DRAWABLE(layer)->offset_y + GIMP_DRAWABLE(layer)->height))
|
|
{
|
|
if (gimage_mask_is_empty (gdisp->gimage) ||
|
|
gimage_mask_value (gdisp->gimage, x, y))
|
|
use_transform_cursor = TRUE;
|
|
}
|
|
|
|
if (use_transform_cursor)
|
|
/* ctype based on transform tool type */
|
|
switch (tool->type)
|
|
{
|
|
case ROTATE: ctype = GDK_EXCHANGE; break;
|
|
case SCALE: ctype = GDK_SIZING; break;
|
|
case SHEAR: ctype = GDK_TCROSS; break;
|
|
case PERSPECTIVE: ctype = GDK_TCROSS; break;
|
|
case FLIP_HORZ: ctype = GDK_SB_H_DOUBLE_ARROW; break;
|
|
case FLIP_VERT: ctype = GDK_SB_V_DOUBLE_ARROW; break;
|
|
default: break;
|
|
}
|
|
|
|
gdisplay_install_tool_cursor (gdisp, ctype);
|
|
}
|
|
|
|
|
|
void
|
|
transform_core_control (tool, action, gdisp_ptr)
|
|
Tool *tool;
|
|
int action;
|
|
gpointer gdisp_ptr;
|
|
{
|
|
TransformCore * transform_core;
|
|
|
|
transform_core = (TransformCore *) tool->private;
|
|
|
|
switch (action)
|
|
{
|
|
case PAUSE :
|
|
draw_core_pause (transform_core->core, tool);
|
|
break;
|
|
case RESUME :
|
|
if (transform_core_recalc (tool, gdisp_ptr))
|
|
draw_core_resume (transform_core->core, tool);
|
|
else
|
|
{
|
|
info_dialog_popdown (transform_info);
|
|
tool->state = INACTIVE;
|
|
}
|
|
break;
|
|
case HALT :
|
|
transform_core_reset (tool, gdisp_ptr);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
transform_core_no_draw (tool)
|
|
Tool * tool;
|
|
{
|
|
return;
|
|
}
|
|
|
|
void
|
|
transform_core_draw (tool)
|
|
Tool * tool;
|
|
{
|
|
int x1, y1, x2, y2, x3, y3, x4, y4;
|
|
TransformCore * transform_core;
|
|
GDisplay * gdisp;
|
|
int srw, srh;
|
|
int i, k, gci;
|
|
int xa, ya, xb, yb;
|
|
|
|
gdisp = tool->gdisp_ptr;
|
|
transform_core = (TransformCore *) tool->private;
|
|
|
|
gdisplay_transform_coords (gdisp, transform_core->tx1, transform_core->ty1,
|
|
&transform_core->sx1, &transform_core->sy1, 0);
|
|
gdisplay_transform_coords (gdisp, transform_core->tx2, transform_core->ty2,
|
|
&transform_core->sx2, &transform_core->sy2, 0);
|
|
gdisplay_transform_coords (gdisp, transform_core->tx3, transform_core->ty3,
|
|
&transform_core->sx3, &transform_core->sy3, 0);
|
|
gdisplay_transform_coords (gdisp, transform_core->tx4, transform_core->ty4,
|
|
&transform_core->sx4, &transform_core->sy4, 0);
|
|
|
|
x1 = transform_core->sx1; y1 = transform_core->sy1;
|
|
x2 = transform_core->sx2; y2 = transform_core->sy2;
|
|
x3 = transform_core->sx3; y3 = transform_core->sy3;
|
|
x4 = transform_core->sx4; y4 = transform_core->sy4;
|
|
|
|
/* find the handles' width and height */
|
|
srw = 10;
|
|
srh = 10;
|
|
|
|
/* draw the bounding box */
|
|
gdk_draw_line (transform_core->core->win, transform_core->core->gc,
|
|
x1, y1, x2, y2);
|
|
gdk_draw_line (transform_core->core->win, transform_core->core->gc,
|
|
x2, y2, x4, y4);
|
|
gdk_draw_line (transform_core->core->win, transform_core->core->gc,
|
|
x3, y3, x4, y4);
|
|
gdk_draw_line (transform_core->core->win, transform_core->core->gc,
|
|
x3, y3, x1, y1);
|
|
|
|
/* Draw the grid */
|
|
|
|
if ((transform_core->grid_coords != NULL) &&
|
|
(transform_core->tgrid_coords != NULL) &&
|
|
((tool->type != PERSPECTIVE) ||
|
|
((transform_core->transform[0][0] >=0.0) &&
|
|
(transform_core->transform[1][1] >=0.0))))
|
|
{
|
|
|
|
gci = 0;
|
|
k = transform_core->ngx + transform_core->ngy;
|
|
for (i = 0; i < k; i++)
|
|
{
|
|
gdisplay_transform_coords (gdisp, transform_core->tgrid_coords[gci],
|
|
transform_core->tgrid_coords[gci+1],
|
|
&xa, &ya, 0);
|
|
gdisplay_transform_coords (gdisp, transform_core->tgrid_coords[gci+2],
|
|
transform_core->tgrid_coords[gci+3],
|
|
&xb, &yb, 0);
|
|
|
|
gdk_draw_line (transform_core->core->win, transform_core->core->gc,
|
|
xa, ya, xb, yb);
|
|
gci += 4;
|
|
}
|
|
}
|
|
|
|
/* draw the tool handles */
|
|
gdk_draw_rectangle (transform_core->core->win, transform_core->core->gc, 0,
|
|
x1 - (srw >> 1), y1 - (srh >> 1), srw, srh);
|
|
gdk_draw_rectangle (transform_core->core->win, transform_core->core->gc, 0,
|
|
x2 - (srw >> 1), y2 - (srh >> 1), srw, srh);
|
|
gdk_draw_rectangle (transform_core->core->win, transform_core->core->gc, 0,
|
|
x3 - (srw >> 1), y3 - (srh >> 1), srw, srh);
|
|
gdk_draw_rectangle (transform_core->core->win, transform_core->core->gc, 0,
|
|
x4 - (srw >> 1), y4 - (srh >> 1), srw, srh);
|
|
|
|
/* draw the center */
|
|
if (tool->type == ROTATE)
|
|
{
|
|
gdisplay_transform_coords (gdisp, transform_core->tcx, transform_core->tcy,
|
|
&transform_core->scx, &transform_core->scy, 0);
|
|
|
|
gdk_draw_arc (transform_core->core->win, transform_core->core->gc, 1,
|
|
transform_core->scx - (srw >> 1),
|
|
transform_core->scy - (srh >> 1),
|
|
srw, srh, 0, 23040);
|
|
}
|
|
}
|
|
|
|
Tool *
|
|
transform_core_new (type, interactive)
|
|
int type;
|
|
int interactive;
|
|
{
|
|
Tool * tool;
|
|
TransformCore * private;
|
|
int i;
|
|
|
|
tool = (Tool *) g_malloc (sizeof (Tool));
|
|
private = (TransformCore *) g_malloc (sizeof (TransformCore));
|
|
|
|
private->interactive = interactive;
|
|
|
|
if (interactive)
|
|
private->core = draw_core_new (transform_core_draw);
|
|
else
|
|
private->core = draw_core_new (transform_core_no_draw);
|
|
|
|
private->function = CREATING;
|
|
private->original = NULL;
|
|
|
|
private->bpressed = FALSE;
|
|
|
|
for (i = 0; i < TRAN_INFO_SIZE; i++)
|
|
private->trans_info[i] = 0;
|
|
|
|
private->grid_coords = private->tgrid_coords = NULL;
|
|
|
|
tool->type = type;
|
|
tool->state = INACTIVE;
|
|
tool->scroll_lock = 1; /* Do not allow scrolling */
|
|
tool->auto_snap_to = TRUE;
|
|
tool->private = (void *) private;
|
|
|
|
tool->preserve = FALSE; /* Destroy when the image is dirtied. */
|
|
|
|
tool->button_press_func = transform_core_button_press;
|
|
tool->button_release_func = transform_core_button_release;
|
|
tool->motion_func = transform_core_motion;
|
|
tool->arrow_keys_func = standard_arrow_keys_func;
|
|
tool->cursor_update_func = transform_core_cursor_update;
|
|
tool->control_func = transform_core_control;
|
|
|
|
return tool;
|
|
}
|
|
|
|
void
|
|
transform_core_free (tool)
|
|
Tool *tool;
|
|
{
|
|
TransformCore * transform_core;
|
|
|
|
transform_core = (TransformCore *) tool->private;
|
|
|
|
/* Make sure the selection core is not visible */
|
|
if (tool->state == ACTIVE)
|
|
draw_core_stop (transform_core->core, tool);
|
|
|
|
/* Free the selection core */
|
|
draw_core_free (transform_core->core);
|
|
|
|
/* Free up the original selection if it exists */
|
|
if (transform_core->original)
|
|
tile_manager_destroy (transform_core->original);
|
|
|
|
/* If there is an information dialog, free it up */
|
|
if (transform_info)
|
|
info_dialog_free (transform_info);
|
|
transform_info = NULL;
|
|
|
|
/* Free the grid line endpoint arrays if they exist */
|
|
if (transform_core->grid_coords != NULL)
|
|
g_free (transform_core->grid_coords);
|
|
if (transform_core->tgrid_coords != NULL)
|
|
g_free (transform_core->tgrid_coords);
|
|
|
|
/* Finally, free the transform tool itself */
|
|
g_free (transform_core);
|
|
}
|
|
|
|
void
|
|
transform_bounding_box (tool)
|
|
Tool * tool;
|
|
{
|
|
TransformCore * transform_core;
|
|
int i, k;
|
|
int gci;
|
|
|
|
transform_core = (TransformCore *) tool->private;
|
|
|
|
gimp_matrix_transform_point (transform_core->transform,
|
|
transform_core->x1, transform_core->y1,
|
|
&transform_core->tx1, &transform_core->ty1);
|
|
gimp_matrix_transform_point (transform_core->transform,
|
|
transform_core->x2, transform_core->y1,
|
|
&transform_core->tx2, &transform_core->ty2);
|
|
gimp_matrix_transform_point (transform_core->transform,
|
|
transform_core->x1, transform_core->y2,
|
|
&transform_core->tx3, &transform_core->ty3);
|
|
gimp_matrix_transform_point (transform_core->transform,
|
|
transform_core->x2, transform_core->y2,
|
|
&transform_core->tx4, &transform_core->ty4);
|
|
|
|
if (tool->type == ROTATE)
|
|
gimp_matrix_transform_point (transform_core->transform,
|
|
transform_core->cx, transform_core->cy,
|
|
&transform_core->tcx, &transform_core->tcy);
|
|
|
|
if (transform_core->grid_coords != NULL &&
|
|
transform_core->tgrid_coords != NULL)
|
|
{
|
|
gci = 0;
|
|
k = (transform_core->ngx + transform_core->ngy) * 2;
|
|
for (i = 0; i < k; i++)
|
|
{
|
|
gimp_matrix_transform_point (transform_core->transform,
|
|
transform_core->grid_coords[gci],
|
|
transform_core->grid_coords[gci+1],
|
|
&(transform_core->tgrid_coords[gci]),
|
|
&(transform_core->tgrid_coords[gci+1]));
|
|
gci += 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
transform_core_reset(tool, gdisp_ptr)
|
|
Tool * tool;
|
|
void * gdisp_ptr;
|
|
{
|
|
TransformCore * transform_core;
|
|
GDisplay * gdisp;
|
|
|
|
transform_core = (TransformCore *) tool->private;
|
|
gdisp = (GDisplay *) gdisp_ptr;
|
|
|
|
if (transform_core->original)
|
|
tile_manager_destroy (transform_core->original);
|
|
transform_core->original = NULL;
|
|
|
|
/* inactivate the tool */
|
|
transform_core->function = CREATING;
|
|
draw_core_stop (transform_core->core, tool);
|
|
info_dialog_popdown (transform_info);
|
|
tool->state = INACTIVE;
|
|
}
|
|
|
|
static int
|
|
transform_core_bounds (tool, gdisp_ptr)
|
|
Tool *tool;
|
|
void *gdisp_ptr;
|
|
{
|
|
GDisplay * gdisp;
|
|
TransformCore * transform_core;
|
|
TileManager * tiles;
|
|
GimpDrawable *drawable;
|
|
int offset_x, offset_y;
|
|
|
|
gdisp = (GDisplay *) gdisp_ptr;
|
|
transform_core = (TransformCore *) tool->private;
|
|
tiles = transform_core->original;
|
|
drawable = gimage_active_drawable (gdisp->gimage);
|
|
|
|
/* find the boundaries */
|
|
if (tiles)
|
|
{
|
|
transform_core->x1 = tiles->x;
|
|
transform_core->y1 = tiles->y;
|
|
transform_core->x2 = tiles->x + tiles->width;
|
|
transform_core->y2 = tiles->y + tiles->height;
|
|
}
|
|
else
|
|
{
|
|
drawable_offsets (drawable, &offset_x, &offset_y);
|
|
drawable_mask_bounds (drawable,
|
|
&transform_core->x1, &transform_core->y1,
|
|
&transform_core->x2, &transform_core->y2);
|
|
transform_core->x1 += offset_x;
|
|
transform_core->y1 += offset_y;
|
|
transform_core->x2 += offset_x;
|
|
transform_core->y2 += offset_y;
|
|
}
|
|
transform_core->cx = (transform_core->x1 + transform_core->x2) / 2;
|
|
transform_core->cy = (transform_core->y1 + transform_core->y2) / 2;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
transform_core_grid_density_changed ()
|
|
{
|
|
TransformCore * transform_core;
|
|
|
|
transform_core = (TransformCore *) active_tool->private;
|
|
|
|
if (transform_core->function == CREATING)
|
|
return;
|
|
|
|
draw_core_pause (transform_core->core, active_tool);
|
|
if (transform_core->grid_coords != NULL)
|
|
{
|
|
g_free (transform_core->grid_coords);
|
|
transform_core->grid_coords = NULL;
|
|
}
|
|
if (transform_core->tgrid_coords != NULL)
|
|
{
|
|
g_free (transform_core->tgrid_coords);
|
|
transform_core->tgrid_coords = NULL;
|
|
}
|
|
if (transform_tool_show_grid())
|
|
transform_core_setup_grid (active_tool);
|
|
transform_bounding_box (active_tool);
|
|
draw_core_resume (transform_core->core, active_tool);
|
|
}
|
|
|
|
static void
|
|
transform_core_setup_grid (tool)
|
|
Tool *tool;
|
|
{
|
|
TransformCore * transform_core;
|
|
int i, gci;
|
|
double *coords;
|
|
|
|
transform_core = (TransformCore *) tool->private;
|
|
|
|
/* We use the transform_tool_grid_size function only here, even
|
|
* if the user changes the grid size in the middle of an
|
|
* operation, nothing happens.
|
|
*/
|
|
transform_core->ngx =
|
|
(transform_core->x2 - transform_core->x1) / transform_tool_grid_size ();
|
|
if (transform_core->ngx > 0)
|
|
transform_core->ngx--;
|
|
transform_core->ngy =
|
|
(transform_core->y2 - transform_core->y1) / transform_tool_grid_size ();
|
|
if (transform_core->ngy > 0)
|
|
transform_core->ngy--;
|
|
transform_core->grid_coords = coords = (double *)
|
|
g_malloc ((transform_core->ngx + transform_core->ngy) * 4
|
|
* sizeof(double));
|
|
transform_core->tgrid_coords = (double *)
|
|
g_malloc ((transform_core->ngx + transform_core->ngy) * 4
|
|
* sizeof(double));
|
|
|
|
gci = 0;
|
|
for (i = 1; i <= transform_core->ngx; i++)
|
|
{
|
|
coords[gci] = transform_core->x1 +
|
|
((double) i)/(transform_core->ngx + 1) *
|
|
(transform_core->x2 - transform_core->x1);
|
|
coords[gci+1] = transform_core->y1;
|
|
coords[gci+2] = coords[gci];
|
|
coords[gci+3] = transform_core->y2;
|
|
gci += 4;
|
|
}
|
|
for (i = 1; i <= transform_core->ngy; i++)
|
|
{
|
|
coords[gci] = transform_core->x1;
|
|
coords[gci+1] = transform_core->y1 +
|
|
((double) i)/(transform_core->ngy + 1) *
|
|
(transform_core->y2 - transform_core->y1);
|
|
coords[gci+2] = transform_core->x2;
|
|
coords[gci+3] = coords[gci+1];
|
|
gci += 4;
|
|
}
|
|
}
|
|
|
|
static void *
|
|
transform_core_recalc (tool, gdisp_ptr)
|
|
Tool * tool;
|
|
void * gdisp_ptr;
|
|
{
|
|
TransformCore * transform_core;
|
|
|
|
transform_core = (TransformCore *) tool->private;
|
|
|
|
transform_core_bounds (tool, gdisp_ptr);
|
|
return (* transform_core->trans_func) (tool, gdisp_ptr, RECALC);
|
|
}
|
|
|
|
/* Actually carry out a transformation */
|
|
TileManager *
|
|
transform_core_do (gimage, drawable, float_tiles, interpolation, matrix)
|
|
GImage *gimage;
|
|
GimpDrawable *drawable;
|
|
TileManager *float_tiles;
|
|
int interpolation;
|
|
GimpMatrix matrix;
|
|
{
|
|
PixelRegion destPR;
|
|
TileManager *tiles;
|
|
GimpMatrix m;
|
|
GimpMatrix im;
|
|
int itx, ity;
|
|
int tx1, ty1, tx2, ty2;
|
|
int width, height;
|
|
int alpha;
|
|
int bytes, b;
|
|
int x, y;
|
|
int sx, sy;
|
|
int plus_x, plus_y;
|
|
int plus2_x, plus2_y;
|
|
int minus_x, minus_y;
|
|
int x1, y1, x2, y2;
|
|
double xinc, yinc, winc;
|
|
double tx, ty, tw;
|
|
double ttx = 0.0, tty = 0.0;
|
|
double dx = 0.0, dy = 0.0;
|
|
unsigned char * dest, * d;
|
|
unsigned char * src[16];
|
|
double src_a[16][MAX_CHANNELS];
|
|
Tile *tile[16];
|
|
int a[16];
|
|
unsigned char bg_col[MAX_CHANNELS];
|
|
int i;
|
|
double a_val, a_mult, a_recip;
|
|
int newval;
|
|
|
|
alpha = 0;
|
|
|
|
/* Get the background color */
|
|
gimage_get_background (gimage, drawable, bg_col);
|
|
|
|
switch (drawable_type (drawable))
|
|
{
|
|
case RGB_GIMAGE: case RGBA_GIMAGE:
|
|
bg_col[ALPHA_PIX] = TRANSPARENT_OPACITY;
|
|
alpha = 3;
|
|
break;
|
|
case GRAY_GIMAGE: case GRAYA_GIMAGE:
|
|
bg_col[ALPHA_G_PIX] = TRANSPARENT_OPACITY;
|
|
alpha = 1;
|
|
break;
|
|
case INDEXED_GIMAGE: case INDEXEDA_GIMAGE:
|
|
bg_col[ALPHA_I_PIX] = TRANSPARENT_OPACITY;
|
|
alpha = 1;
|
|
/* If the gimage is indexed color, ignore smoothing value */
|
|
interpolation = 0;
|
|
break;
|
|
}
|
|
|
|
if (transform_tool_direction () == TRANSFORM_CORRECTIVE)
|
|
{
|
|
/* keep the original matrix here, so we dont need to recalculate
|
|
the inverse later */
|
|
gimp_matrix_duplicate (matrix, m);
|
|
gimp_matrix_invert (matrix, im);
|
|
matrix = im;
|
|
}
|
|
else
|
|
{
|
|
/* Find the inverse of the transformation matrix */
|
|
gimp_matrix_invert (matrix, m);
|
|
}
|
|
|
|
x1 = float_tiles->x;
|
|
y1 = float_tiles->y;
|
|
x2 = x1 + float_tiles->width;
|
|
y2 = y1 + float_tiles->height;
|
|
|
|
/* Find the bounding coordinates */
|
|
if (active_tool && transform_tool_clip ())
|
|
{
|
|
tx1 = x1;
|
|
ty1 = y1;
|
|
tx2 = x2;
|
|
ty2 = y2;
|
|
}
|
|
else
|
|
{
|
|
double dx1, dy1, dx2, dy2, dx3, dy3, dx4, dy4;
|
|
|
|
gimp_matrix_transform_point (matrix, x1, y1, &dx1, &dy1);
|
|
gimp_matrix_transform_point (matrix, x2, y1, &dx2, &dy2);
|
|
gimp_matrix_transform_point (matrix, x1, y2, &dx3, &dy3);
|
|
gimp_matrix_transform_point (matrix, x2, y2, &dx4, &dy4);
|
|
|
|
tx1 = MINIMUM (dx1, dx2);
|
|
tx1 = MINIMUM (tx1, dx3);
|
|
tx1 = MINIMUM (tx1, dx4);
|
|
ty1 = MINIMUM (dy1, dy2);
|
|
ty1 = MINIMUM (ty1, dy3);
|
|
ty1 = MINIMUM (ty1, dy4);
|
|
tx2 = MAXIMUM (dx1, dx2);
|
|
tx2 = MAXIMUM (tx2, dx3);
|
|
tx2 = MAXIMUM (tx2, dx4);
|
|
ty2 = MAXIMUM (dy1, dy2);
|
|
ty2 = MAXIMUM (ty2, dy3);
|
|
ty2 = MAXIMUM (ty2, dy4);
|
|
}
|
|
|
|
/* Get the new temporary buffer for the transformed result */
|
|
tiles = tile_manager_new ((tx2 - tx1), (ty2 - ty1), float_tiles->bpp);
|
|
pixel_region_init (&destPR, tiles, 0, 0, (tx2 - tx1), (ty2 - ty1), TRUE);
|
|
tiles->x = tx1;
|
|
tiles->y = ty1;
|
|
|
|
width = tiles->width;
|
|
height = tiles->height;
|
|
bytes = tiles->bpp;
|
|
|
|
dest = (unsigned char *) g_malloc (width * bytes);
|
|
|
|
xinc = m[0][0];
|
|
yinc = m[1][0];
|
|
winc = m[2][0];
|
|
|
|
for (y = ty1; y < ty2; y++)
|
|
{
|
|
/* When we calculate the inverse transformation, we should transform
|
|
* the center of each destination pixel...
|
|
*/
|
|
tx = xinc * (tx1 + 0.5) + m[0][1] * (y + 0.5) + m[0][2];
|
|
ty = yinc * (tx1 + 0.5) + m[1][1] * (y + 0.5) + m[1][2];
|
|
tw = winc * (tx1 + 0.5) + m[2][1] * (y + 0.5) + m[2][2];
|
|
d = dest;
|
|
for (x = tx1; x < tx2; x++)
|
|
{
|
|
/* normalize homogeneous coords */
|
|
if (tw == 0.0)
|
|
g_message (_("homogeneous coordinate = 0...\n"));
|
|
else if (tw != 1.0)
|
|
{
|
|
ttx = tx / tw;
|
|
tty = ty / tw;
|
|
}
|
|
else
|
|
{
|
|
ttx = tx;
|
|
tty = ty;
|
|
}
|
|
|
|
/* tx & ty are the coordinates of the point in the original
|
|
* selection's floating buffer. Make sure they're within bounds
|
|
*/
|
|
if (ttx < 0)
|
|
itx = (int) (ttx - 0.999999);
|
|
else
|
|
itx = (int) ttx;
|
|
|
|
if (tty < 0)
|
|
ity = (int) (tty - 0.999999);
|
|
else
|
|
ity = (int) tty;
|
|
|
|
/* if interpolation is on, get the fractional error */
|
|
if (interpolation)
|
|
{
|
|
dx = ttx - itx;
|
|
dy = tty - ity;
|
|
}
|
|
|
|
if (itx >= x1 && itx < x2 && ity >= y1 && ity < y2)
|
|
{
|
|
/* x, y coordinates into source tiles */
|
|
sx = itx - x1;
|
|
sy = ity - y1;
|
|
|
|
/* Set the destination pixels */
|
|
if (interpolation)
|
|
{
|
|
plus_x = (itx < (x2 - 1)) ? 1 : 0;
|
|
plus_y = (ity < (y2 - 1)) ? 1 : 0;
|
|
|
|
if (cubic_interpolation)
|
|
{
|
|
minus_x = (itx > x1) ? -1 : 0;
|
|
plus2_x = ((itx + 1) < (x2 - 1)) ? 2 : plus_x;
|
|
|
|
minus_y = (ity > y1) ? -1 : 0;
|
|
plus2_y = ((ity + 1) < (y2 - 1)) ? 2 : plus_y;
|
|
|
|
REF_TILE (0, sx + minus_x, sy + minus_y);
|
|
REF_TILE (1, sx, sy + minus_y);
|
|
REF_TILE (2, sx + plus_x, sy + minus_y);
|
|
REF_TILE (3, sx + plus2_x, sy + minus_y);
|
|
REF_TILE (4, sx + minus_x, sy);
|
|
REF_TILE (5, sx, sy);
|
|
REF_TILE (6, sx + plus_x, sy);
|
|
REF_TILE (7, sx + plus2_x, sy);
|
|
REF_TILE (8, sx + minus_x, sy + plus_y);
|
|
REF_TILE (9, sx, sy + plus_y);
|
|
REF_TILE (10, sx + plus_x, sy + plus_y);
|
|
REF_TILE (11, sx + plus2_x, sy + plus_y);
|
|
REF_TILE (12, sx + minus_x, sy + plus2_y);
|
|
REF_TILE (13, sx, sy + plus2_y);
|
|
REF_TILE (14, sx + plus_x, sy + plus2_y);
|
|
REF_TILE (15, sx + plus2_x, sy + plus2_y);
|
|
|
|
a[0] = (minus_y * minus_x) ? src[0][alpha] : 0;
|
|
a[1] = (minus_y) ? src[1][alpha] : 0;
|
|
a[2] = (minus_y * plus_x) ? src[2][alpha] : 0;
|
|
a[3] = (minus_y * plus2_x) ? src[3][alpha] : 0;
|
|
|
|
a[4] = (minus_x) ? src[4][alpha] : 0;
|
|
a[5] = src[5][alpha];
|
|
a[6] = (plus_x) ? src[6][alpha] : 0;
|
|
a[7] = (plus2_x) ? src[7][alpha] : 0;
|
|
|
|
a[8] = (plus_y * minus_x) ? src[8][alpha] : 0;
|
|
a[9] = (plus_y) ? src[9][alpha] : 0;
|
|
a[10] = (plus_y * plus_x) ? src[10][alpha] : 0;
|
|
a[11] = (plus_y * plus2_x) ? src[11][alpha] : 0;
|
|
|
|
a[12] = (plus2_y * minus_x) ? src[12][alpha] : 0;
|
|
a[13] = (plus2_y) ? src[13][alpha] : 0;
|
|
a[14] = (plus2_y * plus_x) ? src[14][alpha] : 0;
|
|
a[15] = (plus2_y * plus2_x) ? src[15][alpha] : 0;
|
|
|
|
a_val = cubic (dy,
|
|
cubic (dx, a[0], a[1], a[2], a[3]),
|
|
cubic (dx, a[4], a[5], a[6], a[7]),
|
|
cubic (dx, a[8], a[9], a[10], a[11]),
|
|
cubic (dx, a[12], a[13], a[14], a[15]));
|
|
|
|
if (a_val != 0)
|
|
a_recip = 255.0 / a_val;
|
|
else
|
|
a_recip = 0.0;
|
|
|
|
/* premultiply the alpha */
|
|
for (i = 0; i < 16; i++)
|
|
{
|
|
a_mult = a[i] * (1.0 / 255.0);
|
|
for (b = 0; b < alpha; b++)
|
|
src_a[i][b] = src[i][b] * a_mult;
|
|
}
|
|
|
|
for (b = 0; b < alpha; b++)
|
|
{
|
|
newval =
|
|
a_recip *
|
|
cubic (dy,
|
|
cubic (dx, src_a[0][b], src_a[1][b], src_a[2][b], src_a[3][b]),
|
|
cubic (dx, src_a[4][b], src_a[5][b], src_a[6][b], src_a[7][b]),
|
|
cubic (dx, src_a[8][b], src_a[9][b], src_a[10][b], src_a[11][b]),
|
|
cubic (dx, src_a[12][b], src_a[13][b], src_a[14][b], src_a[15][b]));
|
|
if ((newval & 0x100) == 0)
|
|
*d++ = newval;
|
|
else if (newval < 0)
|
|
*d++ = 0;
|
|
else
|
|
*d++ = 255;
|
|
}
|
|
|
|
*d++ = a_val;
|
|
|
|
for (b = 0; b < 16; b++)
|
|
tile_release (tile[b], FALSE);
|
|
}
|
|
else /* linear */
|
|
{
|
|
REF_TILE (0, sx, sy);
|
|
REF_TILE (1, sx + plus_x, sy);
|
|
REF_TILE (2, sx, sy + plus_y);
|
|
REF_TILE (3, sx + plus_x, sy + plus_y);
|
|
|
|
/* Need special treatment for the alpha channel */
|
|
if (plus_x == 0 && plus_y == 0)
|
|
{
|
|
a[0] = src[0][alpha];
|
|
a[1] = a[2] = a[3] = 0;
|
|
}
|
|
else if (plus_x == 0)
|
|
{
|
|
a[0] = src[0][alpha];
|
|
a[2] = src[2][alpha];
|
|
a[1] = a[3] = 0;
|
|
}
|
|
else if (plus_y == 0)
|
|
{
|
|
a[0] = src[0][alpha];
|
|
a[1] = src[1][alpha];
|
|
a[2] = a[3] = 0;
|
|
}
|
|
else
|
|
{
|
|
a[0] = src[0][alpha];
|
|
a[1] = src[1][alpha];
|
|
a[2] = src[2][alpha];
|
|
a[3] = src[3][alpha];
|
|
}
|
|
|
|
/* The alpha channel */
|
|
a_val = BILINEAR (a[0], a[1], a[2], a[3], dx, dy);
|
|
|
|
if (a_val != 0)
|
|
a_recip = 255.0 / a_val;
|
|
else
|
|
a_recip = 0.0;
|
|
|
|
/* premultiply the alpha */
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
a_mult = a[i] * (1.0 / 255.0);
|
|
for (b = 0; b < alpha; b++)
|
|
src_a[i][b] = src[i][b] * a_mult;
|
|
}
|
|
|
|
for (b = 0; b < alpha; b++)
|
|
*d++ = a_recip * BILINEAR (src_a[0][b], src_a[1][b], src_a[2][b], src_a[3][b], dx, dy);
|
|
|
|
*d++ = a_val;
|
|
|
|
for (b = 0; b < 4; b++)
|
|
tile_release (tile[b], FALSE);
|
|
}
|
|
}
|
|
else /* no interpolation */
|
|
{
|
|
REF_TILE (0, sx, sy);
|
|
|
|
for (b = 0; b < bytes; b++)
|
|
*d++ = src[0][b];
|
|
|
|
tile_release (tile[0], FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* increment the destination pointers */
|
|
for (b = 0; b < bytes; b++)
|
|
*d++ = bg_col[b];
|
|
}
|
|
/* increment the transformed coordinates */
|
|
tx += xinc;
|
|
ty += yinc;
|
|
tw += winc;
|
|
}
|
|
|
|
/* set the pixel region row */
|
|
pixel_region_set_row (&destPR, 0, (y - ty1), width, dest);
|
|
}
|
|
|
|
g_free (dest);
|
|
return tiles;
|
|
}
|
|
|
|
|
|
TileManager *
|
|
transform_core_cut (gimage, drawable, new_layer)
|
|
GImage *gimage;
|
|
GimpDrawable *drawable;
|
|
int *new_layer;
|
|
{
|
|
TileManager *tiles;
|
|
|
|
/* extract the selected mask if there is a selection */
|
|
if (! gimage_mask_is_empty (gimage))
|
|
{
|
|
tiles = gimage_mask_extract (gimage, drawable, TRUE, TRUE);
|
|
*new_layer = TRUE;
|
|
}
|
|
/* otherwise, just copy the layer */
|
|
else
|
|
{
|
|
tiles = gimage_mask_extract (gimage, drawable, FALSE, TRUE);
|
|
*new_layer = FALSE;
|
|
}
|
|
|
|
return tiles;
|
|
}
|
|
|
|
|
|
/* Paste a transform to the gdisplay */
|
|
Layer *
|
|
transform_core_paste (gimage, drawable, tiles, new_layer)
|
|
GImage *gimage;
|
|
GimpDrawable *drawable;
|
|
TileManager *tiles;
|
|
int new_layer;
|
|
{
|
|
Layer * layer;
|
|
Layer * floating_layer;
|
|
|
|
if (new_layer)
|
|
{
|
|
layer = layer_from_tiles (gimage, drawable, tiles, "Transformation", OPAQUE_OPACITY, NORMAL_MODE);
|
|
GIMP_DRAWABLE(layer)->offset_x = tiles->x;
|
|
GIMP_DRAWABLE(layer)->offset_y = tiles->y;
|
|
|
|
/* Start a group undo */
|
|
undo_push_group_start (gimage, EDIT_PASTE_UNDO);
|
|
|
|
floating_sel_attach (layer, drawable);
|
|
|
|
/* End the group undo */
|
|
undo_push_group_end (gimage);
|
|
|
|
/* Free the tiles */
|
|
tile_manager_destroy (tiles);
|
|
|
|
active_tool_layer = layer;
|
|
|
|
return layer;
|
|
}
|
|
else
|
|
{
|
|
if (GIMP_IS_LAYER(drawable))
|
|
layer=GIMP_LAYER(drawable);
|
|
else
|
|
return NULL;
|
|
|
|
layer_add_alpha (layer);
|
|
floating_layer = gimage_floating_sel (gimage);
|
|
|
|
if (floating_layer)
|
|
floating_sel_relax (floating_layer, TRUE);
|
|
|
|
gdisplays_update_area (gimage,
|
|
GIMP_DRAWABLE(layer)->offset_x, GIMP_DRAWABLE(layer)->offset_y,
|
|
GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height);
|
|
|
|
/* Push an undo */
|
|
undo_push_layer_mod (gimage, layer);
|
|
|
|
/* set the current layer's data */
|
|
GIMP_DRAWABLE(layer)->tiles = tiles;
|
|
|
|
/* Fill in the new layer's attributes */
|
|
GIMP_DRAWABLE(layer)->width = tiles->width;
|
|
GIMP_DRAWABLE(layer)->height = tiles->height;
|
|
GIMP_DRAWABLE(layer)->bytes = tiles->bpp;
|
|
GIMP_DRAWABLE(layer)->offset_x = tiles->x;
|
|
GIMP_DRAWABLE(layer)->offset_y = tiles->y;
|
|
|
|
if (floating_layer)
|
|
floating_sel_rigor (floating_layer, TRUE);
|
|
|
|
drawable_update (GIMP_DRAWABLE(layer), 0, 0, GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height);
|
|
|
|
return layer;
|
|
}
|
|
}
|
|
|
|
|
|
static double
|
|
cubic (dx, jm1, j, jp1, jp2)
|
|
double dx;
|
|
int jm1, j, jp1, jp2;
|
|
{
|
|
double dx1, dx2, dx3;
|
|
double h1, h2, h3, h4;
|
|
double result;
|
|
|
|
/* constraint parameter = -1 */
|
|
dx1 = fabs (dx);
|
|
dx2 = dx1 * dx1;
|
|
dx3 = dx2 * dx1;
|
|
h1 = dx3 - 2 * dx2 + 1;
|
|
result = h1 * j;
|
|
|
|
dx1 = fabs (dx - 1.0);
|
|
dx2 = dx1 * dx1;
|
|
dx3 = dx2 * dx1;
|
|
h2 = dx3 - 2 * dx2 + 1;
|
|
result += h2 * jp1;
|
|
|
|
dx1 = fabs (dx - 2.0);
|
|
dx2 = dx1 * dx1;
|
|
dx3 = dx2 * dx1;
|
|
h3 = -dx3 + 5 * dx2 - 8 * dx1 + 4;
|
|
result += h3 * jp2;
|
|
|
|
dx1 = fabs (dx + 1.0);
|
|
dx2 = dx1 * dx1;
|
|
dx3 = dx2 * dx1;
|
|
h4 = -dx3 + 5 * dx2 - 8 * dx1 + 4;
|
|
result += h4 * jm1;
|
|
|
|
if (result < 0.0)
|
|
result = 0.0;
|
|
if (result > 255.0)
|
|
result = 255.0;
|
|
|
|
return result;
|
|
}
|