Immplement "Sample Merged" for the clone tool. Fixes bug #123627.

2005-08-28  Michael Natterer  <mitch@gimp.org>

	Immplement "Sample Merged" for the clone tool. Fixes bug #123627.

	* app/paint/gimppaintcore.[ch] (struct GimpPaintCore): added
	members "saved_proj_tiles" which stores the unmodified projection,
	"orig_proj_buf" which stores the unmodified temp paint application
	buf and "use_saved_proj" which controls if all the additional
	stuff should be allocated and managed.

	(gimp_paint_core_start): allocate the saved_proj_tiles if needed.

	(gimp_paint_core_get_orig_proj): new function like
	gimp_paint_core_get_orig_image() which returns unmodified
	projection pixels for paint application.

	(gimp_paint_core_validate_saved_proj_tiles): new function like
	gimp_paint_core_validate_undo_tiles() which copies the tiles that
	will be dirtied to saved_proj_tiles.

	(gimp_paint_core_paste): call above save_proj_tiles() so
	projection tiles are saved before dirtying them.

	* app/paint/gimpclone.[ch]: replaced member src_drawable by
	src_pickable and use the image's projection if sample_merged it
	TRUE. Adjust src offsets accordingly and use GimpPaintCore's new
	get_orig_proj() API to get the src pixels.

	* app/paint/gimpcloneoptions.[ch]: added boolean "sample_merged"
	property.

	* app/tools/gimpclonetool.c: follow GimpClone's src_drawable ->
	src_pickable change.

	(gimp_clone_tool_button_press): set the paint_core's
	"use_saved_proj" boolean before chaining up.

	(gimp_clone_options_gui): add a "Sample Merged" toggle button.
This commit is contained in:
Michael Natterer 2005-08-28 19:17:44 +00:00 committed by Michael Natterer
parent 19ea886842
commit 26374e814e
13 changed files with 536 additions and 141 deletions

View File

@ -1,3 +1,42 @@
2005-08-28 Michael Natterer <mitch@gimp.org>
Immplement "Sample Merged" for the clone tool. Fixes bug #123627.
* app/paint/gimppaintcore.[ch] (struct GimpPaintCore): added
members "saved_proj_tiles" which stores the unmodified projection,
"orig_proj_buf" which stores the unmodified temp paint application
buf and "use_saved_proj" which controls if all the additional
stuff should be allocated and managed.
(gimp_paint_core_start): allocate the saved_proj_tiles if needed.
(gimp_paint_core_get_orig_proj): new function like
gimp_paint_core_get_orig_image() which returns unmodified
projection pixels for paint application.
(gimp_paint_core_validate_saved_proj_tiles): new function like
gimp_paint_core_validate_undo_tiles() which copies the tiles that
will be dirtied to saved_proj_tiles.
(gimp_paint_core_paste): call above save_proj_tiles() so
projection tiles are saved before dirtying them.
* app/paint/gimpclone.[ch]: replaced member src_drawable by
src_pickable and use the image's projection if sample_merged it
TRUE. Adjust src offsets accordingly and use GimpPaintCore's new
get_orig_proj() API to get the src pixels.
* app/paint/gimpcloneoptions.[ch]: added boolean "sample_merged"
property.
* app/tools/gimpclonetool.c: follow GimpClone's src_drawable ->
src_pickable change.
(gimp_clone_tool_button_press): set the paint_core's
"use_saved_proj" boolean before chaining up.
(gimp_clone_options_gui): add a "Sample Merged" toggle button.
2005-08-28 Manish Singh <yosh@gimp.org>
* m4macros/pythondev.m4: python headers on Win32 don't live in a

View File

@ -27,6 +27,7 @@
#include "base/pixel-region.h"
#include "base/temp-buf.h"
#include "base/tile-manager.h"
#include "paint-funcs/paint-funcs.h"
@ -34,6 +35,8 @@
#include "core/gimpdrawable.h"
#include "core/gimpimage.h"
#include "core/gimppattern.h"
#include "core/gimppickable.h"
#include "core/gimpprojection.h"
#include "gimpclone.h"
#include "gimpcloneoptions.h"
@ -56,10 +59,9 @@ static void gimp_clone_motion (GimpPaintCore *paint_core,
static void gimp_clone_line_image (GimpImage *dest,
GimpImage *src,
GimpDrawable *d_drawable,
GimpDrawable *s_drawable,
GimpPickable *s_pickable,
guchar *s,
guchar *d,
gint has_alpha,
gint src_bytes,
gint dest_bytes,
gint width);
@ -72,8 +74,8 @@ static void gimp_clone_line_pattern (GimpImage *dest,
gint bytes,
gint width);
static void gimp_clone_set_src_drawable (GimpClone *clone,
GimpDrawable *drawable);
static void gimp_clone_set_src_pickable (GimpClone *clone,
GimpPickable *pickable);
static GimpBrushCoreClass *parent_class = NULL;
@ -135,7 +137,7 @@ gimp_clone_init (GimpClone *clone)
{
clone->set_source = FALSE;
clone->src_drawable = NULL;
clone->src_pickable = NULL;
clone->src_x = 0.0;
clone->src_y = 0.0;
@ -163,11 +165,28 @@ gimp_clone_paint (GimpPaintCore *paint_core,
case GIMP_PAINT_STATE_INIT:
if (clone->set_source)
{
gimp_clone_set_src_drawable (clone, drawable);
clone->src_x = paint_core->cur_coords.x;
clone->src_y = paint_core->cur_coords.y;
if (options->sample_merged)
{
GimpItem *item = GIMP_ITEM (drawable);
GimpImage *gimage = gimp_item_get_image (item);
gint off_x, off_y;
gimp_item_offsets (item, &off_x, &off_y);
clone->src_x += off_x;
clone->src_y += off_y;
gimp_clone_set_src_pickable (clone,
GIMP_PICKABLE (gimage->projection));
}
else
{
gimp_clone_set_src_pickable (clone, GIMP_PICKABLE (drawable));
}
clone->first_stroke = TRUE;
}
else if (options->align_mode == GIMP_CLONE_ALIGN_NO)
@ -191,6 +210,16 @@ gimp_clone_paint (GimpPaintCore *paint_core,
clone->src_x = paint_core->cur_coords.x;
clone->src_y = paint_core->cur_coords.y;
if (options->sample_merged)
{
gint off_x, off_y;
gimp_item_offsets (GIMP_ITEM (drawable), &off_x, &off_y);
clone->src_x += off_x;
clone->src_y += off_y;
}
clone->first_stroke = TRUE;
}
else
@ -253,7 +282,7 @@ gimp_clone_motion (GimpPaintCore *paint_core,
gpointer pr = NULL;
gint y;
gint x1, y1, x2, y2;
gint has_alpha = -1;
TileManager *src_tiles;
PixelRegion srcPR, destPR;
GimpPattern *pattern = NULL;
gdouble opacity;
@ -274,14 +303,13 @@ gimp_clone_motion (GimpPaintCore *paint_core,
/* Make sure we still have a source if we are doing image cloning */
if (options->clone_type == GIMP_IMAGE_CLONE)
{
if (! clone->src_drawable)
if (! clone->src_pickable)
return;
if (! (src_gimage = gimp_item_get_image (GIMP_ITEM (clone->src_drawable))))
return;
src_gimage = gimp_pickable_get_image (clone->src_pickable);
/* Determine whether the source image has an alpha channel */
has_alpha = gimp_drawable_has_alpha (clone->src_drawable);
if (! src_gimage)
return;
}
area = gimp_paint_core_get_paint_area (paint_core, drawable, paint_options);
@ -294,14 +322,16 @@ gimp_clone_motion (GimpPaintCore *paint_core,
/* Set the paint area to transparent */
temp_buf_data_clear (area);
src_tiles = gimp_pickable_get_tiles (clone->src_pickable);
x1 = CLAMP (area->x + offset_x,
0, gimp_item_width (GIMP_ITEM (clone->src_drawable)));
0, tile_manager_width (src_tiles));
y1 = CLAMP (area->y + offset_y,
0, gimp_item_height (GIMP_ITEM (clone->src_drawable)));
0, tile_manager_height (src_tiles));
x2 = CLAMP (area->x + offset_x + area->width,
0, gimp_item_width (GIMP_ITEM (clone->src_drawable)));
0, tile_manager_width (src_tiles));
y2 = CLAMP (area->y + offset_y + area->height,
0, gimp_item_height (GIMP_ITEM (clone->src_drawable)));
0, tile_manager_height (src_tiles));
if (!(x2 - x1) || !(y2 - y1))
return;
@ -312,9 +342,11 @@ gimp_clone_motion (GimpPaintCore *paint_core,
* Otherwise, we need a call to get_orig_image to make sure
* we get a copy of the unblemished (offset) image
*/
if (clone->src_drawable != drawable)
if (( options->sample_merged && (gimage != src_gimage)) ||
(! options->sample_merged && (clone->src_pickable !=
(GimpPickable *) drawable)))
{
pixel_region_init (&srcPR, gimp_drawable_data (clone->src_drawable),
pixel_region_init (&srcPR, src_tiles,
x1, y1, (x2 - x1), (y2 - y1), FALSE);
}
else
@ -322,8 +354,14 @@ gimp_clone_motion (GimpPaintCore *paint_core,
TempBuf *orig;
/* get the original image */
orig = gimp_paint_core_get_orig_image (paint_core, clone->src_drawable,
x1, y1, x2, y2);
if (options->sample_merged)
orig = gimp_paint_core_get_orig_proj (paint_core,
clone->src_pickable,
x1, y1, x2, y2);
else
orig = gimp_paint_core_get_orig_image (paint_core,
GIMP_DRAWABLE (clone->src_pickable),
x1, y1, x2, y2);
srcPR.bytes = orig->bytes;
srcPR.x = 0;
@ -381,8 +419,8 @@ gimp_clone_motion (GimpPaintCore *paint_core,
{
case GIMP_IMAGE_CLONE:
gimp_clone_line_image (gimage, src_gimage,
drawable, clone->src_drawable,
s, d, has_alpha,
drawable, clone->src_pickable,
s, d,
srcPR.bytes, destPR.bytes, destPR.w);
s += srcPR.rowstride;
break;
@ -415,10 +453,9 @@ static void
gimp_clone_line_image (GimpImage *dest,
GimpImage *src,
GimpDrawable *d_drawable,
GimpDrawable *s_drawable,
GimpPickable *s_pickable,
guchar *s,
guchar *d,
gint has_alpha,
gint src_bytes,
gint dest_bytes,
gint width)
@ -430,7 +467,8 @@ gimp_clone_line_image (GimpImage *dest,
while (width--)
{
gimp_image_get_color (src, gimp_drawable_type (s_drawable), s, rgba);
gimp_image_get_color (src, gimp_pickable_get_image_type (s_pickable),
s, rgba);
gimp_image_transform_color (dest, d_drawable, d, GIMP_RGB, rgba);
d[alpha] = rgba[ALPHA_PIX];
@ -489,33 +527,31 @@ gimp_clone_line_pattern (GimpImage *dest,
}
static void
gimp_clone_src_drawable_disconnect_cb (GimpDrawable *drawable,
gimp_clone_src_pickable_disconnect_cb (GimpPickable *pickable,
GimpClone *clone)
{
if (drawable == clone->src_drawable)
if (pickable == clone->src_pickable)
{
clone->src_drawable = NULL;
clone->src_pickable = NULL;
}
}
static void
gimp_clone_set_src_drawable (GimpClone *clone,
GimpDrawable *drawable)
gimp_clone_set_src_pickable (GimpClone *clone,
GimpPickable *pickable)
{
if (clone->src_drawable == drawable)
if (clone->src_pickable == pickable)
return;
if (clone->src_drawable)
g_signal_handlers_disconnect_by_func (clone->src_drawable,
gimp_clone_src_drawable_disconnect_cb,
if (clone->src_pickable)
g_signal_handlers_disconnect_by_func (clone->src_pickable,
gimp_clone_src_pickable_disconnect_cb,
clone);
clone->src_drawable = drawable;
clone->src_pickable = pickable;
if (clone->src_drawable)
{
g_signal_connect (clone->src_drawable, "disconnect",
G_CALLBACK (gimp_clone_src_drawable_disconnect_cb),
clone);
}
if (clone->src_pickable)
g_signal_connect (clone->src_pickable, "disconnect",
G_CALLBACK (gimp_clone_src_pickable_disconnect_cb),
clone);
}

View File

@ -40,7 +40,7 @@ struct _GimpClone
gboolean set_source;
GimpDrawable *src_drawable;
GimpPickable *src_pickable;
gint src_x;
gint src_y;

View File

@ -35,7 +35,8 @@ enum
{
PROP_0,
PROP_CLONE_TYPE,
PROP_ALIGN_MODE
PROP_ALIGN_MODE,
PROP_SAMPLE_MERGED
};
@ -103,6 +104,10 @@ gimp_clone_options_class_init (GimpCloneOptionsClass *klass)
GIMP_TYPE_CLONE_ALIGN_MODE,
CLONE_DEFAULT_ALIGN_MODE,
0);
GIMP_CONFIG_INSTALL_PROP_BOOLEAN (object_class, PROP_SAMPLE_MERGED,
"sample-merged", NULL,
FALSE,
0);
}
static void
@ -121,6 +126,9 @@ gimp_clone_options_set_property (GObject *object,
case PROP_ALIGN_MODE:
options->align_mode = g_value_get_enum (value);
break;
case PROP_SAMPLE_MERGED:
options->sample_merged = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
@ -143,6 +151,9 @@ gimp_clone_options_get_property (GObject *object,
case PROP_ALIGN_MODE:
g_value_set_enum (value, options->align_mode);
break;
case PROP_SAMPLE_MERGED:
g_value_set_boolean (value, options->sample_merged);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;

View File

@ -40,6 +40,7 @@ struct _GimpCloneOptions
GimpCloneType clone_type;
GimpCloneAlignMode align_mode;
gboolean sample_merged;
};

View File

@ -37,6 +37,7 @@
#include "core/gimpimage.h"
#include "core/gimpimage-undo.h"
#include "core/gimppaintinfo.h"
#include "core/gimppickable.h"
#include "gimppaintcore.h"
#include "gimppaintcore-undo.h"
@ -144,22 +145,25 @@ gimp_paint_core_class_init (GimpPaintCoreClass *klass)
static void
gimp_paint_core_init (GimpPaintCore *core)
{
core->ID = global_core_ID++;
core->ID = global_core_ID++;
core->distance = 0.0;
core->pixel_dist = 0.0;
core->x1 = 0;
core->y1 = 0;
core->x2 = 0;
core->y2 = 0;
core->distance = 0.0;
core->pixel_dist = 0.0;
core->x1 = 0;
core->y1 = 0;
core->x2 = 0;
core->y2 = 0;
core->use_pressure = FALSE;
core->use_pressure = FALSE;
core->use_saved_proj = FALSE;
core->undo_tiles = NULL;
core->canvas_tiles = NULL;
core->undo_tiles = NULL;
core->saved_proj_tiles = NULL;
core->canvas_tiles = NULL;
core->orig_buf = NULL;
core->canvas_buf = NULL;
core->orig_buf = NULL;
core->orig_proj_buf = NULL;
core->canvas_buf = NULL;
}
static void
@ -299,6 +303,23 @@ gimp_paint_core_start (GimpPaintCore *core,
gimp_item_height (item),
gimp_drawable_bytes (drawable));
/* Allocate the saved proj structure */
if (core->saved_proj_tiles)
tile_manager_unref (core->saved_proj_tiles);
if (core->use_saved_proj)
{
GimpPickable *pickable;
TileManager *tiles;
pickable = GIMP_PICKABLE (gimp_item_get_image (item)->projection);
tiles = gimp_pickable_get_tiles (pickable);
core->saved_proj_tiles = tile_manager_new (tile_manager_width (tiles),
tile_manager_height (tiles),
tile_manager_bpp (tiles));
}
/* Allocate the canvas blocks structure */
if (core->canvas_tiles)
tile_manager_unref (core->canvas_tiles);
@ -357,6 +378,12 @@ gimp_paint_core_finish (GimpPaintCore *core,
gimp_image_undo_group_end (gimage);
if (core->saved_proj_tiles)
{
tile_manager_unref (core->saved_proj_tiles);
core->saved_proj_tiles = NULL;
}
/* invalidate the previews -- have to do it here, because
* it is not done during the actual painting.
*/
@ -418,6 +445,12 @@ gimp_paint_core_cancel (GimpPaintCore *core,
tile_manager_unref (core->undo_tiles);
core->undo_tiles = NULL;
if (core->saved_proj_tiles)
{
tile_manager_unref (core->saved_proj_tiles);
core->saved_proj_tiles = NULL;
}
gimp_drawable_update (drawable,
core->x1, core->y1,
core->x2 - core->x1, core->y2 - core->y1);
@ -434,6 +467,12 @@ gimp_paint_core_cleanup (GimpPaintCore *core)
core->undo_tiles = NULL;
}
if (core->saved_proj_tiles)
{
tile_manager_unref (core->saved_proj_tiles);
core->saved_proj_tiles = NULL;
}
if (core->canvas_tiles)
{
tile_manager_unref (core->canvas_tiles);
@ -446,6 +485,12 @@ gimp_paint_core_cleanup (GimpPaintCore *core)
core->orig_buf = NULL;
}
if (core->orig_proj_buf)
{
temp_buf_free (core->orig_proj_buf);
core->orig_proj_buf = NULL;
}
if (core->canvas_buf)
{
temp_buf_free (core->canvas_buf);
@ -562,11 +607,14 @@ gimp_paint_core_get_orig_image (GimpPaintCore *core,
}
d = destPR.data;
pixelwidth = srcPR.w * srcPR.bytes;
h = srcPR.h;
while (h --)
{
memcpy (d, s, pixelwidth);
s += srcPR.rowstride;
d += destPR.rowstride;
}
@ -578,6 +626,105 @@ gimp_paint_core_get_orig_image (GimpPaintCore *core,
return core->orig_buf;
}
TempBuf *
gimp_paint_core_get_orig_proj (GimpPaintCore *core,
GimpPickable *pickable,
gint x1,
gint y1,
gint x2,
gint y2)
{
TileManager *src_tiles;
PixelRegion srcPR;
PixelRegion destPR;
Tile *saved_tile;
gboolean release_tile;
gint h;
gint pixelwidth;
gint pickable_width;
gint pickable_height;
guchar *s;
guchar *d;
gpointer pr;
src_tiles = gimp_pickable_get_tiles (pickable);
core->orig_proj_buf = temp_buf_resize (core->orig_proj_buf,
tile_manager_bpp (src_tiles),
x1, y1,
(x2 - x1), (y2 - y1));
pickable_width = tile_manager_width (src_tiles);
pickable_height = tile_manager_height (src_tiles);
x1 = CLAMP (x1, 0, pickable_width);
y1 = CLAMP (y1, 0, pickable_height);
x2 = CLAMP (x2, 0, pickable_width);
y2 = CLAMP (y2, 0, pickable_height);
/* configure the pixel regions */
pixel_region_init (&srcPR, src_tiles,
x1, y1,
(x2 - x1), (y2 - y1),
FALSE);
destPR.bytes = core->orig_proj_buf->bytes;
destPR.x = 0;
destPR.y = 0;
destPR.w = (x2 - x1);
destPR.h = (y2 - y1);
destPR.rowstride = core->orig_proj_buf->bytes * core->orig_proj_buf->width;
destPR.data = (temp_buf_data (core->orig_proj_buf) +
(y1 - core->orig_proj_buf->y) * destPR.rowstride +
(x1 - core->orig_proj_buf->x) * destPR.bytes);
for (pr = pixel_regions_register (2, &srcPR, &destPR);
pr != NULL;
pr = pixel_regions_process (pr))
{
/* If the saved tile corresponding to this location is valid, use it */
saved_tile = tile_manager_get_tile (core->saved_proj_tiles,
srcPR.x, srcPR.y,
FALSE, FALSE);
if (tile_is_valid (saved_tile))
{
release_tile = TRUE;
saved_tile = tile_manager_get_tile (core->saved_proj_tiles,
srcPR.x, srcPR.y,
TRUE, FALSE);
s = ((guchar *) tile_data_pointer (saved_tile, 0, 0) +
srcPR.rowstride * (srcPR.y % TILE_HEIGHT) +
srcPR.bytes * (srcPR.x % TILE_WIDTH)); /* dubious... */
}
else
{
release_tile = FALSE;
s = srcPR.data;
}
d = destPR.data;
pixelwidth = srcPR.w * srcPR.bytes;
h = srcPR.h;
while (h --)
{
memcpy (d, s, pixelwidth);
s += srcPR.rowstride;
d += destPR.rowstride;
}
if (release_tile)
tile_release (saved_tile, FALSE);
}
return core->orig_proj_buf;
}
void
gimp_paint_core_paste (GimpPaintCore *core,
PixelRegion *paint_maskPR,
@ -602,6 +749,21 @@ gimp_paint_core_paste (GimpPaintCore *core,
core->canvas_buf->width,
core->canvas_buf->height);
if (core->use_saved_proj)
{
GimpPickable *pickable = GIMP_PICKABLE (gimage->projection);
gint off_x;
gint off_y;
gimp_item_offsets (GIMP_ITEM (drawable), &off_x, &off_y);
gimp_paint_core_validate_saved_proj_tiles (core, pickable,
core->canvas_buf->x + off_x,
core->canvas_buf->y + off_y,
core->canvas_buf->width,
core->canvas_buf->height);
}
/* If the mode is CONSTANT:
* combine the canvas buf, the paint mask to the canvas tiles
*/
@ -876,6 +1038,51 @@ gimp_paint_core_validate_undo_tiles (GimpPaintCore *core,
}
}
void
gimp_paint_core_validate_saved_proj_tiles (GimpPaintCore *core,
GimpPickable *pickable,
gint x,
gint y,
gint w,
gint h)
{
gint i;
gint j;
Tile *src_tile;
Tile *dest_tile;
if (! core->saved_proj_tiles)
{
g_warning ("set_saved_proj_tiles: saved_proj_tiles is null");
return;
}
for (i = y; i < (y + h); i += (TILE_HEIGHT - (i % TILE_HEIGHT)))
{
for (j = x; j < (x + w); j += (TILE_WIDTH - (j % TILE_WIDTH)))
{
dest_tile = tile_manager_get_tile (core->saved_proj_tiles, j, i,
FALSE, FALSE);
if (! tile_is_valid (dest_tile))
{
dest_tile = tile_manager_get_tile (core->saved_proj_tiles, j, i,
TRUE, TRUE);
src_tile = tile_manager_get_tile (gimp_pickable_get_tiles (pickable),
j, i, TRUE, FALSE);
/* copy the pixels instead of mapping the tile because
* copy-on-write from the projection is broken
*/
memcpy (tile_data_pointer (dest_tile, 0, 0),
tile_data_pointer (src_tile, 0, 0),
tile_size (src_tile));
tile_release (dest_tile, TRUE);
tile_release (src_tile, FALSE);
}
}
}
}
void
gimp_paint_core_validate_canvas_tiles (GimpPaintCore *core,
gint x,

View File

@ -37,28 +37,31 @@ struct _GimpPaintCore
{
GimpObject parent_instance;
gint ID; /* unique instance ID */
gint ID; /* unique instance ID */
GimpCoords start_coords; /* starting coords (for undo only) */
GimpCoords start_coords; /* starting coords (for undo only) */
GimpCoords cur_coords; /* current coords */
GimpCoords last_coords; /* last coords */
GimpCoords cur_coords; /* current coords */
GimpCoords last_coords; /* last coords */
GimpVector2 last_paint; /* last point that was painted */
GimpVector2 last_paint; /* last point that was painted */
gdouble distance; /* distance traveled by brush */
gdouble pixel_dist; /* distance in pixels */
gdouble distance; /* distance traveled by brush */
gdouble pixel_dist; /* distance in pixels */
gint x1, y1; /* undo extents in image coords */
gint x2, y2; /* undo extents in image coords */
gint x1, y1; /* undo extents in image coords */
gint x2, y2; /* undo extents in image coords */
gboolean use_pressure; /* look at coords->pressure */
gboolean use_pressure; /* look at coords->pressure */
gboolean use_saved_proj; /* keep the unmodified proj around */
TileManager *undo_tiles; /* tiles which have been modified */
TileManager *canvas_tiles; /* the buffer to paint the mask to */
TileManager *undo_tiles; /* tiles which have been modified */
TileManager *saved_proj_tiles; /* proj tiles which have been modified */
TileManager *canvas_tiles; /* the buffer to paint the mask to */
TempBuf *orig_buf; /* the unmodified drawable pixels */
TempBuf *canvas_buf; /* the buffer to paint pixels to */
TempBuf *orig_buf; /* the unmodified drawable pixels */
TempBuf *orig_proj_buf; /* the unmodified projection pixels */
TempBuf *canvas_buf; /* the buffer to paint pixels to */
};
@ -138,6 +141,12 @@ TempBuf * gimp_paint_core_get_orig_image (GimpPaintCore *core,
gint y1,
gint x2,
gint y2);
TempBuf * gimp_paint_core_get_orig_proj (GimpPaintCore *core,
GimpPickable *pickable,
gint x1,
gint y1,
gint x2,
gint y2);
void gimp_paint_core_paste (GimpPaintCore *core,
PixelRegion *paint_maskPR,
@ -153,17 +162,23 @@ void gimp_paint_core_replace (GimpPaintCore *core,
gdouble image_opacity,
GimpPaintApplicationMode mode);
void gimp_paint_core_validate_undo_tiles (GimpPaintCore *core,
GimpDrawable *drawable,
gint x,
gint y,
gint w,
gint h);
void gimp_paint_core_validate_canvas_tiles (GimpPaintCore *core,
gint x,
gint y,
gint w,
gint h);
void gimp_paint_core_validate_undo_tiles (GimpPaintCore *core,
GimpDrawable *drawable,
gint x,
gint y,
gint w,
gint h);
void gimp_paint_core_validate_saved_proj_tiles (GimpPaintCore *core,
GimpPickable *pickable,
gint x,
gint y,
gint w,
gint h);
void gimp_paint_core_validate_canvas_tiles (GimpPaintCore *core,
gint x,
gint y,
gint w,
gint h);
#endif /* __GIMP_PAINT_CORE_H__ */

View File

@ -27,6 +27,7 @@
#include "base/pixel-region.h"
#include "base/temp-buf.h"
#include "base/tile-manager.h"
#include "paint-funcs/paint-funcs.h"
@ -34,6 +35,8 @@
#include "core/gimpdrawable.h"
#include "core/gimpimage.h"
#include "core/gimppattern.h"
#include "core/gimppickable.h"
#include "core/gimpprojection.h"
#include "gimpclone.h"
#include "gimpcloneoptions.h"
@ -56,10 +59,9 @@ static void gimp_clone_motion (GimpPaintCore *paint_core,
static void gimp_clone_line_image (GimpImage *dest,
GimpImage *src,
GimpDrawable *d_drawable,
GimpDrawable *s_drawable,
GimpPickable *s_pickable,
guchar *s,
guchar *d,
gint has_alpha,
gint src_bytes,
gint dest_bytes,
gint width);
@ -72,8 +74,8 @@ static void gimp_clone_line_pattern (GimpImage *dest,
gint bytes,
gint width);
static void gimp_clone_set_src_drawable (GimpClone *clone,
GimpDrawable *drawable);
static void gimp_clone_set_src_pickable (GimpClone *clone,
GimpPickable *pickable);
static GimpBrushCoreClass *parent_class = NULL;
@ -135,7 +137,7 @@ gimp_clone_init (GimpClone *clone)
{
clone->set_source = FALSE;
clone->src_drawable = NULL;
clone->src_pickable = NULL;
clone->src_x = 0.0;
clone->src_y = 0.0;
@ -163,11 +165,28 @@ gimp_clone_paint (GimpPaintCore *paint_core,
case GIMP_PAINT_STATE_INIT:
if (clone->set_source)
{
gimp_clone_set_src_drawable (clone, drawable);
clone->src_x = paint_core->cur_coords.x;
clone->src_y = paint_core->cur_coords.y;
if (options->sample_merged)
{
GimpItem *item = GIMP_ITEM (drawable);
GimpImage *gimage = gimp_item_get_image (item);
gint off_x, off_y;
gimp_item_offsets (item, &off_x, &off_y);
clone->src_x += off_x;
clone->src_y += off_y;
gimp_clone_set_src_pickable (clone,
GIMP_PICKABLE (gimage->projection));
}
else
{
gimp_clone_set_src_pickable (clone, GIMP_PICKABLE (drawable));
}
clone->first_stroke = TRUE;
}
else if (options->align_mode == GIMP_CLONE_ALIGN_NO)
@ -191,6 +210,16 @@ gimp_clone_paint (GimpPaintCore *paint_core,
clone->src_x = paint_core->cur_coords.x;
clone->src_y = paint_core->cur_coords.y;
if (options->sample_merged)
{
gint off_x, off_y;
gimp_item_offsets (GIMP_ITEM (drawable), &off_x, &off_y);
clone->src_x += off_x;
clone->src_y += off_y;
}
clone->first_stroke = TRUE;
}
else
@ -253,7 +282,7 @@ gimp_clone_motion (GimpPaintCore *paint_core,
gpointer pr = NULL;
gint y;
gint x1, y1, x2, y2;
gint has_alpha = -1;
TileManager *src_tiles;
PixelRegion srcPR, destPR;
GimpPattern *pattern = NULL;
gdouble opacity;
@ -274,14 +303,13 @@ gimp_clone_motion (GimpPaintCore *paint_core,
/* Make sure we still have a source if we are doing image cloning */
if (options->clone_type == GIMP_IMAGE_CLONE)
{
if (! clone->src_drawable)
if (! clone->src_pickable)
return;
if (! (src_gimage = gimp_item_get_image (GIMP_ITEM (clone->src_drawable))))
return;
src_gimage = gimp_pickable_get_image (clone->src_pickable);
/* Determine whether the source image has an alpha channel */
has_alpha = gimp_drawable_has_alpha (clone->src_drawable);
if (! src_gimage)
return;
}
area = gimp_paint_core_get_paint_area (paint_core, drawable, paint_options);
@ -294,14 +322,16 @@ gimp_clone_motion (GimpPaintCore *paint_core,
/* Set the paint area to transparent */
temp_buf_data_clear (area);
src_tiles = gimp_pickable_get_tiles (clone->src_pickable);
x1 = CLAMP (area->x + offset_x,
0, gimp_item_width (GIMP_ITEM (clone->src_drawable)));
0, tile_manager_width (src_tiles));
y1 = CLAMP (area->y + offset_y,
0, gimp_item_height (GIMP_ITEM (clone->src_drawable)));
0, tile_manager_height (src_tiles));
x2 = CLAMP (area->x + offset_x + area->width,
0, gimp_item_width (GIMP_ITEM (clone->src_drawable)));
0, tile_manager_width (src_tiles));
y2 = CLAMP (area->y + offset_y + area->height,
0, gimp_item_height (GIMP_ITEM (clone->src_drawable)));
0, tile_manager_height (src_tiles));
if (!(x2 - x1) || !(y2 - y1))
return;
@ -312,9 +342,11 @@ gimp_clone_motion (GimpPaintCore *paint_core,
* Otherwise, we need a call to get_orig_image to make sure
* we get a copy of the unblemished (offset) image
*/
if (clone->src_drawable != drawable)
if (( options->sample_merged && (gimage != src_gimage)) ||
(! options->sample_merged && (clone->src_pickable !=
(GimpPickable *) drawable)))
{
pixel_region_init (&srcPR, gimp_drawable_data (clone->src_drawable),
pixel_region_init (&srcPR, src_tiles,
x1, y1, (x2 - x1), (y2 - y1), FALSE);
}
else
@ -322,8 +354,14 @@ gimp_clone_motion (GimpPaintCore *paint_core,
TempBuf *orig;
/* get the original image */
orig = gimp_paint_core_get_orig_image (paint_core, clone->src_drawable,
x1, y1, x2, y2);
if (options->sample_merged)
orig = gimp_paint_core_get_orig_proj (paint_core,
clone->src_pickable,
x1, y1, x2, y2);
else
orig = gimp_paint_core_get_orig_image (paint_core,
GIMP_DRAWABLE (clone->src_pickable),
x1, y1, x2, y2);
srcPR.bytes = orig->bytes;
srcPR.x = 0;
@ -381,8 +419,8 @@ gimp_clone_motion (GimpPaintCore *paint_core,
{
case GIMP_IMAGE_CLONE:
gimp_clone_line_image (gimage, src_gimage,
drawable, clone->src_drawable,
s, d, has_alpha,
drawable, clone->src_pickable,
s, d,
srcPR.bytes, destPR.bytes, destPR.w);
s += srcPR.rowstride;
break;
@ -415,10 +453,9 @@ static void
gimp_clone_line_image (GimpImage *dest,
GimpImage *src,
GimpDrawable *d_drawable,
GimpDrawable *s_drawable,
GimpPickable *s_pickable,
guchar *s,
guchar *d,
gint has_alpha,
gint src_bytes,
gint dest_bytes,
gint width)
@ -430,7 +467,8 @@ gimp_clone_line_image (GimpImage *dest,
while (width--)
{
gimp_image_get_color (src, gimp_drawable_type (s_drawable), s, rgba);
gimp_image_get_color (src, gimp_pickable_get_image_type (s_pickable),
s, rgba);
gimp_image_transform_color (dest, d_drawable, d, GIMP_RGB, rgba);
d[alpha] = rgba[ALPHA_PIX];
@ -489,33 +527,31 @@ gimp_clone_line_pattern (GimpImage *dest,
}
static void
gimp_clone_src_drawable_disconnect_cb (GimpDrawable *drawable,
gimp_clone_src_pickable_disconnect_cb (GimpPickable *pickable,
GimpClone *clone)
{
if (drawable == clone->src_drawable)
if (pickable == clone->src_pickable)
{
clone->src_drawable = NULL;
clone->src_pickable = NULL;
}
}
static void
gimp_clone_set_src_drawable (GimpClone *clone,
GimpDrawable *drawable)
gimp_clone_set_src_pickable (GimpClone *clone,
GimpPickable *pickable)
{
if (clone->src_drawable == drawable)
if (clone->src_pickable == pickable)
return;
if (clone->src_drawable)
g_signal_handlers_disconnect_by_func (clone->src_drawable,
gimp_clone_src_drawable_disconnect_cb,
if (clone->src_pickable)
g_signal_handlers_disconnect_by_func (clone->src_pickable,
gimp_clone_src_pickable_disconnect_cb,
clone);
clone->src_drawable = drawable;
clone->src_pickable = pickable;
if (clone->src_drawable)
{
g_signal_connect (clone->src_drawable, "disconnect",
G_CALLBACK (gimp_clone_src_drawable_disconnect_cb),
clone);
}
if (clone->src_pickable)
g_signal_connect (clone->src_pickable, "disconnect",
G_CALLBACK (gimp_clone_src_pickable_disconnect_cb),
clone);
}

View File

@ -40,7 +40,7 @@ struct _GimpClone
gboolean set_source;
GimpDrawable *src_drawable;
GimpPickable *src_pickable;
gint src_x;
gint src_y;

View File

@ -35,7 +35,8 @@ enum
{
PROP_0,
PROP_CLONE_TYPE,
PROP_ALIGN_MODE
PROP_ALIGN_MODE,
PROP_SAMPLE_MERGED
};
@ -103,6 +104,10 @@ gimp_clone_options_class_init (GimpCloneOptionsClass *klass)
GIMP_TYPE_CLONE_ALIGN_MODE,
CLONE_DEFAULT_ALIGN_MODE,
0);
GIMP_CONFIG_INSTALL_PROP_BOOLEAN (object_class, PROP_SAMPLE_MERGED,
"sample-merged", NULL,
FALSE,
0);
}
static void
@ -121,6 +126,9 @@ gimp_clone_options_set_property (GObject *object,
case PROP_ALIGN_MODE:
options->align_mode = g_value_get_enum (value);
break;
case PROP_SAMPLE_MERGED:
options->sample_merged = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
@ -143,6 +151,9 @@ gimp_clone_options_get_property (GObject *object,
case PROP_ALIGN_MODE:
g_value_set_enum (value, options->align_mode);
break;
case PROP_SAMPLE_MERGED:
g_value_set_boolean (value, options->sample_merged);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;

View File

@ -40,6 +40,7 @@ struct _GimpCloneOptions
GimpCloneType clone_type;
GimpCloneAlignMode align_mode;
gboolean sample_merged;
};

View File

@ -160,8 +160,13 @@ gimp_clone_tool_button_press (GimpTool *tool,
GdkModifierType state,
GimpDisplay *gdisp)
{
GimpPaintTool *paint_tool = GIMP_PAINT_TOOL (tool);
GimpCloneTool *clone_tool = GIMP_CLONE_TOOL (tool);
GimpPaintTool *paint_tool = GIMP_PAINT_TOOL (tool);
GimpCloneTool *clone_tool = GIMP_CLONE_TOOL (tool);
GimpCloneOptions *options;
options = GIMP_CLONE_OPTIONS (tool->tool_info->tool_options);
paint_tool->core->use_saved_proj = FALSE;
if ((state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == GDK_CONTROL_MASK)
{
@ -181,6 +186,13 @@ gimp_clone_tool_button_press (GimpTool *tool,
else
{
GIMP_CLONE (paint_tool->core)->set_source = FALSE;
if (options->clone_type == GIMP_IMAGE_CLONE &&
options->sample_merged &&
gdisp == clone_tool->src_gdisp)
{
paint_tool->core->use_saved_proj = TRUE;
}
}
GIMP_TOOL_CLASS (parent_class)->button_press (tool, coords, time, state,
@ -232,7 +244,7 @@ gimp_clone_tool_cursor_update (GimpTool *tool,
{
if ((state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == GDK_CONTROL_MASK)
ctype = GIMP_CURSOR_CROSSHAIR_SMALL;
else if (! GIMP_CLONE (GIMP_PAINT_TOOL (tool)->core)->src_drawable)
else if (! GIMP_CLONE (GIMP_PAINT_TOOL (tool)->core)->src_pickable)
ctype = GIMP_CURSOR_BAD;
}
@ -252,7 +264,7 @@ gimp_clone_tool_oper_update (GimpTool *tool,
GIMP_TOOL_CLASS (parent_class)->oper_update (tool, coords, state, gdisp);
if (GIMP_CLONE_OPTIONS (options)->clone_type == GIMP_IMAGE_CLONE &&
GIMP_CLONE (GIMP_PAINT_TOOL (tool)->core)->src_drawable == NULL)
GIMP_CLONE (GIMP_PAINT_TOOL (tool)->core)->src_pickable == NULL)
{
gimp_tool_replace_status (tool, gdisp,
_("Ctrl-Click to set a clone source."));
@ -271,14 +283,15 @@ gimp_clone_tool_draw (GimpDrawTool *draw_tool)
options = (GimpCloneOptions *) tool->tool_info->tool_options;
if (options->clone_type == GIMP_IMAGE_CLONE && clone->src_drawable)
if (options->clone_type == GIMP_IMAGE_CLONE && clone->src_pickable)
{
gint off_x;
gint off_y;
gint off_x = 0;
gint off_y = 0;
GimpDisplay *tmp_gdisp;
GimpCloneTool *clone_tool = GIMP_CLONE_TOOL (draw_tool);
gimp_item_offsets (GIMP_ITEM (clone->src_drawable), &off_x, &off_y);
if (GIMP_IS_DRAWABLE (clone->src_pickable))
gimp_item_offsets (GIMP_ITEM (clone->src_pickable), &off_x, &off_y);
tmp_gdisp = draw_tool->gdisp;
draw_tool->gdisp = clone_tool->src_gdisp;
@ -309,6 +322,7 @@ gimp_clone_options_gui (GimpToolOptions *tool_options)
GtkWidget *vbox;
GtkWidget *frame;
GtkWidget *hbox;
GtkWidget *button;
vbox = gimp_paint_options_gui (tool_options);
@ -327,5 +341,10 @@ gimp_clone_options_gui (GimpToolOptions *tool_options)
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
gtk_widget_show (frame);
button = gimp_prop_check_button_new (config, "sample-merged",
_("Sample Merged"));
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
gtk_widget_show (button);
return vbox;
}

View File

@ -160,8 +160,13 @@ gimp_clone_tool_button_press (GimpTool *tool,
GdkModifierType state,
GimpDisplay *gdisp)
{
GimpPaintTool *paint_tool = GIMP_PAINT_TOOL (tool);
GimpCloneTool *clone_tool = GIMP_CLONE_TOOL (tool);
GimpPaintTool *paint_tool = GIMP_PAINT_TOOL (tool);
GimpCloneTool *clone_tool = GIMP_CLONE_TOOL (tool);
GimpCloneOptions *options;
options = GIMP_CLONE_OPTIONS (tool->tool_info->tool_options);
paint_tool->core->use_saved_proj = FALSE;
if ((state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == GDK_CONTROL_MASK)
{
@ -181,6 +186,13 @@ gimp_clone_tool_button_press (GimpTool *tool,
else
{
GIMP_CLONE (paint_tool->core)->set_source = FALSE;
if (options->clone_type == GIMP_IMAGE_CLONE &&
options->sample_merged &&
gdisp == clone_tool->src_gdisp)
{
paint_tool->core->use_saved_proj = TRUE;
}
}
GIMP_TOOL_CLASS (parent_class)->button_press (tool, coords, time, state,
@ -232,7 +244,7 @@ gimp_clone_tool_cursor_update (GimpTool *tool,
{
if ((state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == GDK_CONTROL_MASK)
ctype = GIMP_CURSOR_CROSSHAIR_SMALL;
else if (! GIMP_CLONE (GIMP_PAINT_TOOL (tool)->core)->src_drawable)
else if (! GIMP_CLONE (GIMP_PAINT_TOOL (tool)->core)->src_pickable)
ctype = GIMP_CURSOR_BAD;
}
@ -252,7 +264,7 @@ gimp_clone_tool_oper_update (GimpTool *tool,
GIMP_TOOL_CLASS (parent_class)->oper_update (tool, coords, state, gdisp);
if (GIMP_CLONE_OPTIONS (options)->clone_type == GIMP_IMAGE_CLONE &&
GIMP_CLONE (GIMP_PAINT_TOOL (tool)->core)->src_drawable == NULL)
GIMP_CLONE (GIMP_PAINT_TOOL (tool)->core)->src_pickable == NULL)
{
gimp_tool_replace_status (tool, gdisp,
_("Ctrl-Click to set a clone source."));
@ -271,14 +283,15 @@ gimp_clone_tool_draw (GimpDrawTool *draw_tool)
options = (GimpCloneOptions *) tool->tool_info->tool_options;
if (options->clone_type == GIMP_IMAGE_CLONE && clone->src_drawable)
if (options->clone_type == GIMP_IMAGE_CLONE && clone->src_pickable)
{
gint off_x;
gint off_y;
gint off_x = 0;
gint off_y = 0;
GimpDisplay *tmp_gdisp;
GimpCloneTool *clone_tool = GIMP_CLONE_TOOL (draw_tool);
gimp_item_offsets (GIMP_ITEM (clone->src_drawable), &off_x, &off_y);
if (GIMP_IS_DRAWABLE (clone->src_pickable))
gimp_item_offsets (GIMP_ITEM (clone->src_pickable), &off_x, &off_y);
tmp_gdisp = draw_tool->gdisp;
draw_tool->gdisp = clone_tool->src_gdisp;
@ -309,6 +322,7 @@ gimp_clone_options_gui (GimpToolOptions *tool_options)
GtkWidget *vbox;
GtkWidget *frame;
GtkWidget *hbox;
GtkWidget *button;
vbox = gimp_paint_options_gui (tool_options);
@ -327,5 +341,10 @@ gimp_clone_options_gui (GimpToolOptions *tool_options)
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
gtk_widget_show (frame);
button = gimp_prop_check_button_new (config, "sample-merged",
_("Sample Merged"));
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
gtk_widget_show (button);
return vbox;
}