Partially implemented sample-merged healing.

This commit is contained in:
Kevin Sookocheff 2006-08-15 21:15:59 +00:00
parent 2a97cb07c1
commit 02156f9a74
9 changed files with 1595 additions and 0 deletions

795
app/paint/gimpheal.c Normal file
View File

@ -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 <stdlib.h>
#include <string.h>
#include <glib-object.h>
#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");
}

64
app/paint/gimpheal.h Normal file
View File

@ -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__ */

114
app/paint/gimphealoptions.c Normal file
View File

@ -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 <glib-object.h>
#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;
}
}

View File

@ -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__ */

492
app/tools/gimphealtool.c Normal file
View File

@ -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 <gtk/gtk.h>
#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;
}

54
app/tools/gimphealtool.h Normal file
View File

@ -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__ */

BIN
cursors/tool-heal.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1020 B

View File

@ -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 };

14
cursors/xbm/tool-heal.xbm Normal file
View File

@ -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 };