Commit 26374e81 authored by Michael Natterer's avatar Michael Natterer 😴 Committed by Michael Natterer

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.
parent 19ea8868
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
......
......@@ -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);
}
......@@ -40,7 +40,7 @@ struct _GimpClone
gboolean set_source;
GimpDrawable *src_drawable;
GimpPickable *src_pickable;
gint src_x;
gint src_y;
......
......@@ -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;
......
......@@ -40,6 +40,7 @@ struct _GimpCloneOptions
GimpCloneType clone_type;
GimpCloneAlignMode align_mode;
gboolean sample_merged;
};
......
......@@ -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);