diff --git a/app/paint/gimpheal.c b/app/paint/gimpheal.c new file mode 100644 index 0000000000..dcb1041aff --- /dev/null +++ b/app/paint/gimpheal.c @@ -0,0 +1,795 @@ +/* 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 "config.h" + +#include +#include + +#include + +#include "libgimpbase/gimpbase.h" + +#include "paint-types.h" + +#include "paint-funcs/paint-funcs.h" + +#include "base/pixel-region.h" +#include "base/temp-buf.h" +#include "base/tile-manager.h" + +#include "core/gimpitem.h" +#include "core/gimppickable.h" +#include "core/gimpimage.h" +#include "core/gimpdrawable.h" +#include "core/gimppattern.h" + +#include "gimpheal.h" +#include "gimphealoptions.h" + +#include "gimp-intl.h" + +enum +{ + PROP_0, + PROP_SRC_DRAWABLE, + PROP_SRC_X, + PROP_SRC_Y +}; + +static void gimp_heal_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static void gimp_heal_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_heal_paint (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpPaintState paint_state, + guint32 time); + +static void gimp_heal_motion (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options); + +static void gimp_heal_set_src_drawable (GimpHeal *heal, + GimpDrawable *drawable); + +G_DEFINE_TYPE (GimpHeal, gimp_heal, GIMP_TYPE_BRUSH_CORE) + +void +gimp_heal_register (Gimp *gimp, + GimpPaintRegisterCallback callback) +{ + (* callback) (gimp, /* gimp */ + GIMP_TYPE_HEAL, /* paint type */ + GIMP_TYPE_HEAL_OPTIONS, /* paint options type */ + "gimp-heal", /* identifier */ + _("Heal"), /* blurb */ + "gimp-tool-heal"); /* stock id */ +} + +static void +gimp_heal_class_init (GimpHealClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpPaintCoreClass *paint_core_class = GIMP_PAINT_CORE_CLASS (klass); + GimpBrushCoreClass *brush_core_class = GIMP_BRUSH_CORE_CLASS (klass); + + object_class->set_property = gimp_heal_set_property; + object_class->get_property = gimp_heal_get_property; + + paint_core_class->paint = gimp_heal_paint; + + brush_core_class->handles_changing_brush = TRUE; + + g_object_class_install_property (object_class, PROP_SRC_DRAWABLE, + g_param_spec_object ("src-drawable", + NULL, NULL, + GIMP_TYPE_DRAWABLE, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_SRC_X, + g_param_spec_double ("src-x", NULL, NULL, + 0, GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_SRC_Y, + g_param_spec_double ("src-y", NULL, NULL, + 0, GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_heal_init (GimpHeal *heal) +{ + heal->set_source = FALSE; + + heal->src_drawable = NULL; + heal->src_x = 0.0; + heal->src_y = 0.0; + + heal->orig_src_x = 0.0; + heal->orig_src_y = 0.0; + + heal->offset_x = 0.0; + heal->offset_y = 0.0; + heal->first_stroke = TRUE; +} + +static void +gimp_heal_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpHeal *heal = GIMP_HEAL (object); + + switch (property_id) + { + case PROP_SRC_DRAWABLE: + gimp_heal_set_src_drawable (heal, g_value_get_object (value)); + break; + case PROP_SRC_X: + heal->src_x = g_value_get_double (value); + break; + case PROP_SRC_Y: + heal->src_y = g_value_get_double (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_heal_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpHeal *heal = GIMP_HEAL (object); + + switch (property_id) + { + case PROP_SRC_DRAWABLE: + g_value_set_object (value, heal->src_drawable); + break; + case PROP_SRC_X: + g_value_set_int (value, heal->src_x); + break; + case PROP_SRC_Y: + g_value_set_int (value, heal->src_y); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_heal_paint (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpPaintState paint_state, + guint32 time) +{ + GimpHeal *heal = GIMP_HEAL (paint_core); + GimpHealOptions *options = GIMP_HEAL_OPTIONS (paint_options); + + /* gimp passes the current state of the painting system to the function */ + switch (paint_state) + { + case GIMP_PAINT_STATE_INIT: + /* heal->set_source is set by gimphealtool when CTRL is pressed */ + if (heal->set_source) + { + /* defined later in this file, sets heal to the current drawable + * and notifies heal that the drawable is ready */ + gimp_heal_set_src_drawable (heal, drawable); + + /* get the current source coordinates from the paint core */ + heal->src_x = paint_core->cur_coords.x; + heal->src_y = paint_core->cur_coords.y; + + /* set first stroke to be true */ + heal->first_stroke = TRUE; + } + else if (options->align_mode == GIMP_HEAL_ALIGN_NO) + { + heal->orig_src_x = heal->src_x; + heal->orig_src_y = heal->src_y; + + heal->first_stroke = TRUE; + } + break; + + case GIMP_PAINT_STATE_MOTION: + if (heal->set_source) + { + /* if the control key is down, move the src and return */ + heal->src_x = paint_core->cur_coords.x; + heal->src_y = paint_core->cur_coords.y; + + heal->first_stroke = TRUE; + } + else + { + /* otherwise, update the target */ + + /* get the current destination from the paint core */ + gint dest_x = paint_core->cur_coords.x; + gint dest_y = paint_core->cur_coords.y; + + /* update the coordinates depending on the alignment mode */ + if (options->align_mode == GIMP_HEAL_ALIGN_FIXED) + { + heal->offset_x = heal->src_x - dest_x; + heal->offset_y = heal->src_y - dest_y; + } + else if (heal->first_stroke) + { + heal->offset_x = heal->src_x - dest_x; + heal->offset_y = heal->src_y - dest_y; + + heal->first_stroke = FALSE; + } + + heal->src_x = dest_x + heal->offset_x; + heal->src_y = dest_y + heal->offset_y; + + /* defined later, does the actual healing */ + gimp_heal_motion (paint_core, drawable, paint_options); + } + break; + + case GIMP_PAINT_STATE_FINISH: + if ((options->align_mode == GIMP_HEAL_ALIGN_NO) && (! heal->first_stroke)) + { + heal->src_x = heal->orig_src_x; + heal->src_y = heal->orig_src_y; + } + break; + + default: + break; + } + + /* notify the brush that the src attributes have changed */ + g_object_notify (G_OBJECT (heal), "src-x"); + g_object_notify (G_OBJECT (heal), "src-y"); +} + +/* + * Substitute any zeros in the input PixelRegion for ones. This is needed by + * the algorithm to avoid division by zero, and to get a more realistic image + * since multiplying by zero is often incorrect (i.e., healing from a dark to + * a light region will have incorrect spots of zero) + */ +static void +gimp_heal_substitute_0_for_1 (PixelRegion *pr) +{ + gint i, j, k; + + gint height = pr->h; + gint width = pr->w; + gint depth = pr->bytes; + + guchar *pr_data = pr->data; + + guchar *p; + + for (i = 0; i < height; i++) + { + p = pr_data; + + for (j = 0; j < width; j++) + { + for (k = 0; k < depth; k++) + { + if (p[k] == 0) + p[k] = 1; + } + + p += depth; + } + + pr_data += pr->rowstride; + } +} + +/* + * Divide topPR by bottomPR and store the result as a double + */ +static void +gimp_heal_divide (PixelRegion *topPR, + PixelRegion *bottomPR, + gdouble *result) +{ + gint i, j, k; + + gint height = topPR->h; + gint width = topPR->w; + gint depth = topPR->bytes; + + guchar *t_data = topPR->data; + guchar *b_data = bottomPR->data; + + guchar *t; + guchar *b; + gdouble *r = result; + + for (i = 0; i < height; i++) + { + t = t_data; + b = b_data; + + for (j = 0; j < width; j++) + { + for (k = 0; k < depth; k++) + { + r[k] = (gdouble) (t[k]) / (gdouble) (b[k]); + } + + t += depth; + b += depth; + r += depth; + } + + t_data += topPR->rowstride; + b_data += bottomPR->rowstride; + } +} + +/* + * multiply first by secondPR and store the result as a PixelRegion + */ +static void +gimp_heal_multiply (gdouble *first, + PixelRegion *secondPR, + PixelRegion *resultPR) +{ + gint i, j, k; + + gint height = secondPR->h; + gint width = secondPR->w; + gint depth = secondPR->bytes; + + guchar *s_data = secondPR->data; + guchar *r_data = resultPR->data; + + gdouble *f = first; + guchar *s; + guchar *r; + + for (i = 0; i < height; i++) + { + s = s_data; + r = r_data; + + for (j = 0; j < width; j++) + { + for (k = 0; k < depth; k++) + { + r[k] = (guchar) CLAMP0255 (ROUND (((gdouble) (f[k])) * ((gdouble) (s[k])))); + } + + f += depth; + s += depth; + r += depth; + } + + s_data += secondPR->rowstride; + r_data += resultPR->rowstride; + } +} + +/* + * Perform one iteration of the laplace solver for matrix. Store the result in + * solution and return the cummulative error of the solution. + */ +gdouble +gimp_heal_laplace_iteration (gdouble *matrix, + gint height, + gint depth, + gint width, + gdouble *solution) +{ + gint rowstride = width * depth; + gint i, j, k; + gdouble err = 0.0; + gdouble tmp, diff; + + for (i = 0; i < height; i++) + { + for (j = 0; j < width; j++) + { + if ((i == 0) || (i == (height - 1)) || (j == 0) || (j == (height - 1))) + /* do nothing at the boundary */ + for (k = 0; k < depth; k++) + *(solution + k) = *(matrix + k); + else + { + /* calculate the mean of four neighbours for all color channels */ + /* v[i][j] = 0.45 * (v[i][j-1]+v[i][j+1]+v[i-1][j]+v[i+1][j]) */ + for (k = 0; k < depth; k++) + { + tmp = *(solution + k); + + *(solution + k) = 0.25 * + ( *(matrix - depth + k) /* west */ + + *(matrix + depth + k) /* east */ + + *(matrix - rowstride + k) /* north */ + + *(matrix + rowstride + k)); /* south */ + + diff = *(solution + k) - tmp; + err += diff*diff; + } + } + + /* advance pointers to data */ + matrix += depth; solution += depth; + } + } + + return sqrt (err); +} + +/* + * Solve the laplace equation for matrix and store the result in solution. + */ +void +gimp_heal_laplace_loop (gdouble *matrix, + gint height, + gint depth, + gint width, + gdouble *solution) +{ +#define EPSILON 0.0001 +#define MAX_ITER 500 + + gint num_iter = 0; + gdouble err; + + /* do one iteration and store the amount of error */ + err = gimp_heal_laplace_iteration (matrix, height, depth, width, solution); + + /* copy solution to matrix */ + memcpy (matrix, solution, width * height * depth * sizeof(double)); + + /* repeat until convergence or max iterations */ + while (err > EPSILON) + { + err = gimp_heal_laplace_iteration (matrix, height, depth, width, solution); + memcpy (matrix, solution, width * height * depth * sizeof(double)); + + num_iter++; + + if (num_iter >= MAX_ITER) + break; + } +} + +/* + * The healing brush algorithm. Heal tempPR and store the result in srcPR. + * Algorithm Design: + * T. Georgiev, "Image Reconstruction Invariant to Relighting", EUROGRAPHICS + * 2005, http://www.tgeorgiev.net/ + */ +PixelRegion * +gimp_heal_region (PixelRegion *tempPR, + PixelRegion *srcPR) +{ + gdouble *i_1 = g_malloc (tempPR->h * tempPR->bytes * tempPR->w * sizeof (gdouble)); + gdouble *i_2 = g_malloc (tempPR->h * tempPR->bytes * tempPR->w * sizeof (gdouble)); + + /* substitute 0's for 1's for the division and multiplication operations that + * come later */ + gimp_heal_substitute_0_for_1 (srcPR); + + /* divide tempPR by srcPR and store the result as a double in i_1 */ + gimp_heal_divide (tempPR, srcPR, i_1); + + /* FIXME: is a faster implementation needed? */ + gimp_heal_laplace_loop (i_1, tempPR->h, tempPR->bytes, tempPR->w, i_2); + + /* multiply a double by srcPR and store in tempPR */ + gimp_heal_multiply (i_2, srcPR, tempPR); + + g_free (i_1); + g_free (i_2); + + return tempPR; +} + +void print_uchar_matrix (guchar *matrix, gint width, gint height, gint depth, gint rowstride) +{ + gint i,j; + + guchar *temp; + + for (i = 0; i < height; i++) + { + temp = matrix; + for (j = 0; j < width; j++) + { + printf("%d\t", *temp); + temp += depth; + } + + printf("\n"); + matrix += rowstride; + } +} + +static void +gimp_heal_motion (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options) +{ + GimpHeal *heal = GIMP_HEAL (paint_core); + GimpHealOptions *options = GIMP_HEAL_OPTIONS (paint_options); + GimpContext *context = GIMP_CONTEXT (paint_options); + GimpPressureOptions *pressure_options = paint_options->pressure_options; + GimpImage *image; + GimpImage *src_image = NULL; + GimpPickable *src_pickable = NULL; + TileManager *src_tiles; + TempBuf *area; + TempBuf *temp; + gdouble opacity; + PixelRegion tempPR; + PixelRegion srcPR; + PixelRegion destPR; + gint offset_x; + gint offset_y; + + /* FIXME: Why doesn't the sample merged option work? It is set up exactly as + * in the clone tool, but nothing gets displayed properly. + * + * Currently merged is disabled in gimphealtool.c. If you want to try + * and get it working, enable it there. + */ + + image = gimp_item_get_image (GIMP_ITEM (drawable)); + + /* FIXME: This test expects a GimpImageType variable, not GimpImage */ + if (GIMP_IMAGE_TYPE_IS_INDEXED (image)) + { + g_message (_("Indexed images are not currently supported.")); + return; + } + + opacity = gimp_paint_options_get_fade (paint_options, image, paint_core->pixel_dist); + + if (opacity == 0.0) + return; + + if (! heal->src_drawable) + return; + + /* prepare the regions to get data from */ + src_pickable = GIMP_PICKABLE (heal->src_drawable); + src_image = gimp_pickable_get_image (src_pickable); + + /* make local copies */ + offset_x = heal->offset_x; + offset_y = heal->offset_y; + + /* adjust offsets and pickable if we are merging layers */ + if (options->sample_merged) + { + gint off_x, off_y; + + src_pickable = GIMP_PICKABLE (src_image->projection); + + gimp_item_offsets (GIMP_ITEM (heal->src_drawable), &off_x, &off_y); + + offset_x += off_x; + offset_y += off_y; + } + + gimp_pickable_flush (src_pickable); + + area = gimp_paint_core_get_paint_area (paint_core, drawable, paint_options); + if (!area) + return; + + /* clear the area where we want to paint */ + temp_buf_data_clear (area); + + /* get the source tiles */ + src_tiles = gimp_pickable_get_tiles (src_pickable); + + /* FIXME: the area under the cursor and the source area should be x% larger + * than the brush size so that we have seamless blending. Otherwise the brush + * must be a lot bigger than the area to heal in order to get good results. + * Having the user pick such a large brush is perhaps counter-intutitive? */ + + /* Get the area underneath the cursor */ + { + TempBuf *orig; + gint x1, x2, y1, y2; + + x1 = CLAMP (area->x, + 0, tile_manager_width (src_tiles)); + y1 = CLAMP (area->y, + 0, tile_manager_height (src_tiles)); + x2 = CLAMP (area->x + area->width, + 0, tile_manager_width (src_tiles)); + y2 = CLAMP (area->y + area->height, + 0, tile_manager_height (src_tiles)); + + if (! (x2 - x1) || (! (y2 - y1))) + return; + + /* get the original image data */ + if (options->sample_merged) + orig = gimp_paint_core_get_orig_proj (paint_core, + src_pickable, + x1, y1, x2, y2); + else + orig = gimp_paint_core_get_orig_image (paint_core, + GIMP_DRAWABLE (src_pickable), + x1, y1, x2, y2); + + pixel_region_init_temp_buf (&srcPR, orig, 0, 0, x2 - x1, y2 - y1); + } + + temp = temp_buf_new (srcPR.w, srcPR.h, srcPR.bytes, 0, 0, NULL); + + pixel_region_init_temp_buf (&tempPR, temp, 0, 0, srcPR.w, srcPR.h); + + copy_region (&srcPR, &tempPR); + + /* now tempPR holds the data under the cursor */ + + /* get a copy of the location we will sample from */ + { + TempBuf *orig; + gint x1, x2, y1, y2; + + x1 = CLAMP (area->x + offset_x, + 0, tile_manager_width (src_tiles)); + y1 = CLAMP (area->y + offset_y, + 0, tile_manager_height (src_tiles)); + x2 = CLAMP (area->x + offset_x + area->width, + 0, tile_manager_width (src_tiles)); + y2 = CLAMP (area->y + offset_y + area->height, + 0, tile_manager_height (src_tiles)); + + if (! (x2 - x1) || (! (y2 - y1))) + return; + + /* if we have a different source and destination image */ + if (( options->sample_merged && (src_image != image)) || + (! options->sample_merged && (heal->src_drawable != drawable))) + { + /* FIXME: Here we need to initialize srcPR using data from the other + * image. */ + g_message (_("Healing between images is not currently supported.")); + return; + } + /* if we don't have a different source and destination image */ + else + { + if (options->sample_merged) + orig = gimp_paint_core_get_orig_proj (paint_core, + src_pickable, + x1, y1, x2, y2); + else + orig = gimp_paint_core_get_orig_image (paint_core, + GIMP_DRAWABLE (src_pickable), + x1, y1, x2, y2); + + pixel_region_init_temp_buf (&srcPR, orig, 0, 0, x2 - x1, y2 - y1); + } + + /* set the proper offset */ + offset_x = x1 - (area->x + offset_x); + offset_y = y1 - (area->y + offset_y); + } + + /* now srcPR holds the source area to sample from */ + + /* get the destination to paint to */ + pixel_region_init_temp_buf (&destPR, area, offset_x, offset_y, srcPR.w, srcPR.h); + + /* FIXME: Can we ensure that this is true in the code above? + * Is it already guaranteed to be true before we get here? */ + /* check that srcPR, tempPR, and destPR are the same size */ + if ((srcPR.w != tempPR.w ) || (srcPR.w != destPR.w ) || + (srcPR.h != tempPR.h ) || (srcPR.h != destPR.h )) + { + g_message (_("Source and destination regions are not the same size.")); + return; + } + + /* heal tempPR using srcPR */ + gimp_heal_region (&tempPR, &srcPR); + + /* re-initialize tempPR so that it can be used within copy_region */ + pixel_region_init_data (&tempPR, tempPR.data, tempPR.bytes, tempPR.rowstride, + 0, 0, tempPR.w, tempPR.h); + + /* add an alpha region to the area if necessary */ + if (! gimp_drawable_has_alpha (drawable)) + add_alpha_region (&tempPR, &destPR); + else + copy_region (&tempPR, &destPR); + + if (pressure_options->opacity) + opacity *= PRESSURE_SCALE * paint_core->cur_coords.pressure; + + gimp_brush_core_replace_canvas (GIMP_BRUSH_CORE (paint_core), + drawable, + MIN (opacity, GIMP_OPACITY_OPAQUE), + gimp_context_get_opacity (context), + gimp_paint_options_get_brush_mode (paint_options), + GIMP_PAINT_CONSTANT); +} + +static void +gimp_heal_src_drawable_removed (GimpDrawable *drawable, + GimpHeal *heal) +{ + /* set the drawable to NULL */ + if (drawable == heal->src_drawable) + { + heal->src_drawable = NULL; + } + + /* disconnect the signal handler for this function */ + g_signal_handlers_disconnect_by_func (drawable, + gimp_heal_src_drawable_removed, + heal); +} + +static void +gimp_heal_set_src_drawable (GimpHeal *heal, + GimpDrawable *drawable) +{ + /* check if we already have a drawable */ + if (heal->src_drawable == drawable) + return; + + /* remove the current signal handler */ + if (heal->src_drawable) + g_signal_handlers_disconnect_by_func (heal->src_drawable, + gimp_heal_src_drawable_removed, + heal); + + /* set the drawable */ + heal->src_drawable = drawable; + + /* connect the signal handler that handles the "remove" signal */ + if (heal->src_drawable) + g_signal_connect (heal->src_drawable, "removed", + G_CALLBACK (gimp_heal_src_drawable_removed), + heal); + + /* notify heal that the source drawable is set */ + g_object_notify (G_OBJECT (heal), "src-drawable"); +} diff --git a/app/paint/gimpheal.h b/app/paint/gimpheal.h new file mode 100644 index 0000000000..9e1b475bea --- /dev/null +++ b/app/paint/gimpheal.h @@ -0,0 +1,64 @@ +/* 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. + */ + +#ifndef __GIMP_HEAL_H__ +#define __GIMP_HEAL_H__ + +#include "gimpbrushcore.h" + +#define GIMP_TYPE_HEAL (gimp_heal_get_type ()) +#define GIMP_HEAL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_HEAL, GimpHeal)) +#define GIMP_HEAL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_HEAL, GimpHealClass)) +#define GIMP_IS_HEAL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_HEAL)) +#define GIMP_IS_HEAL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_HEAL)) +#define GIMP_HEAL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_HEAL, GimpHealClass)) + +typedef struct _GimpHeal GimpHeal; +typedef struct _GimpHealClass GimpHealClass; + +struct _GimpHeal +{ + GimpBrushCore parent_instance; + + gboolean set_source; + + GimpDrawable *src_drawable; + gdouble src_x; + gdouble src_y; + + gdouble orig_src_x; + gdouble orig_src_y; + + gdouble offset_x; + gdouble offset_y; + gboolean first_stroke; +}; + +struct _GimpHealClass +{ + GimpBrushCoreClass parent_class; +}; + + +void gimp_heal_register (Gimp *gimp, + GimpPaintRegisterCallback callback); + +GType gimp_heal_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_HEAL_H__ */ diff --git a/app/paint/gimphealoptions.c b/app/paint/gimphealoptions.c new file mode 100644 index 0000000000..9891d9cfdf --- /dev/null +++ b/app/paint/gimphealoptions.c @@ -0,0 +1,114 @@ +/* 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 "config.h" + +#include + +#include "libgimpconfig/gimpconfig.h" + +#include "paint-types.h" +#include "gimphealoptions.h" + +enum +{ + PROP_0, + PROP_ALIGN_MODE, + PROP_SAMPLE_MERGED +}; + +static void gimp_heal_options_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_heal_options_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +G_DEFINE_TYPE (GimpHealOptions, gimp_heal_options, GIMP_TYPE_PAINT_OPTIONS) + +static void +gimp_heal_options_class_init (GimpHealOptionsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = gimp_heal_options_set_property; + object_class->get_property = gimp_heal_options_get_property; + + GIMP_CONFIG_INSTALL_PROP_ENUM (object_class, PROP_ALIGN_MODE, + "align-mode", NULL, + GIMP_TYPE_HEAL_ALIGN_MODE, + GIMP_HEAL_ALIGN_NO, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_INSTALL_PROP_BOOLEAN (object_class, PROP_SAMPLE_MERGED, + "sample-merged", NULL, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + +} + +static void +gimp_heal_options_init (GimpHealOptions *options) +{ +} + +static void +gimp_heal_options_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpHealOptions *options = GIMP_HEAL_OPTIONS (object); + + switch (property_id) + { + 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; + } +} + +static void +gimp_heal_options_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpHealOptions *options = GIMP_HEAL_OPTIONS (object); + + switch (property_id) + { + 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; + } +} diff --git a/app/paint/gimphealoptions.h b/app/paint/gimphealoptions.h new file mode 100644 index 0000000000..9bdf5724e0 --- /dev/null +++ b/app/paint/gimphealoptions.h @@ -0,0 +1,48 @@ +/* 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. + */ + +#ifndef __GIMP_HEAL_OPTIONS_H__ +#define __GIMP_HEAL_OPTIONS_H__ + + +#include "gimppaintoptions.h" + + +#define GIMP_TYPE_HEAL_OPTIONS (gimp_heal_options_get_type ()) +#define GIMP_HEAL_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_HEAL_OPTIONS, GimpHealOptions)) +#define GIMP_HEAL_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_HEAL_OPTIONS, GimpHealOptionsClass)) +#define GIMP_IS_HEAL_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_HEAL_OPTIONS)) +#define GIMP_IS_HEAL_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_HEAL_OPTIONS)) +#define GIMP_HEAL_OPTIONS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_HEAL_OPTIONS, GimpHealOptionsClass)) + + +typedef struct _GimpHealOptions GimpHealOptions; +typedef struct _GimpPaintOptionsClass GimpHealOptionsClass; + +struct _GimpHealOptions +{ + GimpPaintOptions paint_options; + + GimpHealAlignMode align_mode; + gboolean sample_merged; +}; + + +GType gimp_heal_options_get_type (void) G_GNUC_CONST; + +#endif /* __GIMP_HEAL_OPTIONS_H__ */ diff --git a/app/tools/gimphealtool.c b/app/tools/gimphealtool.c new file mode 100644 index 0000000000..df78bad9a6 --- /dev/null +++ b/app/tools/gimphealtool.c @@ -0,0 +1,492 @@ +/* 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 "config.h" + +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "tools-types.h" + +#include "core/gimpchannel.h" +#include "core/gimpimage.h" +#include "core/gimppickable.h" +#include "core/gimptoolinfo.h" + +#include "paint/gimpheal.h" +#include "paint/gimphealoptions.h" + +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpviewablebox.h" +#include "widgets/gimpwidgets-utils.h" + +#include "display/gimpdisplay.h" + +#include "gimphealtool.h" +#include "gimppaintoptions-gui.h" +#include "gimptoolcontrol.h" + +#include "gimp-intl.h" + +#define TARGET_WIDTH 15 +#define TARGET_HEIGHT 15 + +static gboolean gimp_heal_tool_has_display (GimpTool *tool, + GimpDisplay *display); +static GimpDisplay *gimp_heal_tool_has_image (GimpTool *tool, + GimpImage *image); + +static void gimp_heal_tool_button_press (GimpTool *tool, + GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpDisplay *display); + +static void gimp_heal_tool_control (GimpTool *tool, + GimpToolAction action, + GimpDisplay *display); + +static void gimp_heal_tool_motion (GimpTool *tool, + GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpDisplay *display); + +static void gimp_heal_tool_cursor_update (GimpTool *tool, + GimpCoords *coords, + GdkModifierType state, + GimpDisplay *display); + +static void gimp_heal_tool_oper_update (GimpTool *tool, + GimpCoords *coords, + GdkModifierType state, + gboolean proximity, + GimpDisplay *display); + +static void gimp_heal_tool_draw (GimpDrawTool *draw_tool); + +static GtkWidget *gimp_heal_options_gui (GimpToolOptions *tool_options); + +G_DEFINE_TYPE (GimpHealTool, gimp_heal_tool, GIMP_TYPE_PAINT_TOOL) + +void +gimp_heal_tool_register (GimpToolRegisterCallback callback, + gpointer data) +{ + (* callback) (GIMP_TYPE_HEAL_TOOL, /* tool type */ + GIMP_TYPE_HEAL_OPTIONS, /* tool option type */ + gimp_heal_options_gui, /* options gui */ + GIMP_PAINT_OPTIONS_CONTEXT_MASK, /* context properties */ + "gimp-heal-tool", /* identifier */ + _("Heal"), /* blurb */ + _("Heal image irregularities"), /* help */ + N_("_Heal"), /* menu path */ + "H", /* menu accelerator */ + NULL, /* help domain */ + GIMP_HELP_TOOL_HEAL, /* help data */ + GIMP_STOCK_TOOL_HEAL, /* stock id */ + data); /* register */ +} + +static void +gimp_heal_tool_class_init (GimpHealToolClass *klass) +{ + /* get parent classes where we override methods */ + GimpToolClass *tool_class = GIMP_TOOL_CLASS (klass); + GimpDrawToolClass *draw_tool_class = GIMP_DRAW_TOOL_CLASS (klass); + + /* override the methods */ + tool_class->has_display = gimp_heal_tool_has_display; + tool_class->has_image = gimp_heal_tool_has_image; + tool_class->control = gimp_heal_tool_control; + tool_class->button_press = gimp_heal_tool_button_press; + tool_class->motion = gimp_heal_tool_motion; + tool_class->cursor_update = gimp_heal_tool_cursor_update; + tool_class->oper_update = gimp_heal_tool_oper_update; + + draw_tool_class->draw = gimp_heal_tool_draw; +} + +static void +gimp_heal_tool_init (GimpHealTool *heal) +{ + GimpTool *tool = GIMP_TOOL (heal); + GimpPaintTool *paint_tool = GIMP_PAINT_TOOL (tool); + + gimp_tool_control_set_tool_cursor (tool->control, + GIMP_TOOL_CURSOR_HEAL); + + paint_tool->status = _("Click to heal."); + paint_tool->status_ctrl = _("%s to set a new heal source"); +} + +static gboolean +gimp_heal_tool_has_display (GimpTool *tool, + GimpDisplay *display) +{ + GimpHealTool *heal_tool = GIMP_HEAL_TOOL (tool); + + return (display == heal_tool->src_display || + GIMP_TOOL_CLASS (gimp_heal_tool_parent_class)->has_display (tool, display)); +} + +static GimpDisplay * +gimp_heal_tool_has_image (GimpTool *tool, + GimpImage *image) +{ + GimpHealTool *heal_tool = GIMP_HEAL_TOOL (tool); + GimpDisplay *display; + + display = GIMP_TOOL_CLASS (gimp_heal_tool_parent_class)->has_image (tool, image); + + if (! display && heal_tool->src_display) + { + if (image && heal_tool->src_display->image == image) + display = heal_tool->src_display; + + /* NULL image means any display */ + if (! image) + display = heal_tool->src_display; + } + + return display; +} + +static void +gimp_heal_tool_control (GimpTool *tool, + GimpToolAction action, + GimpDisplay *display) +{ + GimpHealTool *heal_tool = GIMP_HEAL_TOOL (tool); + + switch (action) + { + case GIMP_TOOL_ACTION_PAUSE: + case GIMP_TOOL_ACTION_RESUME: + break; + + case GIMP_TOOL_ACTION_HALT: + heal_tool->src_display = NULL; + g_object_set (GIMP_PAINT_TOOL (tool)->core, + "src-drawable", NULL, + NULL); + break; + } + + GIMP_TOOL_CLASS (gimp_heal_tool_parent_class)->control (tool, action, display); +} + + +static void +gimp_heal_tool_button_press (GimpTool *tool, + GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpDisplay *display) +{ + GimpPaintTool *paint_tool = GIMP_PAINT_TOOL (tool); + GimpHealTool *heal_tool = GIMP_HEAL_TOOL (tool); + GimpHeal *heal = GIMP_HEAL (paint_tool->core); + GimpHealOptions *options; + + options = GIMP_HEAL_OPTIONS (tool->tool_info->tool_options); + + /* pause the tool before drawing */ + gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool)); + + /* use_saved_proj tells whether we keep the unmodified pixel projection + * around. in this case no. */ + paint_tool->core->use_saved_proj = FALSE; + + /* state holds a set of bit-flags to indicate the state of modifier keys and + * mouse buttons in various event types. Typical modifier keys are Shift, + * Control, Meta, Super, Hyper, Alt, Compose, Apple, CapsLock or ShiftLock. + * Part of gtk -> GdkModifierType */ + if ((state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == GDK_CONTROL_MASK) + /* we enter here only if CTRL is pressed */ + { + /* mark that the source display has been set. defined in gimpheal.h */ + heal->set_source = TRUE; + + /* if currently active display != the heal tools source display */ + if (display != heal_tool->src_display) + { + /* check if the heal tools src display has been previously set */ + if (heal_tool->src_display) + /* if so remove the pointer to the display */ + g_object_remove_weak_pointer (G_OBJECT (heal_tool->src_display), + (gpointer *) &heal_tool->src_display); + + /* set the heal tools source display to the currently active + * display */ + heal_tool->src_display = display; + + /* add a pointer to the display */ + g_object_add_weak_pointer (G_OBJECT (display), + (gpointer *) &heal_tool->src_display); + } + } + else + { + /* note that the source is not being set */ + heal->set_source = FALSE; + + if ((options->sample_merged) && (display == heal_tool->src_display)) + { + /* keep unmodified projection around */ + paint_tool->core->use_saved_proj = TRUE; + } + } + + /* chain up to call the parents functions */ + GIMP_TOOL_CLASS (gimp_heal_tool_parent_class)->button_press (tool, coords, + time, state, + display); + + /* set the tool display's source position to match the current heal + * implementation source position */ + heal_tool->src_x = heal->src_x; + heal_tool->src_y = heal->src_y; + + /* resume drawing */ + gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool)); +} + +static void +gimp_heal_tool_motion (GimpTool *tool, + GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpDisplay *display) +{ + GimpHealTool *heal_tool = GIMP_HEAL_TOOL (tool); + GimpPaintTool *paint_tool = GIMP_PAINT_TOOL (tool); + GimpHeal *heal = GIMP_HEAL (paint_tool->core); + + /* pause drawing */ + gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool)); + + /* check if CTRL is pressed */ + if ((state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == GDK_CONTROL_MASK) + /* if yes the source has been set */ + heal->set_source = TRUE; + else + /* if no the source has not been set */ + heal->set_source = FALSE; + + /* chain up to the parent classes motion function */ + GIMP_TOOL_CLASS (gimp_heal_tool_parent_class)->motion (tool, coords, time, + state, display); + + /* set the tool display's source to be the same as the heal implementation + * source */ + heal_tool->src_x = heal->src_x; + heal_tool->src_y = heal->src_y; + + /* resume drawing */ + gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool)); +} + +static void +gimp_heal_tool_cursor_update (GimpTool *tool, + GimpCoords *coords, + GdkModifierType state, + GimpDisplay *display) +{ + GimpHealOptions *options; + GimpCursorType cursor = GIMP_CURSOR_MOUSE; + GimpCursorModifier modifier = GIMP_CURSOR_MODIFIER_NONE; + + options = GIMP_HEAL_OPTIONS (tool->tool_info->tool_options); + + /* if the cursor is in an active area */ + if (gimp_image_coords_in_active_pickable (display->image, coords, + FALSE, TRUE)) + { + /* set the cursor to the normal cursor */ + cursor = GIMP_CURSOR_MOUSE; + } + + /* if CTRL is pressed, change cursor to cross-hair */ + if ((state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == GDK_CONTROL_MASK) + { + cursor = GIMP_CURSOR_CROSSHAIR_SMALL; + } + /* otherwise, let the cursor now that we can't paint */ + else if (! GIMP_HEAL (GIMP_PAINT_TOOL (tool)->core)->src_drawable) + { + modifier = GIMP_CURSOR_MODIFIER_BAD; + } + + /* set the cursor and the modifier cursor */ + gimp_tool_control_set_cursor (tool->control, cursor); + gimp_tool_control_set_cursor_modifier (tool->control, modifier); + + /* chain up to the parent class */ + GIMP_TOOL_CLASS (gimp_heal_tool_parent_class)->cursor_update (tool, coords, + state, display); + +} + +static void +gimp_heal_tool_oper_update (GimpTool *tool, + GimpCoords *coords, + GdkModifierType state, + gboolean proximity, + GimpDisplay *display) +{ + GimpHealTool *heal_tool = GIMP_HEAL_TOOL (tool); + GimpHealOptions *options; + + options = GIMP_HEAL_OPTIONS (tool->tool_info->tool_options); + + if (proximity) + { + GimpPaintTool *paint_tool = GIMP_PAINT_TOOL (tool); + + paint_tool->status_ctrl = _("%s to set a new heal source"); + } + + /* chain up to the parent class */ + GIMP_TOOL_CLASS (gimp_heal_tool_parent_class)->oper_update (tool, coords, + state, proximity, + display); + + if (proximity) + { + GimpHeal *heal = GIMP_HEAL (GIMP_PAINT_TOOL (tool)->core); + + if (heal->src_drawable == NULL) + { + if (state & GDK_CONTROL_MASK) + /* if we haven't set the source drawable yet, make a notice to do so */ + gimp_tool_replace_status (tool, display, + _("Ctrl-Click to set a heal source.")); + else + { + gchar *status; + status = g_strdup_printf (_("%s%sClick to set a heal source."), + gimp_get_mod_name_control (), + gimp_get_mod_separator ()); + gimp_tool_replace_status (tool, display, status); + g_free (status); + } + } + else + { + /* pause drawing */ + gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool)); + + /* set the tool source to match the implementation source */ + heal_tool->src_x = heal->src_x; + heal_tool->src_y = heal->src_y; + + if (! heal->first_stroke) + { + /* set the coordinates based on the alignment type */ + if (options->align_mode == GIMP_HEAL_ALIGN_YES) + { + heal_tool->src_x = coords->x + heal->offset_x; + heal_tool->src_y = coords->y + heal->offset_y; + } + } + + /* resume drawing */ + gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool)); + } + } +} + +static void +gimp_heal_tool_draw (GimpDrawTool *draw_tool) +{ + GimpTool *tool = GIMP_TOOL (draw_tool); + GimpHealTool *heal_tool = GIMP_HEAL_TOOL (draw_tool); + GimpHeal *heal = GIMP_HEAL (GIMP_PAINT_TOOL (tool)->core); + GimpHealOptions *options; + + options = GIMP_HEAL_OPTIONS (tool->tool_info->tool_options); + + /* If we have a source drawable and display we can do the drawing */ + if ((heal->src_drawable) && (heal_tool->src_display)) + { + /* make a temporary display and keep track of offsets */ + GimpDisplay *tmp_display; + gint off_x; + gint off_y; + + /* gimp_item_offsets reveals the X and Y offsets of the first parameter. + * this gets the location of the drawable. */ + gimp_item_offsets (GIMP_ITEM (heal->src_drawable), &off_x, &off_y); + + /* store the display for later */ + tmp_display = draw_tool->display; + + /* set the display */ + draw_tool->display = heal_tool->src_display; + + /* convenience function to simplify drawing */ + gimp_draw_tool_draw_handle (draw_tool, + GIMP_HANDLE_CROSS, + heal_tool->src_x + off_x, + heal_tool->src_y + off_y, + TARGET_WIDTH, TARGET_HEIGHT, + GTK_ANCHOR_CENTER, + FALSE); + + /* restore settings after drawing */ + draw_tool->display = tmp_display; + } + + /* chain up to the parent draw function */ + GIMP_DRAW_TOOL_CLASS (gimp_heal_tool_parent_class)->draw (draw_tool); +} + +static GtkWidget * +gimp_heal_options_gui (GimpToolOptions *tool_options) +{ + GObject *config; + GtkWidget *vbox; + GtkWidget *button; + GtkWidget *table; + GtkWidget *combo; + + config = G_OBJECT (tool_options); + + vbox = gimp_paint_options_gui (tool_options); + + /* create and attach the sample merged checkbox */ + 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); + + /* create and attach the alignment options to a table */ + table = gtk_table_new (1, 2, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (table), 2); + gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0); + gtk_widget_show (table); + + combo = gimp_prop_enum_combo_box_new (config, "align-mode", 0, 0); + gimp_table_attach_aligned (GTK_TABLE (table), 0, 0, + _("Alignment:"), 0.0, 0.5, + combo, 1, FALSE); + + return vbox; +} diff --git a/app/tools/gimphealtool.h b/app/tools/gimphealtool.h new file mode 100644 index 0000000000..63ca48449d --- /dev/null +++ b/app/tools/gimphealtool.h @@ -0,0 +1,54 @@ +/* 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. + */ + +#ifndef __GIMP_HEAL_TOOL_H__ +#define __GIMP_HEAL_TOOL_H__ + +#include "gimppainttool.h" + +#define GIMP_TYPE_HEAL_TOOL (gimp_heal_tool_get_type ()) +#define GIMP_HEAL_TOOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_HEAL_TOOL, GimpHealTool)) +#define GIMP_HEAL_TOOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_HEAL_TOOL, GimpHealToolClass)) +#define GIMP_IS_HEAL_TOOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_HEAL_TOOL)) +#define GIMP_IS_HEAL_TOOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_HEAL_TOOL)) +#define GIMP_HEAL_TOOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_HEAL_TOOL, GimpHealToolClass)) + +typedef struct _GimpHealTool GimpHealTool; +typedef struct _GimpHealToolClass GimpHealToolClass; + +struct _GimpHealTool +{ + GimpPaintTool parent_instance; + + GimpDisplay *src_display; /* Detail about the source location to paint from */ + gint src_x; + gint src_y; +}; + +struct _GimpHealToolClass +{ + GimpPaintToolClass parent_class; +}; + + +void gimp_heal_tool_register (GimpToolRegisterCallback callback, + gpointer data); + +GType gimp_heal_tool_get_type (void) G_GNUC_CONST; + +#endif /* __GIMP_HEAL_TOOL_H__ */ diff --git a/cursors/tool-heal.png b/cursors/tool-heal.png new file mode 100644 index 0000000000..875d83f78c Binary files /dev/null and b/cursors/tool-heal.png differ diff --git a/cursors/xbm/tool-heal-mask.xbm b/cursors/xbm/tool-heal-mask.xbm new file mode 100644 index 0000000000..f5f88a875a --- /dev/null +++ b/cursors/xbm/tool-heal-mask.xbm @@ -0,0 +1,14 @@ +#define tool_heal_mask_width 32 +#define tool_heal_mask_height 32 +static unsigned char tool_heal_mask_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x04, + 0x00, 0x00, 0xa0, 0x01, 0x00, 0x00, 0xa0, 0x0d, 0x00, 0x00, 0x78, 0x05, + 0x00, 0x00, 0xbd, 0x55, 0x00, 0x00, 0xd8, 0x58, 0x00, 0x00, 0x80, 0x37, + 0x00, 0x00, 0xcc, 0x37, 0x00, 0x00, 0xc0, 0x7b, 0x00, 0x00, 0x12, 0x59, + 0x00, 0x00, 0x2a, 0x0b, 0x00, 0x00, 0xf8, 0x09, 0x00, 0x00, 0xc0, 0x01, + 0x00, 0x00, 0x10, 0x06, 0x00, 0x00, 0x60, 0x04 }; diff --git a/cursors/xbm/tool-heal.xbm b/cursors/xbm/tool-heal.xbm new file mode 100644 index 0000000000..e7e70bccec --- /dev/null +++ b/cursors/xbm/tool-heal.xbm @@ -0,0 +1,14 @@ +#define tool_heal_width 32 +#define tool_heal_height 32 +static unsigned char tool_heal_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x04, + 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x40, 0x01, + 0x00, 0x00, 0x21, 0x44, 0x00, 0x00, 0x10, 0x48, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x4c, 0x32, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x02, 0x48, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x88, 0x01, 0x00, 0x00, 0x80, 0x01, + 0x00, 0x00, 0x10, 0x06, 0x00, 0x00, 0x60, 0x04 };