2006-12-10 05:33:38 +08:00
|
|
|
/* GIMP - The GNU Image Manipulation Program
|
1999-07-02 00:52:50 +08:00
|
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
|
|
*
|
2009-01-18 06:28:01 +08:00
|
|
|
* This program is free software: you can redistribute it and/or modify
|
1999-07-02 00:52:50 +08:00
|
|
|
* it under the terms of the GNU General Public License as published by
|
2009-01-18 06:28:01 +08:00
|
|
|
* the Free Software Foundation; either version 3 of the License, or
|
1999-07-02 00:52:50 +08:00
|
|
|
* (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
|
2018-07-12 05:27:07 +08:00
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
1999-07-02 00:52:50 +08:00
|
|
|
*/
|
2000-12-17 05:37:03 +08:00
|
|
|
|
app/appenv.h New file. Includes <math.h>. Move G_PI, RINT(), ROUND() etc
1999-09-01 Tor Lillqvist <tml@iki.fi>
* app/appenv.h
* libgimp/gimpmath.h: New file. Includes <math.h>. Move G_PI,
RINT(), ROUND() etc from app/appenv.h here, so plug-ins can
use them, too. Remove some commented-out old stuff in appenv.h.
* libgimp/gimp.h: Include gimpmath.h.
* libgimp/gimp.c (gimp_main): Win32: Don't install signal
handlers, we can't do anything useful in the handler ourselves
anyway (it would be nice to print out a backtrace, but that seems
pretty hard to do, even if not impossible). Let Windows inform the
user about the crash. If the plug-in was compiled with MSVC, and
the user also has it, she is offered a chance to start the
debugger automatically anyway.
* app/*several*.c: Include gimpmath.h for G_PI etc. Don't include
<math.h>, as gimpmath.h includes it.
* plug-ins/*/*many*.c: Include config.h. Don't include <math.h>.
Remove all the duplicated definitions of G_PI and rint(). Use
RINT() instead of rint().
* app/app_procs.[ch]: app_exit() takes a gboolean.
* app/batch.c
* app/commands.c
* app/interface.c: Call app_exit() with FALSE or TRUE.
* app/main.c (on_error): Call gimp_fatal_error. (main): Don't
install any signal handler on Win32 here, either.
* app/errors.c (gimp_fatal_error, gimp_terminate): Win32: Format
the message and call MessageBox with it. g_on_error_query doesn't
do anything useful on Win32, and printf'ing a message to stdout or
stderr doesn't do anything, either, in a windowing application.
1999-09-02 04:30:56 +08:00
|
|
|
#include "config.h"
|
|
|
|
|
2013-10-15 07:58:39 +08:00
|
|
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
2008-10-10 04:24:04 +08:00
|
|
|
#include <gegl.h>
|
app/appenv.h New file. Includes <math.h>. Move G_PI, RINT(), ROUND() etc
1999-09-01 Tor Lillqvist <tml@iki.fi>
* app/appenv.h
* libgimp/gimpmath.h: New file. Includes <math.h>. Move G_PI,
RINT(), ROUND() etc from app/appenv.h here, so plug-ins can
use them, too. Remove some commented-out old stuff in appenv.h.
* libgimp/gimp.h: Include gimpmath.h.
* libgimp/gimp.c (gimp_main): Win32: Don't install signal
handlers, we can't do anything useful in the handler ourselves
anyway (it would be nice to print out a backtrace, but that seems
pretty hard to do, even if not impossible). Let Windows inform the
user about the crash. If the plug-in was compiled with MSVC, and
the user also has it, she is offered a chance to start the
debugger automatically anyway.
* app/*several*.c: Include gimpmath.h for G_PI etc. Don't include
<math.h>, as gimpmath.h includes it.
* plug-ins/*/*many*.c: Include config.h. Don't include <math.h>.
Remove all the duplicated definitions of G_PI and rint(). Use
RINT() instead of rint().
* app/app_procs.[ch]: app_exit() takes a gboolean.
* app/batch.c
* app/commands.c
* app/interface.c: Call app_exit() with FALSE or TRUE.
* app/main.c (on_error): Call gimp_fatal_error. (main): Don't
install any signal handler on Win32 here, either.
* app/errors.c (gimp_fatal_error, gimp_terminate): Win32: Format
the message and call MessageBox with it. g_on_error_query doesn't
do anything useful on Win32, and printf'ing a message to stdout or
stderr doesn't do anything, either, in a windowing application.
1999-09-02 04:30:56 +08:00
|
|
|
|
2001-01-24 07:56:18 +08:00
|
|
|
#include "libgimpmath/gimpmath.h"
|
|
|
|
|
2002-02-15 03:31:16 +08:00
|
|
|
#include "paint-types.h"
|
2000-12-17 05:37:03 +08:00
|
|
|
|
2012-04-18 06:32:10 +08:00
|
|
|
#include "gegl/gimp-gegl-loops.h"
|
2012-03-20 04:09:35 +08:00
|
|
|
#include "gegl/gimp-gegl-utils.h"
|
|
|
|
|
2018-03-30 01:53:12 +08:00
|
|
|
#include "core/gimp-palettes.h"
|
2001-05-09 10:32:03 +08:00
|
|
|
#include "core/gimpbrush.h"
|
2018-07-06 18:31:06 +08:00
|
|
|
#include "core/gimpbrush-header.h"
|
2001-05-09 10:32:03 +08:00
|
|
|
#include "core/gimpdrawable.h"
|
2009-10-11 21:41:54 +08:00
|
|
|
#include "core/gimpdynamics.h"
|
2001-07-07 20:17:23 +08:00
|
|
|
#include "core/gimpimage.h"
|
2004-07-14 07:04:05 +08:00
|
|
|
#include "core/gimppickable.h"
|
2016-01-28 02:13:17 +08:00
|
|
|
#include "core/gimpsymmetry.h"
|
2012-04-09 06:59:20 +08:00
|
|
|
#include "core/gimptempbuf.h"
|
2001-05-09 10:32:03 +08:00
|
|
|
|
2002-02-15 03:31:16 +08:00
|
|
|
#include "gimpsmudge.h"
|
2003-02-05 22:39:40 +08:00
|
|
|
#include "gimpsmudgeoptions.h"
|
2001-04-12 01:20:34 +08:00
|
|
|
|
2003-03-26 00:38:19 +08:00
|
|
|
#include "gimp-intl.h"
|
2003-02-14 22:14:29 +08:00
|
|
|
|
2000-04-28 01:27:28 +08:00
|
|
|
|
2006-09-14 01:32:36 +08:00
|
|
|
static void gimp_smudge_finalize (GObject *object);
|
|
|
|
|
|
|
|
static void gimp_smudge_paint (GimpPaintCore *paint_core,
|
|
|
|
GimpDrawable *drawable,
|
|
|
|
GimpPaintOptions *paint_options,
|
2016-01-28 02:13:17 +08:00
|
|
|
GimpSymmetry *sym,
|
2006-09-14 01:32:36 +08:00
|
|
|
GimpPaintState paint_state,
|
|
|
|
guint32 time);
|
|
|
|
static gboolean gimp_smudge_start (GimpPaintCore *paint_core,
|
|
|
|
GimpDrawable *drawable,
|
2009-05-03 18:08:21 +08:00
|
|
|
GimpPaintOptions *paint_options,
|
2016-01-28 02:13:17 +08:00
|
|
|
GimpSymmetry *sym);
|
2006-09-14 01:32:36 +08:00
|
|
|
static void gimp_smudge_motion (GimpPaintCore *paint_core,
|
|
|
|
GimpDrawable *drawable,
|
2009-05-03 18:08:21 +08:00
|
|
|
GimpPaintOptions *paint_options,
|
2016-01-28 02:13:17 +08:00
|
|
|
GimpSymmetry *sym);
|
2006-09-14 01:32:36 +08:00
|
|
|
|
2012-01-18 04:26:32 +08:00
|
|
|
static void gimp_smudge_accumulator_coords (GimpPaintCore *paint_core,
|
|
|
|
const GimpCoords *coords,
|
2016-01-28 02:13:17 +08:00
|
|
|
gint stroke,
|
2012-01-18 04:26:32 +08:00
|
|
|
gint *x,
|
|
|
|
gint *y);
|
|
|
|
|
|
|
|
static void gimp_smudge_accumulator_size (GimpPaintOptions *paint_options,
|
2014-11-14 02:32:59 +08:00
|
|
|
const GimpCoords *coords,
|
2012-01-18 04:26:32 +08:00
|
|
|
gint *accumulator_size);
|
1999-07-02 00:52:50 +08:00
|
|
|
|
2000-01-14 20:41:00 +08:00
|
|
|
|
2006-05-15 17:46:31 +08:00
|
|
|
G_DEFINE_TYPE (GimpSmudge, gimp_smudge, GIMP_TYPE_BRUSH_CORE)
|
2005-12-08 05:11:53 +08:00
|
|
|
|
|
|
|
#define parent_class gimp_smudge_parent_class
|
2001-11-21 07:00:47 +08:00
|
|
|
|
1999-07-02 00:52:50 +08:00
|
|
|
|
2002-02-27 21:57:49 +08:00
|
|
|
void
|
|
|
|
gimp_smudge_register (Gimp *gimp,
|
|
|
|
GimpPaintRegisterCallback callback)
|
|
|
|
{
|
2003-02-14 22:14:29 +08:00
|
|
|
(* callback) (gimp,
|
|
|
|
GIMP_TYPE_SMUDGE,
|
|
|
|
GIMP_TYPE_SMUDGE_OPTIONS,
|
2005-12-28 02:57:01 +08:00
|
|
|
"gimp-smudge",
|
|
|
|
_("Smudge"),
|
|
|
|
"gimp-tool-smudge");
|
2002-02-27 21:57:49 +08:00
|
|
|
}
|
|
|
|
|
2001-04-12 01:20:34 +08:00
|
|
|
static void
|
2002-02-15 03:31:16 +08:00
|
|
|
gimp_smudge_class_init (GimpSmudgeClass *klass)
|
1999-07-02 00:52:50 +08:00
|
|
|
{
|
2004-05-26 04:41:09 +08:00
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
GimpPaintCoreClass *paint_core_class = GIMP_PAINT_CORE_CLASS (klass);
|
2004-05-26 17:32:03 +08:00
|
|
|
GimpBrushCoreClass *brush_core_class = GIMP_BRUSH_CORE_CLASS (klass);
|
1999-07-02 00:52:50 +08:00
|
|
|
|
2007-11-13 17:30:46 +08:00
|
|
|
object_class->finalize = gimp_smudge_finalize;
|
2004-05-26 17:32:03 +08:00
|
|
|
|
2007-11-13 17:30:46 +08:00
|
|
|
paint_core_class->paint = gimp_smudge_paint;
|
2002-06-09 21:56:09 +08:00
|
|
|
|
2017-07-16 00:36:53 +08:00
|
|
|
brush_core_class->handles_changing_brush = TRUE;
|
|
|
|
brush_core_class->handles_transforming_brush = TRUE;
|
2012-01-18 04:26:32 +08:00
|
|
|
brush_core_class->handles_dynamic_transforming_brush = TRUE;
|
2001-04-12 01:20:34 +08:00
|
|
|
}
|
1999-07-02 00:52:50 +08:00
|
|
|
|
2001-04-12 01:20:34 +08:00
|
|
|
static void
|
2002-02-15 03:31:16 +08:00
|
|
|
gimp_smudge_init (GimpSmudge *smudge)
|
2001-04-12 01:20:34 +08:00
|
|
|
{
|
2002-06-09 21:56:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_smudge_finalize (GObject *object)
|
|
|
|
{
|
2004-05-26 04:41:09 +08:00
|
|
|
GimpSmudge *smudge = GIMP_SMUDGE (object);
|
2002-06-09 21:56:09 +08:00
|
|
|
|
2016-01-28 02:13:17 +08:00
|
|
|
if (smudge->accum_buffers)
|
2002-06-09 21:56:09 +08:00
|
|
|
{
|
2016-01-28 02:13:17 +08:00
|
|
|
GList *iter;
|
|
|
|
|
|
|
|
for (iter = smudge->accum_buffers; iter; iter = g_list_next (iter))
|
|
|
|
{
|
|
|
|
if (iter->data)
|
|
|
|
g_object_unref (iter->data);
|
|
|
|
}
|
2017-07-16 00:36:53 +08:00
|
|
|
|
|
|
|
g_list_free (smudge->accum_buffers);
|
|
|
|
smudge->accum_buffers = NULL;
|
2002-06-09 21:56:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
1999-07-02 00:52:50 +08:00
|
|
|
}
|
|
|
|
|
2001-04-12 01:20:34 +08:00
|
|
|
static void
|
2004-07-19 22:37:40 +08:00
|
|
|
gimp_smudge_paint (GimpPaintCore *paint_core,
|
|
|
|
GimpDrawable *drawable,
|
|
|
|
GimpPaintOptions *paint_options,
|
2016-01-28 02:13:17 +08:00
|
|
|
GimpSymmetry *sym,
|
2004-07-19 22:37:40 +08:00
|
|
|
GimpPaintState paint_state,
|
|
|
|
guint32 time)
|
1999-07-02 00:52:50 +08:00
|
|
|
{
|
2004-05-26 04:41:09 +08:00
|
|
|
GimpSmudge *smudge = GIMP_SMUDGE (paint_core);
|
2001-11-20 21:53:21 +08:00
|
|
|
|
2002-02-15 03:31:16 +08:00
|
|
|
switch (paint_state)
|
1999-07-02 00:52:50 +08:00
|
|
|
{
|
2018-03-30 01:53:12 +08:00
|
|
|
case GIMP_PAINT_STATE_INIT:
|
|
|
|
{
|
|
|
|
GimpContext *context = GIMP_CONTEXT (paint_options);
|
|
|
|
GimpSmudgeOptions *options = GIMP_SMUDGE_OPTIONS (paint_options);
|
|
|
|
GimpBrushCore *brush_core = GIMP_BRUSH_CORE (paint_core);
|
|
|
|
GimpDynamics *dynamics = gimp_context_get_dynamics (context);
|
|
|
|
|
|
|
|
/* Don't add to color history when
|
|
|
|
* 1. pure smudging (flow=0)
|
|
|
|
* 2. color is from gradient or pixmap brushes
|
|
|
|
*/
|
|
|
|
if (options->flow > 0.0 &&
|
|
|
|
! gimp_dynamics_is_output_enabled (dynamics, GIMP_DYNAMICS_OUTPUT_COLOR) &&
|
|
|
|
! (brush_core->brush && gimp_brush_get_pixmap (brush_core->brush)))
|
|
|
|
{
|
|
|
|
GimpRGB foreground;
|
|
|
|
|
|
|
|
gimp_context_get_foreground (context, &foreground);
|
|
|
|
gimp_palettes_add_color_history (context->gimp, &foreground);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2004-07-19 22:37:40 +08:00
|
|
|
case GIMP_PAINT_STATE_MOTION:
|
2002-06-09 21:56:09 +08:00
|
|
|
/* initialization fails if the user starts outside the drawable */
|
|
|
|
if (! smudge->initialized)
|
2006-04-12 20:49:29 +08:00
|
|
|
smudge->initialized = gimp_smudge_start (paint_core, drawable,
|
2016-01-28 02:13:17 +08:00
|
|
|
paint_options, sym);
|
2002-06-09 21:56:09 +08:00
|
|
|
|
|
|
|
if (smudge->initialized)
|
2016-01-28 02:13:17 +08:00
|
|
|
gimp_smudge_motion (paint_core, drawable, paint_options, sym);
|
1999-07-02 00:52:50 +08:00
|
|
|
break;
|
2000-12-31 12:07:42 +08:00
|
|
|
|
2004-07-19 22:37:40 +08:00
|
|
|
case GIMP_PAINT_STATE_FINISH:
|
2016-01-28 02:13:17 +08:00
|
|
|
if (smudge->accum_buffers)
|
2002-06-09 21:56:09 +08:00
|
|
|
{
|
2016-01-28 02:13:17 +08:00
|
|
|
GList *iter;
|
|
|
|
|
|
|
|
for (iter = smudge->accum_buffers; iter; iter = g_list_next (iter))
|
|
|
|
{
|
|
|
|
if (iter->data)
|
|
|
|
g_object_unref (iter->data);
|
|
|
|
}
|
2017-07-16 00:36:53 +08:00
|
|
|
|
|
|
|
g_list_free (smudge->accum_buffers);
|
|
|
|
smudge->accum_buffers = NULL;
|
2002-06-09 21:56:09 +08:00
|
|
|
}
|
2017-07-16 00:36:53 +08:00
|
|
|
|
2002-06-09 21:56:09 +08:00
|
|
|
smudge->initialized = FALSE;
|
1999-07-02 00:52:50 +08:00
|
|
|
break;
|
2000-12-31 12:07:42 +08:00
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
1999-07-02 00:52:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-01-15 02:45:36 +08:00
|
|
|
static gboolean
|
2004-05-26 17:32:03 +08:00
|
|
|
gimp_smudge_start (GimpPaintCore *paint_core,
|
|
|
|
GimpDrawable *drawable,
|
2009-05-03 18:08:21 +08:00
|
|
|
GimpPaintOptions *paint_options,
|
2016-01-28 02:13:17 +08:00
|
|
|
GimpSymmetry *sym)
|
1999-07-02 00:52:50 +08:00
|
|
|
{
|
2019-05-27 02:21:09 +08:00
|
|
|
GimpSmudge *smudge = GIMP_SMUDGE (paint_core);
|
|
|
|
GimpBrushCore *brush_core = GIMP_BRUSH_CORE (paint_core);
|
|
|
|
GimpSmudgeOptions *options = GIMP_SMUDGE_OPTIONS (paint_options);
|
|
|
|
GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable));
|
2019-01-21 00:48:04 +08:00
|
|
|
GimpPickable *dest_pickable;
|
|
|
|
GeglBuffer *pickable_buffer;
|
|
|
|
GeglBuffer *paint_buffer;
|
|
|
|
GimpCoords *coords;
|
|
|
|
gint dest_pickable_off_x;
|
|
|
|
gint dest_pickable_off_y;
|
|
|
|
gint paint_buffer_x;
|
|
|
|
gint paint_buffer_y;
|
|
|
|
gint accum_size;
|
|
|
|
gint n_strokes;
|
|
|
|
gint i;
|
|
|
|
gint x, y;
|
1999-07-02 00:52:50 +08:00
|
|
|
|
2016-01-28 02:13:17 +08:00
|
|
|
coords = gimp_symmetry_get_origin (sym);
|
2019-05-27 02:21:09 +08:00
|
|
|
gimp_brush_core_eval_transform_dynamics (brush_core,
|
2016-01-28 02:13:17 +08:00
|
|
|
drawable,
|
|
|
|
paint_options,
|
|
|
|
coords);
|
2005-09-04 01:16:58 +08:00
|
|
|
|
2019-01-21 00:48:04 +08:00
|
|
|
if (options->sample_merged)
|
|
|
|
{
|
|
|
|
dest_pickable = GIMP_PICKABLE (image);
|
|
|
|
|
|
|
|
gimp_item_get_offset (GIMP_ITEM (drawable),
|
|
|
|
&dest_pickable_off_x,
|
|
|
|
&dest_pickable_off_y);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dest_pickable = GIMP_PICKABLE (drawable);
|
|
|
|
|
|
|
|
dest_pickable_off_x = 0;
|
|
|
|
dest_pickable_off_y = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
pickable_buffer = gimp_pickable_get_buffer (dest_pickable);
|
|
|
|
|
2016-01-28 02:13:17 +08:00
|
|
|
n_strokes = gimp_symmetry_get_size (sym);
|
|
|
|
for (i = 0; i < n_strokes; i++)
|
2003-06-13 19:12:04 +08:00
|
|
|
{
|
2016-01-28 02:13:17 +08:00
|
|
|
GeglBuffer *accum_buffer;
|
|
|
|
|
|
|
|
coords = gimp_symmetry_get_coords (sym, i);
|
|
|
|
|
|
|
|
gimp_smudge_accumulator_size (paint_options, coords, &accum_size);
|
|
|
|
|
|
|
|
/* Allocate the accumulation buffer */
|
|
|
|
accum_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
|
|
|
|
accum_size,
|
|
|
|
accum_size),
|
|
|
|
babl_format ("RGBA float"));
|
|
|
|
smudge->accum_buffers = g_list_prepend (smudge->accum_buffers,
|
|
|
|
accum_buffer);
|
|
|
|
|
|
|
|
/* adjust the x and y coordinates to the upper left corner of the
|
|
|
|
* accumulator
|
|
|
|
*/
|
2016-11-07 00:10:28 +08:00
|
|
|
paint_buffer = gimp_paint_core_get_paint_buffer (paint_core, drawable,
|
2017-01-21 07:27:48 +08:00
|
|
|
paint_options,
|
2017-08-13 03:35:47 +08:00
|
|
|
GIMP_LAYER_MODE_NORMAL,
|
2017-01-21 07:27:48 +08:00
|
|
|
coords,
|
2016-11-07 00:10:28 +08:00
|
|
|
&paint_buffer_x,
|
|
|
|
&paint_buffer_y,
|
|
|
|
NULL, NULL);
|
|
|
|
if (! paint_buffer)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
gimp_smudge_accumulator_coords (paint_core, coords, 0, &x, &y);
|
2016-01-28 02:13:17 +08:00
|
|
|
|
|
|
|
/* If clipped, prefill the smudge buffer with the color at the
|
|
|
|
* brush position.
|
|
|
|
*/
|
|
|
|
if (x != paint_buffer_x ||
|
|
|
|
y != paint_buffer_y ||
|
|
|
|
accum_size != gegl_buffer_get_width (paint_buffer) ||
|
|
|
|
accum_size != gegl_buffer_get_height (paint_buffer))
|
|
|
|
{
|
2018-06-18 02:14:21 +08:00
|
|
|
gfloat pixel[4];
|
2016-01-28 02:13:17 +08:00
|
|
|
GeglColor *color;
|
2019-01-21 00:48:04 +08:00
|
|
|
gint pick_x;
|
|
|
|
gint pick_y;
|
|
|
|
|
|
|
|
pick_x = CLAMP ((gint) coords->x + dest_pickable_off_x,
|
|
|
|
0,
|
|
|
|
gegl_buffer_get_width (pickable_buffer) - 1);
|
|
|
|
pick_y = CLAMP ((gint) coords->y + dest_pickable_off_y,
|
|
|
|
0,
|
|
|
|
gegl_buffer_get_height (pickable_buffer) - 1);
|
|
|
|
|
|
|
|
gimp_pickable_get_pixel_at (dest_pickable,
|
|
|
|
pick_x, pick_y,
|
2018-06-18 02:14:21 +08:00
|
|
|
babl_format ("RGBA float"),
|
|
|
|
pixel);
|
2016-01-28 02:13:17 +08:00
|
|
|
|
2018-06-18 02:14:21 +08:00
|
|
|
color = gegl_color_new (NULL);
|
|
|
|
gegl_color_set_pixel (color, babl_format ("RGBA float"), pixel);
|
2016-01-28 02:13:17 +08:00
|
|
|
gegl_buffer_set_color (accum_buffer, NULL, color);
|
|
|
|
g_object_unref (color);
|
|
|
|
}
|
1999-09-26 12:53:59 +08:00
|
|
|
|
2016-01-28 02:13:17 +08:00
|
|
|
/* copy the region under the original painthit. */
|
2019-01-21 00:48:04 +08:00
|
|
|
gimp_gegl_buffer_copy
|
|
|
|
(pickable_buffer,
|
|
|
|
GEGL_RECTANGLE (paint_buffer_x + dest_pickable_off_x,
|
|
|
|
paint_buffer_y + dest_pickable_off_y,
|
|
|
|
gegl_buffer_get_width (paint_buffer),
|
|
|
|
gegl_buffer_get_height (paint_buffer)),
|
|
|
|
GEGL_ABYSS_NONE,
|
|
|
|
accum_buffer,
|
|
|
|
GEGL_RECTANGLE (paint_buffer_x - x,
|
|
|
|
paint_buffer_y - y,
|
|
|
|
0, 0));
|
2016-01-28 02:13:17 +08:00
|
|
|
}
|
2017-07-16 00:36:53 +08:00
|
|
|
|
2016-11-07 00:10:28 +08:00
|
|
|
smudge->accum_buffers = g_list_reverse (smudge->accum_buffers);
|
2012-04-02 00:50:19 +08:00
|
|
|
|
2000-01-15 02:45:36 +08:00
|
|
|
return TRUE;
|
1999-07-02 00:52:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2002-06-09 21:56:09 +08:00
|
|
|
gimp_smudge_motion (GimpPaintCore *paint_core,
|
|
|
|
GimpDrawable *drawable,
|
2009-05-03 18:08:21 +08:00
|
|
|
GimpPaintOptions *paint_options,
|
2016-01-28 02:13:17 +08:00
|
|
|
GimpSymmetry *sym)
|
1999-07-02 00:52:50 +08:00
|
|
|
{
|
2017-07-23 00:20:23 +08:00
|
|
|
GimpSmudge *smudge = GIMP_SMUDGE (paint_core);
|
|
|
|
GimpBrushCore *brush_core = GIMP_BRUSH_CORE (paint_core);
|
|
|
|
GimpSmudgeOptions *options = GIMP_SMUDGE_OPTIONS (paint_options);
|
|
|
|
GimpContext *context = GIMP_CONTEXT (paint_options);
|
|
|
|
GimpDynamics *dynamics = GIMP_BRUSH_CORE (paint_core)->dynamics;
|
|
|
|
GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable));
|
2019-01-21 00:48:04 +08:00
|
|
|
GimpPickable *dest_pickable;
|
2016-01-28 02:13:17 +08:00
|
|
|
GeglBuffer *paint_buffer;
|
2019-01-21 00:48:04 +08:00
|
|
|
gint dest_pickable_off_x;
|
|
|
|
gint dest_pickable_off_y;
|
2016-01-28 02:13:17 +08:00
|
|
|
gint paint_buffer_x;
|
|
|
|
gint paint_buffer_y;
|
|
|
|
gint paint_buffer_width;
|
|
|
|
gint paint_buffer_height;
|
2017-07-23 00:20:23 +08:00
|
|
|
/* brush dynamics */
|
2016-01-28 02:13:17 +08:00
|
|
|
gdouble fade_point;
|
|
|
|
gdouble opacity;
|
|
|
|
gdouble rate;
|
2017-07-23 00:20:23 +08:00
|
|
|
gdouble flow;
|
|
|
|
gdouble grad_point;
|
|
|
|
/* brush color */
|
|
|
|
GimpRGB brush_color;
|
|
|
|
GimpRGB *brush_color_ptr; /* whether use single color or pixmap */
|
|
|
|
/* accum buffer */
|
2016-01-28 02:13:17 +08:00
|
|
|
gint x, y;
|
|
|
|
GeglBuffer *accum_buffer;
|
2017-07-23 00:20:23 +08:00
|
|
|
/* other variables */
|
|
|
|
gdouble force;
|
2016-01-28 02:13:17 +08:00
|
|
|
GimpCoords *coords;
|
|
|
|
gint paint_width, paint_height;
|
|
|
|
gint n_strokes;
|
|
|
|
gint i;
|
2011-03-04 01:43:56 +08:00
|
|
|
|
2019-01-21 00:48:04 +08:00
|
|
|
if (options->sample_merged)
|
|
|
|
{
|
|
|
|
dest_pickable = GIMP_PICKABLE (image);
|
|
|
|
|
|
|
|
gimp_item_get_offset (GIMP_ITEM (drawable),
|
|
|
|
&dest_pickable_off_x,
|
|
|
|
&dest_pickable_off_y);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dest_pickable = GIMP_PICKABLE (drawable);
|
|
|
|
|
|
|
|
dest_pickable_off_x = 0;
|
|
|
|
dest_pickable_off_y = 0;
|
|
|
|
}
|
|
|
|
|
2009-10-14 02:48:44 +08:00
|
|
|
fade_point = gimp_paint_options_get_fade (paint_options, image,
|
|
|
|
paint_core->pixel_dist);
|
1999-07-02 00:52:50 +08:00
|
|
|
|
2016-01-28 02:13:17 +08:00
|
|
|
coords = gimp_symmetry_get_origin (sym);
|
2012-04-28 17:42:33 +08:00
|
|
|
opacity = gimp_dynamics_get_linear_value (dynamics,
|
|
|
|
GIMP_DYNAMICS_OUTPUT_OPACITY,
|
|
|
|
coords,
|
|
|
|
paint_options,
|
|
|
|
fade_point);
|
2003-07-16 19:25:37 +08:00
|
|
|
if (opacity == 0.0)
|
|
|
|
return;
|
|
|
|
|
2019-05-27 02:21:09 +08:00
|
|
|
gimp_brush_core_eval_transform_dynamics (brush_core,
|
2016-01-28 02:13:17 +08:00
|
|
|
drawable,
|
|
|
|
paint_options,
|
|
|
|
coords);
|
1999-07-02 00:52:50 +08:00
|
|
|
|
2017-07-23 00:20:23 +08:00
|
|
|
/* Get brush dynamic values other than opacity */
|
|
|
|
rate = ((options->rate / 100.0) *
|
|
|
|
gimp_dynamics_get_linear_value (dynamics,
|
|
|
|
GIMP_DYNAMICS_OUTPUT_RATE,
|
|
|
|
coords,
|
|
|
|
paint_options,
|
|
|
|
fade_point));
|
|
|
|
|
|
|
|
flow = ((options->flow / 100.0) *
|
|
|
|
gimp_dynamics_get_linear_value (dynamics,
|
|
|
|
GIMP_DYNAMICS_OUTPUT_FLOW,
|
|
|
|
coords,
|
|
|
|
paint_options,
|
|
|
|
fade_point));
|
|
|
|
|
|
|
|
grad_point = gimp_dynamics_get_linear_value (dynamics,
|
|
|
|
GIMP_DYNAMICS_OUTPUT_COLOR,
|
|
|
|
coords,
|
|
|
|
paint_options,
|
|
|
|
fade_point);
|
|
|
|
|
|
|
|
/* Get current gradient color, brush pixmap, or foreground color */
|
|
|
|
brush_color_ptr = &brush_color;
|
|
|
|
if (gimp_paint_options_get_gradient_color (paint_options, image,
|
|
|
|
grad_point,
|
|
|
|
paint_core->pixel_dist,
|
|
|
|
&brush_color))
|
|
|
|
{
|
|
|
|
/* No more processing needed */
|
|
|
|
}
|
|
|
|
else if (brush_core->brush && gimp_brush_get_pixmap (brush_core->brush))
|
|
|
|
{
|
|
|
|
brush_color_ptr = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gimp_context_get_foreground (context, &brush_color);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert to linear RGBA */
|
|
|
|
if (brush_color_ptr)
|
2019-01-21 00:48:04 +08:00
|
|
|
gimp_pickable_srgb_to_pixel (dest_pickable,
|
2017-07-23 00:20:23 +08:00
|
|
|
&brush_color,
|
|
|
|
babl_format ("RGBA double"),
|
|
|
|
&brush_color);
|
|
|
|
|
2016-01-28 02:13:17 +08:00
|
|
|
n_strokes = gimp_symmetry_get_size (sym);
|
|
|
|
for (i = 0; i < n_strokes; i++)
|
|
|
|
{
|
|
|
|
coords = gimp_symmetry_get_coords (sym, i);
|
|
|
|
|
2019-05-27 02:21:09 +08:00
|
|
|
gimp_brush_core_eval_transform_symmetry (brush_core, sym, i);
|
|
|
|
|
2016-01-28 02:13:17 +08:00
|
|
|
paint_buffer = gimp_paint_core_get_paint_buffer (paint_core, drawable,
|
2017-01-21 07:27:48 +08:00
|
|
|
paint_options,
|
2017-08-13 03:35:47 +08:00
|
|
|
GIMP_LAYER_MODE_NORMAL,
|
2017-01-21 07:27:48 +08:00
|
|
|
coords,
|
2016-01-28 02:13:17 +08:00
|
|
|
&paint_buffer_x,
|
|
|
|
&paint_buffer_y,
|
|
|
|
&paint_width,
|
|
|
|
&paint_height);
|
|
|
|
if (! paint_buffer)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
paint_buffer_width = gegl_buffer_get_width (paint_buffer);
|
|
|
|
paint_buffer_height = gegl_buffer_get_height (paint_buffer);
|
|
|
|
|
|
|
|
/* Get the unclipped acumulator coordinates */
|
|
|
|
gimp_smudge_accumulator_coords (paint_core, coords, i, &x, &y);
|
|
|
|
|
2017-07-23 00:20:23 +08:00
|
|
|
accum_buffer = g_list_nth_data (smudge->accum_buffers, i);
|
2016-01-28 02:13:17 +08:00
|
|
|
|
2017-07-23 00:20:23 +08:00
|
|
|
/* Old smudge tool:
|
|
|
|
* Smudge uses the buffer Accum.
|
2016-01-28 02:13:17 +08:00
|
|
|
* For each successive painthit Accum is built like this
|
|
|
|
* Accum = rate*Accum + (1-rate)*I.
|
|
|
|
* where I is the pixels under the current painthit.
|
|
|
|
* Then the paint area (paint_area) is built as
|
|
|
|
* (Accum,1) (if no alpha),
|
|
|
|
*/
|
|
|
|
|
2017-07-23 00:20:23 +08:00
|
|
|
/* 2017/4/22: New smudge painting tool:
|
|
|
|
* Accum=rate*Accum + (1-rate)*I
|
|
|
|
* if brush_color_ptr!=NULL
|
|
|
|
* Paint=(1-flow)*Accum + flow*BrushColor
|
|
|
|
* else, draw brush pixmap on the paint_buffer and
|
|
|
|
* Paint=(1-flow)*Accum + flow*Paint
|
|
|
|
*
|
|
|
|
* For non-pixmap brushes, calculate blending in
|
|
|
|
* gimp_gegl_smudge_with_paint() instead of calling
|
|
|
|
* gegl_buffer_set_color() to reduce gegl's internal processing.
|
|
|
|
*/
|
2019-02-25 02:11:33 +08:00
|
|
|
if (! brush_color_ptr && flow > 0.0)
|
2017-07-23 00:20:23 +08:00
|
|
|
{
|
|
|
|
gimp_brush_core_color_area_with_pixmap (brush_core, drawable,
|
2019-05-27 02:21:09 +08:00
|
|
|
coords,
|
2017-07-23 00:20:23 +08:00
|
|
|
paint_buffer,
|
|
|
|
paint_buffer_x,
|
|
|
|
paint_buffer_y,
|
app: improve gimp_brush_core_color_area_with_pixmap()
Reimplement gimp_brush_core_color_area_with_pixmap(), which copies
the brush's dab to the paint buffer when using a pixmap brush, in
terms of gimp-gegl-loops. This simplifies the functions,
parallelizes processing, and transparently handles float brushes.
Replace the "mode" parameter of the function with an "apply_mask"
parameter, which specifies whether to apply the brush's mask to
the dab as part of copying. Avoid applying the mask in
GimpPaintbrush; previously, we would erroneously apply the mask
twice when using the paintbrush tool: once when copying the
dab to the paint buffer, and again when pasting the paint buffer
to the canvas.
We still apply the mask in GimpSmudge, which results in the same
double-application behavior, however, this might be less practical
to fix.
2019-02-25 01:54:58 +08:00
|
|
|
TRUE);
|
2017-07-23 00:20:23 +08:00
|
|
|
}
|
2017-07-16 00:36:53 +08:00
|
|
|
|
2017-07-23 00:20:23 +08:00
|
|
|
gimp_gegl_smudge_with_paint (accum_buffer,
|
|
|
|
GEGL_RECTANGLE (paint_buffer_x - x,
|
|
|
|
paint_buffer_y - y,
|
|
|
|
paint_buffer_width,
|
|
|
|
paint_buffer_height),
|
2019-01-21 00:48:04 +08:00
|
|
|
gimp_pickable_get_buffer (dest_pickable),
|
|
|
|
GEGL_RECTANGLE (paint_buffer_x +
|
|
|
|
dest_pickable_off_x,
|
|
|
|
paint_buffer_y +
|
|
|
|
dest_pickable_off_y,
|
2017-07-23 00:20:23 +08:00
|
|
|
paint_buffer_width,
|
|
|
|
paint_buffer_height),
|
|
|
|
brush_color_ptr,
|
|
|
|
paint_buffer,
|
|
|
|
options->no_erasing,
|
|
|
|
flow,
|
|
|
|
rate);
|
2016-01-28 02:13:17 +08:00
|
|
|
|
|
|
|
if (gimp_dynamics_is_output_enabled (dynamics, GIMP_DYNAMICS_OUTPUT_FORCE))
|
|
|
|
force = gimp_dynamics_get_linear_value (dynamics,
|
|
|
|
GIMP_DYNAMICS_OUTPUT_FORCE,
|
|
|
|
coords,
|
|
|
|
paint_options,
|
|
|
|
fade_point);
|
|
|
|
else
|
|
|
|
force = paint_options->brush_force;
|
|
|
|
|
|
|
|
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),
|
|
|
|
force,
|
2019-05-27 02:21:09 +08:00
|
|
|
GIMP_PAINT_INCREMENTAL);
|
2016-01-28 02:13:17 +08:00
|
|
|
}
|
1999-07-02 00:52:50 +08:00
|
|
|
}
|
2001-04-12 01:20:34 +08:00
|
|
|
|
2003-07-14 22:50:41 +08:00
|
|
|
static void
|
2012-01-18 04:26:32 +08:00
|
|
|
gimp_smudge_accumulator_coords (GimpPaintCore *paint_core,
|
|
|
|
const GimpCoords *coords,
|
2016-01-28 02:13:17 +08:00
|
|
|
gint stroke,
|
2012-01-18 04:26:32 +08:00
|
|
|
gint *x,
|
|
|
|
gint *y)
|
2001-04-12 01:20:34 +08:00
|
|
|
{
|
2012-01-29 23:59:22 +08:00
|
|
|
GimpSmudge *smudge = GIMP_SMUDGE (paint_core);
|
2016-01-28 02:13:17 +08:00
|
|
|
GeglBuffer *accum_buffer;
|
2012-01-18 04:26:32 +08:00
|
|
|
|
2016-01-28 02:13:17 +08:00
|
|
|
accum_buffer = g_list_nth_data (smudge->accum_buffers, stroke);
|
2017-07-16 00:36:53 +08:00
|
|
|
|
2016-01-28 02:13:17 +08:00
|
|
|
*x = (gint) coords->x - gegl_buffer_get_width (accum_buffer) / 2;
|
|
|
|
*y = (gint) coords->y - gegl_buffer_get_height (accum_buffer) / 2;
|
2001-04-12 01:20:34 +08:00
|
|
|
}
|
2012-01-18 04:26:32 +08:00
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_smudge_accumulator_size (GimpPaintOptions *paint_options,
|
2014-11-14 02:32:59 +08:00
|
|
|
const GimpCoords *coords,
|
2012-01-18 04:26:32 +08:00
|
|
|
gint *accumulator_size)
|
|
|
|
{
|
2014-11-14 02:32:59 +08:00
|
|
|
gdouble max_view_scale = 1.0;
|
|
|
|
gdouble max_brush_size;
|
|
|
|
|
2017-12-23 05:59:13 +08:00
|
|
|
if (paint_options->brush_lock_to_view)
|
2014-11-14 02:32:59 +08:00
|
|
|
max_view_scale = MAX (coords->xscale, coords->yscale);
|
2014-11-18 02:59:34 +08:00
|
|
|
|
|
|
|
max_brush_size = MIN (paint_options->brush_size / max_view_scale,
|
|
|
|
GIMP_BRUSH_MAX_SIZE);
|
2014-11-14 02:32:59 +08:00
|
|
|
|
2012-04-02 01:02:22 +08:00
|
|
|
/* Note: the max brush mask size plus a border of 1 pixel and a
|
|
|
|
* little headroom
|
|
|
|
*/
|
2014-11-14 02:32:59 +08:00
|
|
|
*accumulator_size = ceil (sqrt (2 * SQR (max_brush_size + 1)) + 2);
|
2012-01-29 23:59:22 +08:00
|
|
|
}
|