gimp/app/paint/gimpheal.c

574 lines
20 KiB
C
Raw Normal View History

/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* gimpheal.c
* Copyright (C) Jean-Yves Couleaud <cjyves@free.fr>
* Copyright (C) 2013 Loren Merritt
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <stdint.h>
#include <string.h>
#include <gegl.h>
#include "libgimpbase/gimpbase.h"
#include "libgimpmath/gimpmath.h"
#include "paint-types.h"
#include "core/gimpbrush.h"
#include "core/gimpdrawable.h"
#include "core/gimpdynamics.h"
#include "core/gimperror.h"
#include "core/gimpimage.h"
#include "core/gimppickable.h"
#include "core/gimptempbuf.h"
#include "gimpheal.h"
#include "gimpsourceoptions.h"
#include "gimp-intl.h"
/* NOTES
*
* The method used here is similar to the lighting invariant correction
* method but slightly different: we do not divide the RGB components,
2013-01-27 23:52:38 +08:00
* but subtract them I2 = I0 - I1, where I0 is the sample image to be
* corrected, I1 is the reference pattern. Then we solve DeltaI=0
* (Laplace) with I2 Dirichlet conditions at the borders of the
* mask. The solver is a red/black checker Gauss-Seidel with over-relaxation.
* It could benefit from a multi-grid evaluation of an initial solution
* before the main iteration loop.
Merged the "soc-2006-healing-brush" branch. That branch is now officially 2006-09-02 Michael Natterer <mitch@gimp.org> Merged the "soc-2006-healing-brush" branch. That branch is now officially closed and all further fixes and changes have to be applied to HEAD. Did some minor adjustments, mostly small indentation and spacing fixes. Derive the tool from the newly introduced GimpBrushTool which did not exist when the branch was created. Thanks a lot to Kevin Sookocheff for this nice contribution! * app/paint/paint-enums.[ch]: new enum GimpHealAlignMode. * app/paint/Makefile.am * app/paint/makefile.msc * app/paint/gimpheal.[ch] * app/paint/gimphealoptions.[ch]: the heal core and its options. * app/paint/gimp-paint.c: register the heal core. * app/tools/Makefile.am * app/tools/makefile.msc * app/tools/gimphealtool.[ch]: the heal tool. * app/tools/gimp-tools.c: register the heal tool. * app/tools/gimppaintoptions-gui.c: show the widgets that are used by heal. * app/widgets/gimphelp-ids.h: the heal help ID. * tools/pdbgen/stddefs.pdb * tools/pdbgen/pdb/paint_tools.pdb: the heal PDB wrappers. * app/widgets/widgets-enums.h * app/widgets/gimpcursor.c * cursors/Makefile.am * cursors/makefile.msc * cursors/tool-heal.png * cursors/xbm/tool-heal.xbm * cursors/xbm/tool-heal-mask.xbm: a new cursor for the heal tool. * libgimpwidgets/gimpstock.[ch] * themes/Default/images/Makefile.am * themes/Default/images/makefile.msc * themes/Default/images/tools/stock-tool-heal-16.png * themes/Default/images/tools/stock-tool-heal-22.png: new stock icons for the heal tool. * app/pdb/internal_procs.c * app/pdb/paint_tools_cmds.c * libgimp/gimppainttools_pdb.[ch]: regenerated.
2006-09-03 02:54:35 +08:00
*
* I reduced the convergence criteria to 0.1% (0.001) as we are
* dealing here with RGB integer components, more is overkill.
*
* Jean-Yves Couleaud cjyves@free.fr
Merged the "soc-2006-healing-brush" branch. That branch is now officially 2006-09-02 Michael Natterer <mitch@gimp.org> Merged the "soc-2006-healing-brush" branch. That branch is now officially closed and all further fixes and changes have to be applied to HEAD. Did some minor adjustments, mostly small indentation and spacing fixes. Derive the tool from the newly introduced GimpBrushTool which did not exist when the branch was created. Thanks a lot to Kevin Sookocheff for this nice contribution! * app/paint/paint-enums.[ch]: new enum GimpHealAlignMode. * app/paint/Makefile.am * app/paint/makefile.msc * app/paint/gimpheal.[ch] * app/paint/gimphealoptions.[ch]: the heal core and its options. * app/paint/gimp-paint.c: register the heal core. * app/tools/Makefile.am * app/tools/makefile.msc * app/tools/gimphealtool.[ch]: the heal tool. * app/tools/gimp-tools.c: register the heal tool. * app/tools/gimppaintoptions-gui.c: show the widgets that are used by heal. * app/widgets/gimphelp-ids.h: the heal help ID. * tools/pdbgen/stddefs.pdb * tools/pdbgen/pdb/paint_tools.pdb: the heal PDB wrappers. * app/widgets/widgets-enums.h * app/widgets/gimpcursor.c * cursors/Makefile.am * cursors/makefile.msc * cursors/tool-heal.png * cursors/xbm/tool-heal.xbm * cursors/xbm/tool-heal-mask.xbm: a new cursor for the heal tool. * libgimpwidgets/gimpstock.[ch] * themes/Default/images/Makefile.am * themes/Default/images/makefile.msc * themes/Default/images/tools/stock-tool-heal-16.png * themes/Default/images/tools/stock-tool-heal-22.png: new stock icons for the heal tool. * app/pdb/internal_procs.c * app/pdb/paint_tools_cmds.c * libgimp/gimppainttools_pdb.[ch]: regenerated.
2006-09-03 02:54:35 +08:00
*/
static gboolean gimp_heal_start (GimpPaintCore *paint_core,
GimpDrawable *drawable,
GimpPaintOptions *paint_options,
const GimpCoords *coords,
GError **error);
static void gimp_heal_motion (GimpSourceCore *source_core,
GimpDrawable *drawable,
GimpPaintOptions *paint_options,
const GimpCoords *coords,
gdouble opacity,
GimpPickable *src_pickable,
GeglBuffer *src_buffer,
GeglRectangle *src_rect,
gint src_offset_x,
gint src_offset_y,
GeglBuffer *paint_buffer,
gint paint_buffer_x,
gint paint_buffer_y,
gint paint_area_offset_x,
gint paint_area_offset_y,
gint paint_area_width,
gint paint_area_height);
G_DEFINE_TYPE (GimpHeal, gimp_heal, GIMP_TYPE_SOURCE_CORE)
#define parent_class gimp_heal_parent_class
Merged the "soc-2006-healing-brush" branch. That branch is now officially 2006-09-02 Michael Natterer <mitch@gimp.org> Merged the "soc-2006-healing-brush" branch. That branch is now officially closed and all further fixes and changes have to be applied to HEAD. Did some minor adjustments, mostly small indentation and spacing fixes. Derive the tool from the newly introduced GimpBrushTool which did not exist when the branch was created. Thanks a lot to Kevin Sookocheff for this nice contribution! * app/paint/paint-enums.[ch]: new enum GimpHealAlignMode. * app/paint/Makefile.am * app/paint/makefile.msc * app/paint/gimpheal.[ch] * app/paint/gimphealoptions.[ch]: the heal core and its options. * app/paint/gimp-paint.c: register the heal core. * app/tools/Makefile.am * app/tools/makefile.msc * app/tools/gimphealtool.[ch]: the heal tool. * app/tools/gimp-tools.c: register the heal tool. * app/tools/gimppaintoptions-gui.c: show the widgets that are used by heal. * app/widgets/gimphelp-ids.h: the heal help ID. * tools/pdbgen/stddefs.pdb * tools/pdbgen/pdb/paint_tools.pdb: the heal PDB wrappers. * app/widgets/widgets-enums.h * app/widgets/gimpcursor.c * cursors/Makefile.am * cursors/makefile.msc * cursors/tool-heal.png * cursors/xbm/tool-heal.xbm * cursors/xbm/tool-heal-mask.xbm: a new cursor for the heal tool. * libgimpwidgets/gimpstock.[ch] * themes/Default/images/Makefile.am * themes/Default/images/makefile.msc * themes/Default/images/tools/stock-tool-heal-16.png * themes/Default/images/tools/stock-tool-heal-22.png: new stock icons for the heal tool. * app/pdb/internal_procs.c * app/pdb/paint_tools_cmds.c * libgimp/gimppainttools_pdb.[ch]: regenerated.
2006-09-03 02:54:35 +08:00
void
gimp_heal_register (Gimp *gimp,
GimpPaintRegisterCallback callback)
{
(* callback) (gimp,
GIMP_TYPE_HEAL,
GIMP_TYPE_SOURCE_OPTIONS,
"gimp-heal",
_("Heal"),
"gimp-tool-heal");
}
static void
gimp_heal_class_init (GimpHealClass *klass)
{
added GError** parameter to GimpPaintCore::start(). 2006-09-26 Michael Natterer <mitch@gimp.org> * app/paint/gimppaintcore.[ch]: added GError** parameter to GimpPaintCore::start(). * app/tools/gimppainttool.c (button_press): display the error in the statusbar. * app/paint/gimppaintcore-stroke.c: pass a NULL error, effectively swallowing mssages. Will fix that later. * app/paint/gimpbrushcore.c * app/paint/gimpclone.c * app/paint/gimpsourcecore.c: changed accordingly. Set the error instead of calling g_message(). * app/paint/gimpheal.c * app/paint/gimpperspectiveclone.c: implement start() and bail out early on indexed drawables instead of showing a g_message() in other functions that are called later. * app/tools/gimptool.[ch]: added GError** to GimpTool::initialize(). * app/tools/gimptool.c (gimp_tool_initialize): display the error in the statusbar. Keep the external API GError-free. * app/tools/gimprectangletool.[ch]: added GError** to gimp_rectangle_tool_initialize(). * app/tools/gimpbrightnesscontrasttool.c * app/tools/gimpcolorbalancetool.c * app/tools/gimpcolorizetool.c * app/tools/gimpcroptool.c * app/tools/gimpcurvestool.c * app/tools/gimphuesaturationtool.c * app/tools/gimpimagemaptool.c * app/tools/gimplevelstool.c * app/tools/gimpperspectiveclonetool.c * app/tools/gimpposterizetool.c * app/tools/gimpthresholdtool.c * app/tools/gimptransformtool.c: changed accordingly. Set the errors in initialize() instead of using gimp_message(). * app/tools/gimpblendtool.c: implement initialize() and bail out early on indexed images instead of showing a gimp_message() in button_press().
2006-09-27 04:55:40 +08:00
GimpPaintCoreClass *paint_core_class = GIMP_PAINT_CORE_CLASS (klass);
GimpSourceCoreClass *source_core_class = GIMP_SOURCE_CORE_CLASS (klass);
added GError** parameter to GimpPaintCore::start(). 2006-09-26 Michael Natterer <mitch@gimp.org> * app/paint/gimppaintcore.[ch]: added GError** parameter to GimpPaintCore::start(). * app/tools/gimppainttool.c (button_press): display the error in the statusbar. * app/paint/gimppaintcore-stroke.c: pass a NULL error, effectively swallowing mssages. Will fix that later. * app/paint/gimpbrushcore.c * app/paint/gimpclone.c * app/paint/gimpsourcecore.c: changed accordingly. Set the error instead of calling g_message(). * app/paint/gimpheal.c * app/paint/gimpperspectiveclone.c: implement start() and bail out early on indexed drawables instead of showing a g_message() in other functions that are called later. * app/tools/gimptool.[ch]: added GError** to GimpTool::initialize(). * app/tools/gimptool.c (gimp_tool_initialize): display the error in the statusbar. Keep the external API GError-free. * app/tools/gimprectangletool.[ch]: added GError** to gimp_rectangle_tool_initialize(). * app/tools/gimpbrightnesscontrasttool.c * app/tools/gimpcolorbalancetool.c * app/tools/gimpcolorizetool.c * app/tools/gimpcroptool.c * app/tools/gimpcurvestool.c * app/tools/gimphuesaturationtool.c * app/tools/gimpimagemaptool.c * app/tools/gimplevelstool.c * app/tools/gimpperspectiveclonetool.c * app/tools/gimpposterizetool.c * app/tools/gimpthresholdtool.c * app/tools/gimptransformtool.c: changed accordingly. Set the errors in initialize() instead of using gimp_message(). * app/tools/gimpblendtool.c: implement initialize() and bail out early on indexed images instead of showing a gimp_message() in button_press().
2006-09-27 04:55:40 +08:00
paint_core_class->start = gimp_heal_start;
source_core_class->motion = gimp_heal_motion;
}
static void
gimp_heal_init (GimpHeal *heal)
{
}
added GError** parameter to GimpPaintCore::start(). 2006-09-26 Michael Natterer <mitch@gimp.org> * app/paint/gimppaintcore.[ch]: added GError** parameter to GimpPaintCore::start(). * app/tools/gimppainttool.c (button_press): display the error in the statusbar. * app/paint/gimppaintcore-stroke.c: pass a NULL error, effectively swallowing mssages. Will fix that later. * app/paint/gimpbrushcore.c * app/paint/gimpclone.c * app/paint/gimpsourcecore.c: changed accordingly. Set the error instead of calling g_message(). * app/paint/gimpheal.c * app/paint/gimpperspectiveclone.c: implement start() and bail out early on indexed drawables instead of showing a g_message() in other functions that are called later. * app/tools/gimptool.[ch]: added GError** to GimpTool::initialize(). * app/tools/gimptool.c (gimp_tool_initialize): display the error in the statusbar. Keep the external API GError-free. * app/tools/gimprectangletool.[ch]: added GError** to gimp_rectangle_tool_initialize(). * app/tools/gimpbrightnesscontrasttool.c * app/tools/gimpcolorbalancetool.c * app/tools/gimpcolorizetool.c * app/tools/gimpcroptool.c * app/tools/gimpcurvestool.c * app/tools/gimphuesaturationtool.c * app/tools/gimpimagemaptool.c * app/tools/gimplevelstool.c * app/tools/gimpperspectiveclonetool.c * app/tools/gimpposterizetool.c * app/tools/gimpthresholdtool.c * app/tools/gimptransformtool.c: changed accordingly. Set the errors in initialize() instead of using gimp_message(). * app/tools/gimpblendtool.c: implement initialize() and bail out early on indexed images instead of showing a gimp_message() in button_press().
2006-09-27 04:55:40 +08:00
static gboolean
gimp_heal_start (GimpPaintCore *paint_core,
GimpDrawable *drawable,
GimpPaintOptions *paint_options,
const GimpCoords *coords,
added GError** parameter to GimpPaintCore::start(). 2006-09-26 Michael Natterer <mitch@gimp.org> * app/paint/gimppaintcore.[ch]: added GError** parameter to GimpPaintCore::start(). * app/tools/gimppainttool.c (button_press): display the error in the statusbar. * app/paint/gimppaintcore-stroke.c: pass a NULL error, effectively swallowing mssages. Will fix that later. * app/paint/gimpbrushcore.c * app/paint/gimpclone.c * app/paint/gimpsourcecore.c: changed accordingly. Set the error instead of calling g_message(). * app/paint/gimpheal.c * app/paint/gimpperspectiveclone.c: implement start() and bail out early on indexed drawables instead of showing a g_message() in other functions that are called later. * app/tools/gimptool.[ch]: added GError** to GimpTool::initialize(). * app/tools/gimptool.c (gimp_tool_initialize): display the error in the statusbar. Keep the external API GError-free. * app/tools/gimprectangletool.[ch]: added GError** to gimp_rectangle_tool_initialize(). * app/tools/gimpbrightnesscontrasttool.c * app/tools/gimpcolorbalancetool.c * app/tools/gimpcolorizetool.c * app/tools/gimpcroptool.c * app/tools/gimpcurvestool.c * app/tools/gimphuesaturationtool.c * app/tools/gimpimagemaptool.c * app/tools/gimplevelstool.c * app/tools/gimpperspectiveclonetool.c * app/tools/gimpposterizetool.c * app/tools/gimpthresholdtool.c * app/tools/gimptransformtool.c: changed accordingly. Set the errors in initialize() instead of using gimp_message(). * app/tools/gimpblendtool.c: implement initialize() and bail out early on indexed images instead of showing a gimp_message() in button_press().
2006-09-27 04:55:40 +08:00
GError **error)
{
GimpSourceCore *source_core = GIMP_SOURCE_CORE (paint_core);
if (! GIMP_PAINT_CORE_CLASS (parent_class)->start (paint_core, drawable,
paint_options, coords,
error))
{
return FALSE;
}
if (! source_core->set_source && gimp_drawable_is_indexed (drawable))
{
g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
_("Healing does not operate on indexed layers."));
added GError** parameter to GimpPaintCore::start(). 2006-09-26 Michael Natterer <mitch@gimp.org> * app/paint/gimppaintcore.[ch]: added GError** parameter to GimpPaintCore::start(). * app/tools/gimppainttool.c (button_press): display the error in the statusbar. * app/paint/gimppaintcore-stroke.c: pass a NULL error, effectively swallowing mssages. Will fix that later. * app/paint/gimpbrushcore.c * app/paint/gimpclone.c * app/paint/gimpsourcecore.c: changed accordingly. Set the error instead of calling g_message(). * app/paint/gimpheal.c * app/paint/gimpperspectiveclone.c: implement start() and bail out early on indexed drawables instead of showing a g_message() in other functions that are called later. * app/tools/gimptool.[ch]: added GError** to GimpTool::initialize(). * app/tools/gimptool.c (gimp_tool_initialize): display the error in the statusbar. Keep the external API GError-free. * app/tools/gimprectangletool.[ch]: added GError** to gimp_rectangle_tool_initialize(). * app/tools/gimpbrightnesscontrasttool.c * app/tools/gimpcolorbalancetool.c * app/tools/gimpcolorizetool.c * app/tools/gimpcroptool.c * app/tools/gimpcurvestool.c * app/tools/gimphuesaturationtool.c * app/tools/gimpimagemaptool.c * app/tools/gimplevelstool.c * app/tools/gimpperspectiveclonetool.c * app/tools/gimpposterizetool.c * app/tools/gimpthresholdtool.c * app/tools/gimptransformtool.c: changed accordingly. Set the errors in initialize() instead of using gimp_message(). * app/tools/gimpblendtool.c: implement initialize() and bail out early on indexed images instead of showing a gimp_message() in button_press().
2006-09-27 04:55:40 +08:00
return FALSE;
}
return TRUE;
}
/* Subtract bottom from top and store in result as a float
*/
static void
gimp_heal_sub (GeglBuffer *top_buffer,
const GeglRectangle *top_rect,
GeglBuffer *bottom_buffer,
const GeglRectangle *bottom_rect,
GeglBuffer *result_buffer,
const GeglRectangle *result_rect)
{
GeglBufferIterator *iter;
const Babl *format = gegl_buffer_get_format (top_buffer);
gint n_components = babl_format_get_n_components (format);
if (n_components == 2)
format = babl_format ("Y'A float");
else if (n_components == 4)
format = babl_format ("R'G'B'A float");
else
g_return_if_reached ();
iter = gegl_buffer_iterator_new (top_buffer, top_rect, 0, format,
GEGL_BUFFER_READ, GEGL_ABYSS_NONE);
gegl_buffer_iterator_add (iter, bottom_buffer, bottom_rect, 0, format,
GEGL_BUFFER_READ, GEGL_ABYSS_NONE);
Merged the "soc-2006-healing-brush" branch. That branch is now officially 2006-09-02 Michael Natterer <mitch@gimp.org> Merged the "soc-2006-healing-brush" branch. That branch is now officially closed and all further fixes and changes have to be applied to HEAD. Did some minor adjustments, mostly small indentation and spacing fixes. Derive the tool from the newly introduced GimpBrushTool which did not exist when the branch was created. Thanks a lot to Kevin Sookocheff for this nice contribution! * app/paint/paint-enums.[ch]: new enum GimpHealAlignMode. * app/paint/Makefile.am * app/paint/makefile.msc * app/paint/gimpheal.[ch] * app/paint/gimphealoptions.[ch]: the heal core and its options. * app/paint/gimp-paint.c: register the heal core. * app/tools/Makefile.am * app/tools/makefile.msc * app/tools/gimphealtool.[ch]: the heal tool. * app/tools/gimp-tools.c: register the heal tool. * app/tools/gimppaintoptions-gui.c: show the widgets that are used by heal. * app/widgets/gimphelp-ids.h: the heal help ID. * tools/pdbgen/stddefs.pdb * tools/pdbgen/pdb/paint_tools.pdb: the heal PDB wrappers. * app/widgets/widgets-enums.h * app/widgets/gimpcursor.c * cursors/Makefile.am * cursors/makefile.msc * cursors/tool-heal.png * cursors/xbm/tool-heal.xbm * cursors/xbm/tool-heal-mask.xbm: a new cursor for the heal tool. * libgimpwidgets/gimpstock.[ch] * themes/Default/images/Makefile.am * themes/Default/images/makefile.msc * themes/Default/images/tools/stock-tool-heal-16.png * themes/Default/images/tools/stock-tool-heal-22.png: new stock icons for the heal tool. * app/pdb/internal_procs.c * app/pdb/paint_tools_cmds.c * libgimp/gimppainttools_pdb.[ch]: regenerated.
2006-09-03 02:54:35 +08:00
gegl_buffer_iterator_add (iter, result_buffer, result_rect, 0,
babl_format_n (babl_type ("float"), n_components),
GEGL_BUFFER_WRITE, GEGL_ABYSS_NONE);
while (gegl_buffer_iterator_next (iter))
{
gfloat *t = iter->data[0];
gfloat *b = iter->data[1];
gfloat *r = iter->data[2];
gint length = iter->length * n_components;
while (length--)
2012-12-09 05:41:33 +08:00
*r++ = *t++ - *b++;
}
}
/* Add first to second and store in result
*/
static void
gimp_heal_add (GeglBuffer *first_buffer,
const GeglRectangle *first_rect,
GeglBuffer *second_buffer,
const GeglRectangle *second_rect,
GeglBuffer *result_buffer,
const GeglRectangle *result_rect)
{
GeglBufferIterator *iter;
const Babl *format = gegl_buffer_get_format (result_buffer);
gint n_components = babl_format_get_n_components (format);
if (n_components == 2)
format = babl_format ("Y'A float");
else if (n_components == 4)
format = babl_format ("R'G'B'A float");
else
g_return_if_reached ();
iter = gegl_buffer_iterator_new (first_buffer, first_rect, 0,
babl_format_n (babl_type ("float"),
n_components),
GEGL_BUFFER_READ, GEGL_ABYSS_NONE);
gegl_buffer_iterator_add (iter, second_buffer, second_rect, 0, format,
GEGL_BUFFER_READ, GEGL_ABYSS_NONE);
gegl_buffer_iterator_add (iter, result_buffer, result_rect, 0, format,
GEGL_BUFFER_WRITE, GEGL_ABYSS_NONE);
while (gegl_buffer_iterator_next (iter))
{
gfloat *f = iter->data[0];
gfloat *s = iter->data[1];
gfloat *r = iter->data[2];
gint length = iter->length * n_components;
while (length--)
*r++ = *f++ + *s++;
}
}
#if defined(__SSE__) && defined(__GNUC__) && __GNUC__ >= 4
static float
gimp_heal_laplace_iteration_sse (gfloat *pixels,
gfloat *Adiag,
gint *Aidx,
gfloat w,
gint nmask)
{
typedef float v4sf __attribute__((vector_size(16)));
gint i;
v4sf wv = { w, w, w, w };
v4sf err = { 0, 0, 0, 0 };
union { v4sf v; float f[4]; } erru;
#define Xv(j) (*(v4sf*)&pixels[Aidx[i * 5 + j]])
for (i = 0; i < nmask; i++)
{
v4sf a = { Adiag[i], Adiag[i], Adiag[i], Adiag[i] };
v4sf diff = a * Xv(0) - wv * (Xv(1) + Xv(2) + Xv(3) + Xv(4));
Xv(0) -= diff;
err += diff * diff;
}
erru.v = err;
return erru.f[0] + erru.f[1] + erru.f[2] + erru.f[3];
}
#endif
/* Perform one iteration of Gauss-Seidel, and return the sum squared residual.
*/
static float
gimp_heal_laplace_iteration (gfloat *pixels,
gfloat *Adiag,
gint *Aidx,
gfloat w,
gint nmask,
gint depth)
{
gint i, k;
gfloat err = 0;
#if defined(__SSE__) && defined(__GNUC__) && __GNUC__ >= 4
if (depth == 4)
return gimp_heal_laplace_iteration_sse (pixels, Adiag, Aidx, w, nmask);
#endif
for (i = 0; i < nmask; i++)
{
gint j0 = Aidx[i * 5 + 0];
gint j1 = Aidx[i * 5 + 1];
gint j2 = Aidx[i * 5 + 2];
gint j3 = Aidx[i * 5 + 3];
gint j4 = Aidx[i * 5 + 4];
gfloat a = Adiag[i];
for (k = 0; k < depth; k++)
{
gfloat diff = (a * pixels[j0 + k] -
w * (pixels[j1 + k] +
pixels[j2 + k] +
pixels[j3 + k] +
pixels[j4 + k]));
pixels[j0 + k] -= diff;
err += diff * diff;
}
}
return err;
}
/* Solve the laplace equation for pixels and store the result in-place.
*/
static void
gimp_heal_laplace_loop (gfloat *pixels,
gint height,
gint depth,
gint width,
guchar *mask)
{
/* Tolerate a total deviation-from-smoothness of 0.1 LSBs at 8bit depth. */
#define EPSILON (0.1/255)
#define MAX_ITER 500
gint i, j, iter, parity, nmask, zero;
gfloat *Adiag;
gint *Aidx;
gfloat w;
Adiag = g_new (gfloat, width * height);
Aidx = g_new (gint, 5 * width * height);
/* All off-diagonal elements of A are either -1 or 0. We could store it as a
* general-purpose sparse matrix, but that adds some unnecessary overhead to
* the inner loop. Instead, assume exactly 4 off-diagonal elements in each
* row, all of which have value -1. Any row that in fact wants less than 4
* coefs can put them in a dummy column to be multiplied by an empty pixel.
*/
zero = depth * width * height;
memset (pixels + zero, 0, depth * sizeof (gfloat));
/* Construct the system of equations.
* Arrange Aidx in checkerboard order, so that a single linear pass over that
* array results updating all of the red cells and then all of the black cells.
*/
nmask = 0;
for (parity = 0; parity < 2; parity++)
for (i = 0; i < height; i++)
for (j = (i&1)^parity; j < width; j+=2)
if (mask[j + i * width])
{
#define A_NEIGHBOR(o,di,dj) \
if ((dj<0 && j==0) || (dj>0 && j==width-1) || (di<0 && i==0) || (di>0 && i==height-1)) \
Aidx[o + nmask * 5] = zero; \
else \
Aidx[o + nmask * 5] = ((i + di) * width + (j + dj)) * depth;
/* Omit Dirichlet conditions for any neighbors off the
* edge of the canvas.
*/
Adiag[nmask] = 4 - (i==0) - (j==0) - (i==height-1) - (j==width-1);
A_NEIGHBOR (0, 0, 0);
A_NEIGHBOR (1, 0, 1);
A_NEIGHBOR (2, 1, 0);
A_NEIGHBOR (3, 0, -1);
A_NEIGHBOR (4, -1, 0);
nmask++;
}
/* Empirically optimal over-relaxation factor. (Benchmarked on
* round brushes, at least. I don't know whether aspect ratio
* affects it.)
*/
w = 2.0 - 1.0 / (0.1575 * sqrt (nmask) + 0.8);
w *= 0.25;
for (i = 0; i < nmask; i++)
Adiag[i] *= w;
/* Gauss-Seidel with successive over-relaxation */
for (iter = 0; iter < MAX_ITER; iter++)
{
gfloat err = gimp_heal_laplace_iteration (pixels, Adiag, Aidx,
w, nmask, depth);
if (err < EPSILON * EPSILON * w * w)
break;
}
g_free (Adiag);
g_free (Aidx);
}
/* Original Algorithm Design:
Merged the "soc-2006-healing-brush" branch. That branch is now officially 2006-09-02 Michael Natterer <mitch@gimp.org> Merged the "soc-2006-healing-brush" branch. That branch is now officially closed and all further fixes and changes have to be applied to HEAD. Did some minor adjustments, mostly small indentation and spacing fixes. Derive the tool from the newly introduced GimpBrushTool which did not exist when the branch was created. Thanks a lot to Kevin Sookocheff for this nice contribution! * app/paint/paint-enums.[ch]: new enum GimpHealAlignMode. * app/paint/Makefile.am * app/paint/makefile.msc * app/paint/gimpheal.[ch] * app/paint/gimphealoptions.[ch]: the heal core and its options. * app/paint/gimp-paint.c: register the heal core. * app/tools/Makefile.am * app/tools/makefile.msc * app/tools/gimphealtool.[ch]: the heal tool. * app/tools/gimp-tools.c: register the heal tool. * app/tools/gimppaintoptions-gui.c: show the widgets that are used by heal. * app/widgets/gimphelp-ids.h: the heal help ID. * tools/pdbgen/stddefs.pdb * tools/pdbgen/pdb/paint_tools.pdb: the heal PDB wrappers. * app/widgets/widgets-enums.h * app/widgets/gimpcursor.c * cursors/Makefile.am * cursors/makefile.msc * cursors/tool-heal.png * cursors/xbm/tool-heal.xbm * cursors/xbm/tool-heal-mask.xbm: a new cursor for the heal tool. * libgimpwidgets/gimpstock.[ch] * themes/Default/images/Makefile.am * themes/Default/images/makefile.msc * themes/Default/images/tools/stock-tool-heal-16.png * themes/Default/images/tools/stock-tool-heal-22.png: new stock icons for the heal tool. * app/pdb/internal_procs.c * app/pdb/paint_tools_cmds.c * libgimp/gimppainttools_pdb.[ch]: regenerated.
2006-09-03 02:54:35 +08:00
*
* T. Georgiev, "Photoshop Healing Brush: a Tool for Seamless Cloning
* http://www.tgeorgiev.net/Photoshop_Healing.pdf
*/
static void
gimp_heal (GeglBuffer *src_buffer,
const GeglRectangle *src_rect,
GeglBuffer *dest_buffer,
const GeglRectangle *dest_rect,
GeglBuffer *mask_buffer,
const GeglRectangle *mask_rect)
{
const Babl *src_format;
const Babl *dest_format;
2012-12-09 05:41:33 +08:00
gint src_components;
gint dest_components;
gint width;
gint height;
gfloat *diff, *diff_alloc;
GeglBuffer *diff_buffer;
guchar *mask;
src_format = gegl_buffer_get_format (src_buffer);
dest_format = gegl_buffer_get_format (dest_buffer);
2012-12-09 05:41:33 +08:00
src_components = babl_format_get_n_components (src_format);
dest_components = babl_format_get_n_components (dest_format);
width = gegl_buffer_get_width (src_buffer);
height = gegl_buffer_get_height (src_buffer);
2012-12-09 05:41:33 +08:00
g_return_if_fail (src_components == dest_components);
diff_alloc = g_new (gfloat, 4 + (width * height + 1) * src_components);
diff = (gfloat*)(((uintptr_t)diff_alloc + 15) & ~15);
diff_buffer =
gegl_buffer_linear_new_from_data (diff,
babl_format_n (babl_type ("float"),
2012-12-09 05:41:33 +08:00
src_components),
GEGL_RECTANGLE (0, 0, width, height),
GEGL_AUTO_ROWSTRIDE,
(GDestroyNotify) g_free, diff_alloc);
/* subtract pattern from image and store the result as a float in diff */
gimp_heal_sub (dest_buffer, dest_rect,
src_buffer, src_rect,
diff_buffer, GEGL_RECTANGLE (0, 0, width, height));
mask = g_new (guchar, mask_rect->width * mask_rect->height);
gegl_buffer_get (mask_buffer, mask_rect, 1.0, babl_format ("Y u8"),
mask, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
gimp_heal_laplace_loop (diff, height, src_components, width, mask);
g_free (mask);
Merged the "soc-2006-healing-brush" branch. That branch is now officially 2006-09-02 Michael Natterer <mitch@gimp.org> Merged the "soc-2006-healing-brush" branch. That branch is now officially closed and all further fixes and changes have to be applied to HEAD. Did some minor adjustments, mostly small indentation and spacing fixes. Derive the tool from the newly introduced GimpBrushTool which did not exist when the branch was created. Thanks a lot to Kevin Sookocheff for this nice contribution! * app/paint/paint-enums.[ch]: new enum GimpHealAlignMode. * app/paint/Makefile.am * app/paint/makefile.msc * app/paint/gimpheal.[ch] * app/paint/gimphealoptions.[ch]: the heal core and its options. * app/paint/gimp-paint.c: register the heal core. * app/tools/Makefile.am * app/tools/makefile.msc * app/tools/gimphealtool.[ch]: the heal tool. * app/tools/gimp-tools.c: register the heal tool. * app/tools/gimppaintoptions-gui.c: show the widgets that are used by heal. * app/widgets/gimphelp-ids.h: the heal help ID. * tools/pdbgen/stddefs.pdb * tools/pdbgen/pdb/paint_tools.pdb: the heal PDB wrappers. * app/widgets/widgets-enums.h * app/widgets/gimpcursor.c * cursors/Makefile.am * cursors/makefile.msc * cursors/tool-heal.png * cursors/xbm/tool-heal.xbm * cursors/xbm/tool-heal-mask.xbm: a new cursor for the heal tool. * libgimpwidgets/gimpstock.[ch] * themes/Default/images/Makefile.am * themes/Default/images/makefile.msc * themes/Default/images/tools/stock-tool-heal-16.png * themes/Default/images/tools/stock-tool-heal-22.png: new stock icons for the heal tool. * app/pdb/internal_procs.c * app/pdb/paint_tools_cmds.c * libgimp/gimppainttools_pdb.[ch]: regenerated.
2006-09-03 02:54:35 +08:00
/* add solution to original image and store in dest */
gimp_heal_add (diff_buffer, GEGL_RECTANGLE (0, 0, width, height),
src_buffer, src_rect,
dest_buffer, dest_rect);
g_object_unref (diff_buffer);
}
static void
gimp_heal_motion (GimpSourceCore *source_core,
GimpDrawable *drawable,
GimpPaintOptions *paint_options,
const GimpCoords *coords,
gdouble opacity,
GimpPickable *src_pickable,
GeglBuffer *src_buffer,
GeglRectangle *src_rect,
gint src_offset_x,
gint src_offset_y,
GeglBuffer *paint_buffer,
gint paint_buffer_x,
gint paint_buffer_y,
gint paint_area_offset_x,
gint paint_area_offset_y,
gint paint_area_width,
gint paint_area_height)
{
GimpPaintCore *paint_core = GIMP_PAINT_CORE (source_core);
GimpContext *context = GIMP_CONTEXT (paint_options);
GimpDynamics *dynamics = GIMP_BRUSH_CORE (paint_core)->dynamics;
GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable));
GeglBuffer *src_copy;
GeglBuffer *mask_buffer;
const GimpTempBuf *mask_buf;
gdouble fade_point;
gdouble hardness;
gint mask_off_x;
gint mask_off_y;
2008-05-23 00:38:57 +08:00
fade_point = gimp_paint_options_get_fade (paint_options, image,
paint_core->pixel_dist);
2009-10-03 23:53:25 +08:00
hardness = gimp_dynamics_get_linear_value (dynamics,
GIMP_DYNAMICS_OUTPUT_HARDNESS,
coords,
paint_options,
fade_point);
mask_buf = gimp_brush_core_get_brush_mask (GIMP_BRUSH_CORE (source_core),
coords,
2008-05-23 00:38:57 +08:00
GIMP_BRUSH_HARD,
hardness);
/* check that all buffers are of the same size */
if (src_rect->width != gegl_buffer_get_width (paint_buffer) ||
src_rect->height != gegl_buffer_get_height (paint_buffer))
{
/* this generally means that the source point has hit the edge
* of the layer, so it is not an error and we should not
* complain, just don't do anything
*/
return;
}
/* heal should work in perceptual space, use R'G'B' instead of RGB */
src_copy = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
src_rect->width,
src_rect->height),
babl_format ("R'G'B'A float"));
gegl_buffer_copy (src_buffer,
src_rect,
src_copy,
GEGL_RECTANGLE (0, 0,
src_rect->width,
src_rect->height));
gegl_buffer_copy (gimp_drawable_get_buffer (drawable),
GEGL_RECTANGLE (paint_buffer_x, paint_buffer_y,
gegl_buffer_get_width (paint_buffer),
gegl_buffer_get_height (paint_buffer)),
paint_buffer,
GEGL_RECTANGLE (paint_area_offset_x,
paint_area_offset_y,
paint_area_width,
paint_area_height));
mask_buffer = gimp_temp_buf_create_buffer ((GimpTempBuf *) mask_buf);
/* find the offset of the brush mask's rect */
{
gint x = (gint) floor (coords->x) - (gegl_buffer_get_width (mask_buffer) >> 1);
gint y = (gint) floor (coords->y) - (gegl_buffer_get_height (mask_buffer) >> 1);
mask_off_x = (x < 0) ? -x : 0;
mask_off_y = (y < 0) ? -y : 0;
}
gimp_heal (src_copy,
GEGL_RECTANGLE (0, 0,
gegl_buffer_get_width (src_copy),
gegl_buffer_get_height (src_copy)),
paint_buffer,
GEGL_RECTANGLE (paint_area_offset_x,
paint_area_offset_y,
paint_area_width,
paint_area_height),
mask_buffer,
GEGL_RECTANGLE (mask_off_x, mask_off_y,
paint_area_width,
paint_area_height));
g_object_unref (src_copy);
g_object_unref (mask_buffer);
Merged the "soc-2006-healing-brush" branch. That branch is now officially 2006-09-02 Michael Natterer <mitch@gimp.org> Merged the "soc-2006-healing-brush" branch. That branch is now officially closed and all further fixes and changes have to be applied to HEAD. Did some minor adjustments, mostly small indentation and spacing fixes. Derive the tool from the newly introduced GimpBrushTool which did not exist when the branch was created. Thanks a lot to Kevin Sookocheff for this nice contribution! * app/paint/paint-enums.[ch]: new enum GimpHealAlignMode. * app/paint/Makefile.am * app/paint/makefile.msc * app/paint/gimpheal.[ch] * app/paint/gimphealoptions.[ch]: the heal core and its options. * app/paint/gimp-paint.c: register the heal core. * app/tools/Makefile.am * app/tools/makefile.msc * app/tools/gimphealtool.[ch]: the heal tool. * app/tools/gimp-tools.c: register the heal tool. * app/tools/gimppaintoptions-gui.c: show the widgets that are used by heal. * app/widgets/gimphelp-ids.h: the heal help ID. * tools/pdbgen/stddefs.pdb * tools/pdbgen/pdb/paint_tools.pdb: the heal PDB wrappers. * app/widgets/widgets-enums.h * app/widgets/gimpcursor.c * cursors/Makefile.am * cursors/makefile.msc * cursors/tool-heal.png * cursors/xbm/tool-heal.xbm * cursors/xbm/tool-heal-mask.xbm: a new cursor for the heal tool. * libgimpwidgets/gimpstock.[ch] * themes/Default/images/Makefile.am * themes/Default/images/makefile.msc * themes/Default/images/tools/stock-tool-heal-16.png * themes/Default/images/tools/stock-tool-heal-22.png: new stock icons for the heal tool. * app/pdb/internal_procs.c * app/pdb/paint_tools_cmds.c * libgimp/gimppainttools_pdb.[ch]: regenerated.
2006-09-03 02:54:35 +08:00
/* replace the canvas with our healed data */
gimp_brush_core_replace_canvas (GIMP_BRUSH_CORE (paint_core), drawable,
coords,
MIN (opacity, GIMP_OPACITY_OPAQUE),
gimp_context_get_opacity (context),
gimp_paint_options_get_brush_mode (paint_options),
2008-05-23 00:38:57 +08:00
hardness,
GIMP_PAINT_INCREMENTAL);
}