
1999-12-25 Garry R. Osgood <gosgood@idt.net> Season's Greetings! * app/clone.c * app/paint_core.c * app/paint_core.h * MAINTAINERS MAINTAINERS: Updated my entry (it wasn't there ;) app/paint_core.[ch] supplied new PaintTool states to clone_paint_func() so that writes of temporary markings made directly to the window are not clobbered by buffered writes stemming from gdisplay_flush_xxx() routines. clone_tool_paint_func() has been modified to take advantage of these new states, retiring bug #2184 in a way that does not change user interface semantics. There are small additions to the PaintCore interface that do not affect clientele unaware of added semantics. These changes are detailed at http://idt.net/~gosgood/gimp-patch/patch03.html.
789 lines
21 KiB
C
789 lines
21 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 <string.h>
|
|
#include "appenv.h"
|
|
#include "drawable.h"
|
|
#include "errors.h"
|
|
#include "gdisplay.h"
|
|
#include "gimage_mask.h"
|
|
#include "interface.h"
|
|
#include "paint_funcs.h"
|
|
#include "paint_core.h"
|
|
#include "paint_options.h"
|
|
#include "clone.h"
|
|
#include "selection.h"
|
|
#include "tools.h"
|
|
#include "cursorutil.h"
|
|
|
|
#include "libgimp/gimpintl.h"
|
|
|
|
#define TARGET_HEIGHT 15
|
|
#define TARGET_WIDTH 15
|
|
|
|
/* default types */
|
|
#define CLONE_DEFAULT_TYPE IMAGE_CLONE
|
|
#define CLONE_DEFAULT_ALIGNED ALIGN_NO
|
|
|
|
/* the clone structures */
|
|
|
|
typedef enum
|
|
{
|
|
ALIGN_NO,
|
|
ALIGN_YES,
|
|
ALIGN_REGISTERED
|
|
} AlignType;
|
|
|
|
typedef struct _CloneOptions CloneOptions;
|
|
struct _CloneOptions
|
|
{
|
|
PaintOptions paint_options;
|
|
|
|
CloneType type;
|
|
CloneType type_d;
|
|
GtkWidget *type_w[2]; /* 2 radio buttons */
|
|
|
|
AlignType aligned;
|
|
AlignType aligned_d;
|
|
GtkWidget *aligned_w[3]; /* 3 radio buttons */
|
|
};
|
|
|
|
|
|
/* the clone tool options */
|
|
static CloneOptions *clone_options = NULL;
|
|
|
|
/* local variables */
|
|
static int src_x = 0; /* */
|
|
static int src_y = 0; /* position of clone src */
|
|
static int dest_x = 0; /* */
|
|
static int dest_y = 0; /* position of clone src */
|
|
static int offset_x = 0; /* */
|
|
static int offset_y = 0; /* offset for cloning */
|
|
static int first = TRUE;
|
|
static int trans_tx, trans_ty; /* transformed target */
|
|
static GDisplay *the_src_gdisp = NULL; /* ID of source gdisplay */
|
|
static GimpDrawable *src_drawable_ = NULL; /* source drawable */
|
|
|
|
static GimpDrawable *non_gui_src_drawable;
|
|
static int non_gui_offset_x;
|
|
static int non_gui_offset_y;
|
|
static CloneType non_gui_type;
|
|
|
|
/* forward function declarations */
|
|
|
|
static void clone_draw (Tool *);
|
|
static void clone_motion (PaintCore *, GimpDrawable *, GimpDrawable *,
|
|
PaintPressureOptions *, CloneType, int, int);
|
|
static void clone_line_image (GImage *, GImage *, GimpDrawable *,
|
|
GimpDrawable *, unsigned char *,
|
|
unsigned char *, int, int, int, int);
|
|
static void clone_line_pattern (GImage *, GimpDrawable *, GPattern *,
|
|
unsigned char *, int, int, int, int);
|
|
|
|
|
|
/* functions */
|
|
|
|
static void
|
|
clone_type_callback (GtkWidget *widget,
|
|
gpointer data)
|
|
{
|
|
clone_options->type = (CloneType) data;
|
|
}
|
|
|
|
static void
|
|
align_type_callback (GtkWidget *widget,
|
|
gpointer data)
|
|
{
|
|
clone_options->aligned = (AlignType) data;
|
|
}
|
|
|
|
static void
|
|
clone_options_reset (void)
|
|
{
|
|
CloneOptions *options = clone_options;
|
|
|
|
paint_options_reset ((PaintOptions *) options);
|
|
|
|
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->type_w[options->type_d]), TRUE);
|
|
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->aligned_w[options->aligned_d]), TRUE);
|
|
}
|
|
|
|
static CloneOptions *
|
|
clone_options_new (void)
|
|
{
|
|
CloneOptions *options;
|
|
|
|
GtkWidget *vbox;
|
|
GSList *group = NULL;
|
|
GtkWidget *radio_frame;
|
|
GtkWidget *radio_box;
|
|
GtkWidget *radio_button;
|
|
int i;
|
|
|
|
static gchar *source_names[] =
|
|
{
|
|
N_("Image Source"),
|
|
N_("Pattern Source")
|
|
};
|
|
static gint n_source_names = sizeof (source_names) / sizeof (source_names[0]);
|
|
|
|
static gchar *align_names[] =
|
|
{
|
|
N_("Non Aligned"),
|
|
N_("Aligned"),
|
|
N_("Registered")
|
|
};
|
|
static gint n_align_names = sizeof (align_names) / sizeof (align_names[0]);
|
|
|
|
/* the new clone tool options structure */
|
|
options = (CloneOptions *) g_malloc (sizeof (CloneOptions));
|
|
paint_options_init ((PaintOptions *) options,
|
|
CLONE,
|
|
clone_options_reset);
|
|
options->type = options->type_d = CLONE_DEFAULT_TYPE;
|
|
options->aligned = options->aligned_d = CLONE_DEFAULT_ALIGNED;
|
|
|
|
/* the main vbox */
|
|
vbox = ((ToolOptions *) options)->main_vbox;
|
|
|
|
/* the radio frame and box */
|
|
radio_frame = gtk_frame_new (_("Source"));
|
|
gtk_box_pack_start (GTK_BOX (vbox), radio_frame, FALSE, FALSE, 0);
|
|
|
|
radio_box = gtk_vbox_new (FALSE, 1);
|
|
gtk_container_set_border_width (GTK_CONTAINER (radio_box), 2);
|
|
gtk_container_add (GTK_CONTAINER (radio_frame), radio_box);
|
|
|
|
/* the radio buttons */
|
|
for (i = 0; i < n_source_names; i++)
|
|
{
|
|
radio_button = gtk_radio_button_new_with_label (group,
|
|
gettext(source_names[i]));
|
|
group = gtk_radio_button_group (GTK_RADIO_BUTTON (radio_button));
|
|
gtk_signal_connect (GTK_OBJECT (radio_button), "toggled",
|
|
(GtkSignalFunc) clone_type_callback,
|
|
(void *)((long) i));
|
|
gtk_box_pack_start (GTK_BOX (radio_box), radio_button, FALSE, FALSE, 0);
|
|
gtk_widget_show (radio_button);
|
|
|
|
options->type_w[i] = radio_button;
|
|
}
|
|
gtk_widget_show (radio_box);
|
|
gtk_widget_show (radio_frame);
|
|
|
|
/* the radio frame and box */
|
|
radio_frame = gtk_frame_new (_("Alignment"));
|
|
gtk_box_pack_start (GTK_BOX (vbox), radio_frame, FALSE, FALSE, 0);
|
|
|
|
radio_box = gtk_vbox_new (FALSE, 1);
|
|
gtk_container_set_border_width (GTK_CONTAINER (radio_box), 2);
|
|
gtk_container_add (GTK_CONTAINER (radio_frame), radio_box);
|
|
|
|
/* the radio buttons */
|
|
group = NULL;
|
|
for (i = 0; i < n_align_names; i++)
|
|
{
|
|
radio_button = gtk_radio_button_new_with_label (group,
|
|
gettext(align_names[i]));
|
|
group = gtk_radio_button_group (GTK_RADIO_BUTTON (radio_button));
|
|
gtk_signal_connect (GTK_OBJECT (radio_button), "toggled",
|
|
(GtkSignalFunc) align_type_callback,
|
|
(void *)((long) i));
|
|
gtk_box_pack_start (GTK_BOX (radio_box), radio_button, FALSE, FALSE, 0);
|
|
gtk_widget_show (radio_button);
|
|
|
|
options->aligned_w[i] = radio_button;
|
|
}
|
|
gtk_widget_show (radio_box);
|
|
gtk_widget_show (radio_frame);
|
|
|
|
return options;
|
|
}
|
|
|
|
static void
|
|
clone_src_drawable_destroyed_cb (GimpDrawable *drawable,
|
|
GimpDrawable **src_drawable)
|
|
{
|
|
if (drawable == *src_drawable)
|
|
{
|
|
*src_drawable = NULL;
|
|
the_src_gdisp = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
clone_set_src_drawable(GimpDrawable *drawable)
|
|
{
|
|
if (src_drawable_ == drawable)
|
|
return;
|
|
if (src_drawable_)
|
|
gtk_signal_disconnect_by_data(GTK_OBJECT (src_drawable_), &src_drawable_);
|
|
src_drawable_ = drawable;
|
|
if (drawable)
|
|
{
|
|
gtk_signal_connect (GTK_OBJECT (drawable),
|
|
"destroy",
|
|
GTK_SIGNAL_FUNC (clone_src_drawable_destroyed_cb),
|
|
&src_drawable_);
|
|
}
|
|
}
|
|
|
|
|
|
void *
|
|
clone_paint_func (PaintCore *paint_core,
|
|
GimpDrawable *drawable,
|
|
int state)
|
|
{
|
|
GDisplay * gdisp;
|
|
GDisplay * src_gdisp;
|
|
int x1, y1, x2, y2;
|
|
static int orig_src_x, orig_src_y;
|
|
|
|
gdisp = (GDisplay *) active_tool->gdisp_ptr;
|
|
|
|
switch (state)
|
|
{
|
|
case PRETRACE_PAINT :
|
|
draw_core_pause (paint_core->core, active_tool);
|
|
break;
|
|
case MOTION_PAINT :
|
|
x1 = paint_core->curx;
|
|
y1 = paint_core->cury;
|
|
x2 = paint_core->lastx;
|
|
y2 = paint_core->lasty;
|
|
|
|
/* If the control key is down, move the src target and return */
|
|
if (paint_core->state & GDK_CONTROL_MASK)
|
|
{
|
|
src_x = x1;
|
|
src_y = y1;
|
|
first = TRUE;
|
|
}
|
|
/* otherwise, update the target */
|
|
else
|
|
{
|
|
dest_x = x1;
|
|
dest_y = y1;
|
|
|
|
if (clone_options->aligned == ALIGN_REGISTERED)
|
|
{
|
|
offset_x = 0;
|
|
offset_y = 0;
|
|
}
|
|
else if (first)
|
|
{
|
|
offset_x = src_x - dest_x;
|
|
offset_y = src_y - dest_y;
|
|
first = FALSE;
|
|
}
|
|
|
|
src_x = dest_x + offset_x;
|
|
src_y = dest_y + offset_y;
|
|
|
|
clone_motion (paint_core, drawable, src_drawable_,
|
|
clone_options->paint_options.pressure_options,
|
|
clone_options->type, offset_x, offset_y);
|
|
}
|
|
|
|
break;
|
|
|
|
case INIT_PAINT :
|
|
if (paint_core->state & GDK_CONTROL_MASK)
|
|
{
|
|
the_src_gdisp = gdisp;
|
|
clone_set_src_drawable(drawable);
|
|
src_x = paint_core->curx;
|
|
src_y = paint_core->cury;
|
|
first = TRUE;
|
|
}
|
|
else if (clone_options->aligned == ALIGN_NO)
|
|
{
|
|
first = TRUE;
|
|
orig_src_x = src_x;
|
|
orig_src_y = src_y;
|
|
}
|
|
if (clone_options->type == PATTERN_CLONE)
|
|
if (! gimp_context_get_pattern (NULL))
|
|
g_message (_("No patterns available for this operation."));
|
|
break;
|
|
|
|
case FINISH_PAINT :
|
|
draw_core_stop (paint_core->core, active_tool);
|
|
if (clone_options->aligned == ALIGN_NO && !first)
|
|
{
|
|
src_x = orig_src_x;
|
|
src_y = orig_src_y;
|
|
}
|
|
return NULL;
|
|
break;
|
|
|
|
default :
|
|
break;
|
|
}
|
|
|
|
/* Calculate the coordinates of the target */
|
|
src_gdisp = the_src_gdisp;
|
|
if (!src_gdisp)
|
|
{
|
|
the_src_gdisp = gdisp;
|
|
src_gdisp = the_src_gdisp;
|
|
}
|
|
|
|
if (state == INIT_PAINT)
|
|
/* Initialize the tool drawing core */
|
|
draw_core_start (paint_core->core,
|
|
src_gdisp->canvas->window,
|
|
active_tool);
|
|
else if (state == POSTTRACE_PAINT)
|
|
{
|
|
/* Find the target cursor's location onscreen */
|
|
gdisplay_transform_coords (src_gdisp, src_x, src_y, &trans_tx, &trans_ty, 1);
|
|
draw_core_resume (paint_core->core, active_tool);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
clone_cursor_update (Tool *tool,
|
|
GdkEventMotion *mevent,
|
|
gpointer gdisp_ptr)
|
|
{
|
|
GDisplay *gdisp;
|
|
Layer *layer;
|
|
GdkCursorType ctype = GDK_TOP_LEFT_ARROW;
|
|
int x, y;
|
|
|
|
gdisp = (GDisplay *) gdisp_ptr;
|
|
|
|
gdisplay_untransform_coords (gdisp, (double) mevent->x, (double) mevent->y,
|
|
&x, &y, TRUE, FALSE);
|
|
|
|
if ((layer = gimage_get_active_layer (gdisp->gimage)))
|
|
{
|
|
int off_x, off_y;
|
|
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))))
|
|
{
|
|
/* One more test--is there a selected region?
|
|
* if so, is cursor inside?
|
|
*/
|
|
if (gimage_mask_is_empty (gdisp->gimage))
|
|
ctype = GDK_PENCIL;
|
|
else if (gimage_mask_value (gdisp->gimage, x, y))
|
|
ctype = GDK_PENCIL;
|
|
}
|
|
}
|
|
|
|
if (clone_options->type == IMAGE_CLONE)
|
|
{
|
|
if (mevent->state & GDK_CONTROL_MASK)
|
|
ctype = GDK_CROSSHAIR;
|
|
else if (!src_drawable_)
|
|
ctype = GIMP_BAD_CURSOR;
|
|
}
|
|
|
|
gdisplay_install_tool_cursor (gdisp, ctype);
|
|
}
|
|
|
|
|
|
Tool *
|
|
tools_new_clone ()
|
|
{
|
|
Tool * tool;
|
|
PaintCore * private;
|
|
|
|
/* The tool options */
|
|
if (! clone_options)
|
|
{
|
|
clone_options = clone_options_new ();
|
|
tools_register (CLONE, (ToolOptions *) clone_options);
|
|
|
|
/* press all default buttons */
|
|
clone_options_reset ();
|
|
}
|
|
|
|
tool = paint_core_new (CLONE);
|
|
/* the clone tool provides its own cursor_update_function
|
|
until I figure out somethinh nicer -- Sven */
|
|
tool->cursor_update_func = clone_cursor_update;
|
|
|
|
private = (PaintCore *) tool->private;
|
|
private->paint_func = clone_paint_func;
|
|
private->core->draw_func = clone_draw;
|
|
private->flags |= TOOL_TRACES_ON_WINDOW;
|
|
|
|
return tool;
|
|
}
|
|
|
|
void
|
|
tools_free_clone (Tool *tool)
|
|
{
|
|
paint_core_free (tool);
|
|
}
|
|
|
|
static void
|
|
clone_draw (Tool *tool)
|
|
{
|
|
PaintCore * paint_core;
|
|
|
|
paint_core = (PaintCore *) tool->private;
|
|
|
|
if (paint_core->core->gc != NULL && clone_options->type == IMAGE_CLONE)
|
|
{
|
|
gdk_draw_line (paint_core->core->win, paint_core->core->gc,
|
|
trans_tx - (TARGET_WIDTH >> 1), trans_ty,
|
|
trans_tx + (TARGET_WIDTH >> 1), trans_ty);
|
|
gdk_draw_line (paint_core->core->win, paint_core->core->gc,
|
|
trans_tx, trans_ty - (TARGET_HEIGHT >> 1),
|
|
trans_tx, trans_ty + (TARGET_HEIGHT >> 1));
|
|
}
|
|
}
|
|
|
|
static void
|
|
clone_motion (PaintCore *paint_core,
|
|
GimpDrawable *drawable,
|
|
GimpDrawable *src_drawable,
|
|
PaintPressureOptions *pressure_options,
|
|
CloneType type,
|
|
int offset_x,
|
|
int offset_y)
|
|
{
|
|
GImage *gimage;
|
|
GImage *src_gimage = NULL;
|
|
unsigned char * s;
|
|
unsigned char * d;
|
|
TempBuf * orig;
|
|
TempBuf * area;
|
|
void * pr;
|
|
int y;
|
|
int x1, y1, x2, y2;
|
|
int has_alpha = -1;
|
|
PixelRegion srcPR, destPR;
|
|
GPattern *pattern;
|
|
gint opacity;
|
|
gdouble scale;
|
|
|
|
pr = NULL;
|
|
pattern = NULL;
|
|
|
|
/* Make sure we still have a source if we are doing image cloning */
|
|
if (type == IMAGE_CLONE)
|
|
{
|
|
if (!src_drawable)
|
|
return;
|
|
if (! (src_gimage = drawable_gimage (src_drawable)))
|
|
return;
|
|
/* Determine whether the source image has an alpha channel */
|
|
has_alpha = drawable_has_alpha (src_drawable);
|
|
}
|
|
|
|
/* We always need a destination image */
|
|
if (! (gimage = drawable_gimage (drawable)))
|
|
return;
|
|
|
|
if (pressure_options->size)
|
|
scale = paint_core->curpressure;
|
|
else
|
|
scale = 1.0;
|
|
|
|
/* Get a region which can be used to paint to */
|
|
if (! (area = paint_core_get_paint_area (paint_core, drawable, scale)))
|
|
return;
|
|
|
|
switch (type)
|
|
{
|
|
case IMAGE_CLONE:
|
|
/* Set the paint area to transparent */
|
|
memset (temp_buf_data (area), 0, area->width * area->height * area->bytes);
|
|
|
|
/* If the source gimage is different from the destination,
|
|
* then we should copy straight from the destination image
|
|
* to the canvas.
|
|
* Otherwise, we need a call to get_orig_image to make sure
|
|
* we get a copy of the unblemished (offset) image
|
|
*/
|
|
if (src_drawable != drawable)
|
|
{
|
|
x1 = BOUNDS (area->x + offset_x, 0, drawable_width (src_drawable));
|
|
y1 = BOUNDS (area->y + offset_y, 0, drawable_height (src_drawable));
|
|
x2 = BOUNDS (area->x + offset_x + area->width,
|
|
0, drawable_width (src_drawable));
|
|
y2 = BOUNDS (area->y + offset_y + area->height,
|
|
0, drawable_height (src_drawable));
|
|
|
|
if (!(x2 - x1) || !(y2 - y1))
|
|
return;
|
|
|
|
pixel_region_init (&srcPR, drawable_data (src_drawable), x1, y1, (x2 - x1), (y2 - y1), FALSE);
|
|
}
|
|
else
|
|
{
|
|
x1 = BOUNDS (area->x + offset_x, 0, drawable_width (drawable));
|
|
y1 = BOUNDS (area->y + offset_y, 0, drawable_height (drawable));
|
|
x2 = BOUNDS (area->x + offset_x + area->width,
|
|
0, drawable_width (drawable));
|
|
y2 = BOUNDS (area->y + offset_y + area->height,
|
|
0, drawable_height (drawable));
|
|
|
|
if (!(x2 - x1) || !(y2 - y1))
|
|
return;
|
|
|
|
/* get the original image */
|
|
orig = paint_core_get_orig_image (paint_core, drawable, x1, y1, x2, y2);
|
|
|
|
srcPR.bytes = orig->bytes;
|
|
srcPR.x = 0; srcPR.y = 0;
|
|
srcPR.w = x2 - x1;
|
|
srcPR.h = y2 - y1;
|
|
srcPR.rowstride = srcPR.bytes * orig->width;
|
|
srcPR.data = temp_buf_data (orig);
|
|
}
|
|
|
|
offset_x = x1 - (area->x + offset_x);
|
|
offset_y = y1 - (area->y + offset_y);
|
|
|
|
/* configure the destination */
|
|
destPR.bytes = area->bytes;
|
|
destPR.x = 0; destPR.y = 0;
|
|
destPR.w = srcPR.w;
|
|
destPR.h = srcPR.h;
|
|
destPR.rowstride = destPR.bytes * area->width;
|
|
destPR.data = temp_buf_data (area) + offset_y * destPR.rowstride +
|
|
offset_x * destPR.bytes;
|
|
|
|
pr = pixel_regions_register (2, &srcPR, &destPR);
|
|
break;
|
|
|
|
case PATTERN_CLONE:
|
|
pattern = gimp_context_get_pattern (NULL);
|
|
|
|
if (!pattern)
|
|
return;
|
|
|
|
destPR.bytes = area->bytes;
|
|
destPR.x = 0; destPR.y = 0;
|
|
destPR.w = area->width;
|
|
destPR.h = area->height;
|
|
destPR.rowstride = destPR.bytes * area->width;
|
|
destPR.data = temp_buf_data (area);
|
|
|
|
pr = pixel_regions_register (1, &destPR);
|
|
break;
|
|
}
|
|
|
|
for (; pr != NULL; pr = pixel_regions_process (pr))
|
|
{
|
|
s = srcPR.data;
|
|
d = destPR.data;
|
|
for (y = 0; y < destPR.h; y++)
|
|
{
|
|
switch (type)
|
|
{
|
|
case IMAGE_CLONE:
|
|
clone_line_image (gimage, src_gimage, drawable, src_drawable, s, d,
|
|
has_alpha, srcPR.bytes, destPR.bytes, destPR.w);
|
|
s += srcPR.rowstride;
|
|
break;
|
|
case PATTERN_CLONE:
|
|
clone_line_pattern (gimage, drawable, pattern, d,
|
|
area->x + offset_x, area->y + y + offset_y,
|
|
destPR.bytes, destPR.w);
|
|
break;
|
|
}
|
|
|
|
d += destPR.rowstride;
|
|
}
|
|
}
|
|
|
|
opacity = 255.0 * gimp_context_get_opacity (NULL);
|
|
if (pressure_options->opacity)
|
|
opacity = opacity * 2.0 * paint_core->curpressure;
|
|
|
|
/* paste the newly painted canvas to the gimage which is being worked on */
|
|
paint_core_paste_canvas (paint_core, drawable,
|
|
MIN (opacity, 255),
|
|
(int) (gimp_context_get_opacity (NULL) * 255),
|
|
gimp_context_get_paint_mode (NULL),
|
|
pressure_options->pressure ? PRESSURE : SOFT,
|
|
scale, CONSTANT);
|
|
}
|
|
|
|
|
|
static void
|
|
clone_line_image (GImage *dest,
|
|
GImage *src,
|
|
GimpDrawable *d_drawable,
|
|
GimpDrawable *s_drawable,
|
|
unsigned char *s,
|
|
unsigned char *d,
|
|
int has_alpha,
|
|
int src_bytes,
|
|
int dest_bytes,
|
|
int width)
|
|
{
|
|
unsigned char rgb[3];
|
|
int src_alpha, dest_alpha;
|
|
|
|
src_alpha = src_bytes - 1;
|
|
dest_alpha = dest_bytes - 1;
|
|
|
|
while (width--)
|
|
{
|
|
gimage_get_color (src, drawable_type (s_drawable), rgb, s);
|
|
gimage_transform_color (dest, d_drawable, rgb, d, RGB);
|
|
|
|
if (has_alpha)
|
|
d[dest_alpha] = s[src_alpha];
|
|
else
|
|
d[dest_alpha] = OPAQUE_OPACITY;
|
|
|
|
s += src_bytes;
|
|
d += dest_bytes;
|
|
}
|
|
}
|
|
|
|
static void
|
|
clone_line_pattern (GImage *dest,
|
|
GimpDrawable *drawable,
|
|
GPattern *pattern,
|
|
unsigned char *d,
|
|
int x,
|
|
int y,
|
|
int bytes,
|
|
int width)
|
|
{
|
|
unsigned char *pat, *p;
|
|
int color, alpha;
|
|
int i;
|
|
|
|
/* Make sure x, y are positive */
|
|
while (x < 0)
|
|
x += pattern->mask->width;
|
|
while (y < 0)
|
|
y += pattern->mask->height;
|
|
|
|
/* Get a pointer to the appropriate scanline of the pattern buffer */
|
|
pat = temp_buf_data (pattern->mask) +
|
|
(y % pattern->mask->height) * pattern->mask->width * pattern->mask->bytes;
|
|
color = (pattern->mask->bytes == 3) ? RGB : GRAY;
|
|
|
|
alpha = bytes - 1;
|
|
|
|
for (i = 0; i < width; i++)
|
|
{
|
|
p = pat + ((i + x) % pattern->mask->width) * pattern->mask->bytes;
|
|
|
|
gimage_transform_color (dest, drawable, p, d, color);
|
|
|
|
d[alpha] = OPAQUE_OPACITY;
|
|
|
|
d += bytes;
|
|
}
|
|
}
|
|
|
|
static void *
|
|
clone_non_gui_paint_func (PaintCore *paint_core,
|
|
GimpDrawable *drawable,
|
|
int state)
|
|
{
|
|
clone_motion (paint_core, drawable, non_gui_src_drawable, &non_gui_pressure_options,
|
|
non_gui_type, non_gui_offset_x, non_gui_offset_y);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
gboolean
|
|
clone_non_gui_default (GimpDrawable *drawable,
|
|
int num_strokes,
|
|
double *stroke_array)
|
|
{
|
|
GimpDrawable *src_drawable = NULL;
|
|
CloneType clone_type = CLONE_DEFAULT_TYPE;
|
|
double local_src_x = 0.0;
|
|
double local_src_y = 0.0;
|
|
CloneOptions *options = clone_options;
|
|
|
|
if(options)
|
|
{
|
|
clone_type = options->type;
|
|
src_drawable = src_drawable_;
|
|
local_src_x = src_x;
|
|
local_src_y = src_y;
|
|
}
|
|
|
|
return clone_non_gui(drawable,
|
|
src_drawable,
|
|
clone_type,
|
|
local_src_x,local_src_y,
|
|
num_strokes,stroke_array);
|
|
}
|
|
|
|
gboolean
|
|
clone_non_gui (GimpDrawable *drawable,
|
|
GimpDrawable *src_drawable,
|
|
CloneType clone_type,
|
|
double src_x,
|
|
double src_y,
|
|
int num_strokes,
|
|
double *stroke_array)
|
|
{
|
|
int i;
|
|
|
|
if (paint_core_init (&non_gui_paint_core, drawable,
|
|
stroke_array[0], stroke_array[1]))
|
|
{
|
|
/* Set the paint core's paint func */
|
|
non_gui_paint_core.paint_func = clone_non_gui_paint_func;
|
|
|
|
non_gui_type = clone_type;
|
|
|
|
non_gui_src_drawable = src_drawable;
|
|
|
|
non_gui_paint_core.startx = non_gui_paint_core.lastx = stroke_array[0];
|
|
non_gui_paint_core.starty = non_gui_paint_core.lasty = stroke_array[1];
|
|
|
|
non_gui_offset_x = (int) (src_x - non_gui_paint_core.startx);
|
|
non_gui_offset_y = (int) (src_y - non_gui_paint_core.starty);
|
|
|
|
clone_non_gui_paint_func (&non_gui_paint_core, drawable, 0);
|
|
|
|
for (i = 1; i < num_strokes; i++)
|
|
{
|
|
non_gui_paint_core.curx = stroke_array[i * 2 + 0];
|
|
non_gui_paint_core.cury = stroke_array[i * 2 + 1];
|
|
|
|
paint_core_interpolate (&non_gui_paint_core, drawable);
|
|
|
|
non_gui_paint_core.lastx = non_gui_paint_core.curx;
|
|
non_gui_paint_core.lasty = non_gui_paint_core.cury;
|
|
}
|
|
|
|
/* Finish the painting */
|
|
paint_core_finish (&non_gui_paint_core, drawable, -1);
|
|
|
|
/* Cleanup */
|
|
paint_core_cleanup ();
|
|
return TRUE;
|
|
}
|
|
else
|
|
return FALSE;
|
|
}
|