gimp/app/tools/paint_core.c

1978 lines
55 KiB
C
Raw Normal View History

1997-11-25 06:05:25 +08:00
/* 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.
1997-11-25 06:05:25 +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"
1997-11-25 06:05:25 +08:00
#include <stdlib.h>
#include <stdio.h> /* temporary for debugging */
1997-11-25 06:05:25 +08:00
#include <string.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
1997-11-25 06:05:25 +08:00
#include "appenv.h"
#include "brush_scale.h"
#include "devices.h"
1997-11-25 06:05:25 +08:00
#include "drawable.h"
#include "errors.h"
#include "gdisplay.h"
#include "gimage_mask.h"
#include "gimpbrushpipe.h"
#include "gimprc.h"
#include "gradient.h" /* for grad_get_color_at() */
1997-11-25 06:05:25 +08:00
#include "paint_funcs.h"
#include "paint_core.h"
#include "selection.h"
#include "tools.h"
#include "undo.h"
#include "cursorutil.h"
1997-11-25 06:05:25 +08:00
#include "libgimp/gimpintl.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
#include "libgimp/gimpmath.h"
1998-08-12 01:35:34 +08:00
#include "tile.h" /* ick. */
/* target size */
namespace cleanups. 1999-06-21 Michael Natterer <mitschel@cs.tu-berlin.de> * app/context_manager.c: namespace cleanups. * app/commands.[ch] * app/menus.c: moved the "Toggle Selection" menu entry to "View", sprinkled some separators and made the layers/channels/paths popup menus consistent with Tigert's last ops buttons change. * app/fileops.c * app/plug_in.c: check for gdisplay_active() returning NULL in some more places. * app/[all tool related files]: - Turned the ToolAction and ToolState #define's into typedef'ed enums, so the compiler can do some more sanity checking. - Removed one more unused global variable "active_tool_layer". - Removed some #include's from tools.c. - Standardized the individual tools' structure names. - Moved showing/hiding the tool options to separate functions. - Stuff... * app/commands.c * app/disp_callbacks.c * app/gdisplay.c * app/tools.c: fixed the segfaults which happened when the image of one of the tools which have dialogs (levels/posterize/...) was deleted. My approach was to do stricter sanity checking and to set some gdisplay pointers correctly where appropriate, so I can't tell exactly where the bug was. The curves tool now(??) updates on every _second_ display change only, which is really obscure. Finding/changing the display to operate on should definitely be done by connecting to the user context's "display_changed" signal. * app/gimpset.c: emit the "remove" signal _after_ removing the pointer from the set. If this was not a bug but a feature, please let me know, we'll need two signals then.
1999-06-22 06:12:07 +08:00
#define TARGET_HEIGHT 15
#define TARGET_WIDTH 15
#define EPSILON 0.00001
#define STATUSBAR_SIZE 128
1997-11-25 06:05:25 +08:00
/* global variables--for use in the various paint tools */
PaintCore non_gui_paint_core;
/* local function prototypes */
static void paint_core_calculate_brush_size (MaskBuf *, double, int *, int *);
1997-11-25 06:05:25 +08:00
static MaskBuf * paint_core_subsample_mask (MaskBuf *, double, double);
namespace cleanups. 1999-06-21 Michael Natterer <mitschel@cs.tu-berlin.de> * app/context_manager.c: namespace cleanups. * app/commands.[ch] * app/menus.c: moved the "Toggle Selection" menu entry to "View", sprinkled some separators and made the layers/channels/paths popup menus consistent with Tigert's last ops buttons change. * app/fileops.c * app/plug_in.c: check for gdisplay_active() returning NULL in some more places. * app/[all tool related files]: - Turned the ToolAction and ToolState #define's into typedef'ed enums, so the compiler can do some more sanity checking. - Removed one more unused global variable "active_tool_layer". - Removed some #include's from tools.c. - Standardized the individual tools' structure names. - Moved showing/hiding the tool options to separate functions. - Stuff... * app/commands.c * app/disp_callbacks.c * app/gdisplay.c * app/tools.c: fixed the segfaults which happened when the image of one of the tools which have dialogs (levels/posterize/...) was deleted. My approach was to do stricter sanity checking and to set some gdisplay pointers correctly where appropriate, so I can't tell exactly where the bug was. The curves tool now(??) updates on every _second_ display change only, which is really obscure. Finding/changing the display to operate on should definitely be done by connecting to the user context's "display_changed" signal. * app/gimpset.c: emit the "remove" signal _after_ removing the pointer from the set. If this was not a bug but a feature, please let me know, we'll need two signals then.
1999-06-22 06:12:07 +08:00
static MaskBuf * paint_core_pressurize_mask (MaskBuf *, double, double, double);
1997-11-25 06:05:25 +08:00
static MaskBuf * paint_core_solidify_mask (MaskBuf *);
static MaskBuf * paint_core_scale_mask (MaskBuf *, gdouble);
static MaskBuf * paint_core_scale_pixmap (MaskBuf *, gdouble);
static MaskBuf * paint_core_get_brush_mask (PaintCore *, BrushApplicationMode, gdouble);
namespace cleanups. 1999-06-21 Michael Natterer <mitschel@cs.tu-berlin.de> * app/context_manager.c: namespace cleanups. * app/commands.[ch] * app/menus.c: moved the "Toggle Selection" menu entry to "View", sprinkled some separators and made the layers/channels/paths popup menus consistent with Tigert's last ops buttons change. * app/fileops.c * app/plug_in.c: check for gdisplay_active() returning NULL in some more places. * app/[all tool related files]: - Turned the ToolAction and ToolState #define's into typedef'ed enums, so the compiler can do some more sanity checking. - Removed one more unused global variable "active_tool_layer". - Removed some #include's from tools.c. - Standardized the individual tools' structure names. - Moved showing/hiding the tool options to separate functions. - Stuff... * app/commands.c * app/disp_callbacks.c * app/gdisplay.c * app/tools.c: fixed the segfaults which happened when the image of one of the tools which have dialogs (levels/posterize/...) was deleted. My approach was to do stricter sanity checking and to set some gdisplay pointers correctly where appropriate, so I can't tell exactly where the bug was. The curves tool now(??) updates on every _second_ display change only, which is really obscure. Finding/changing the display to operate on should definitely be done by connecting to the user context's "display_changed" signal. * app/gimpset.c: emit the "remove" signal _after_ removing the pointer from the set. If this was not a bug but a feature, please let me know, we'll need two signals then.
1999-06-22 06:12:07 +08:00
static void paint_core_paste (PaintCore *, MaskBuf *,
GimpDrawable *, int, int,
LayerModeEffects,
PaintApplicationMode);
namespace cleanups. 1999-06-21 Michael Natterer <mitschel@cs.tu-berlin.de> * app/context_manager.c: namespace cleanups. * app/commands.[ch] * app/menus.c: moved the "Toggle Selection" menu entry to "View", sprinkled some separators and made the layers/channels/paths popup menus consistent with Tigert's last ops buttons change. * app/fileops.c * app/plug_in.c: check for gdisplay_active() returning NULL in some more places. * app/[all tool related files]: - Turned the ToolAction and ToolState #define's into typedef'ed enums, so the compiler can do some more sanity checking. - Removed one more unused global variable "active_tool_layer". - Removed some #include's from tools.c. - Standardized the individual tools' structure names. - Moved showing/hiding the tool options to separate functions. - Stuff... * app/commands.c * app/disp_callbacks.c * app/gdisplay.c * app/tools.c: fixed the segfaults which happened when the image of one of the tools which have dialogs (levels/posterize/...) was deleted. My approach was to do stricter sanity checking and to set some gdisplay pointers correctly where appropriate, so I can't tell exactly where the bug was. The curves tool now(??) updates on every _second_ display change only, which is really obscure. Finding/changing the display to operate on should definitely be done by connecting to the user context's "display_changed" signal. * app/gimpset.c: emit the "remove" signal _after_ removing the pointer from the set. If this was not a bug but a feature, please let me know, we'll need two signals then.
1999-06-22 06:12:07 +08:00
static void paint_core_replace (PaintCore *, MaskBuf *,
GimpDrawable *, int, int,
PaintApplicationMode);
static void brush_to_canvas_tiles (PaintCore *, MaskBuf *, int);
static void canvas_tiles_to_canvas_buf (PaintCore *);
static void brush_to_canvas_buf (PaintCore *, MaskBuf *, int);
static void set_undo_tiles (GimpDrawable *, int, int, int, int);
1997-11-25 06:05:25 +08:00
static void set_canvas_tiles (int, int, int, int);
1999-10-27 02:27:27 +08:00
static void paint_core_invalidate_cache (GimpBrush *brush, gpointer *blah);
1997-11-25 06:05:25 +08:00
/***********************************************************************/
/* undo blocks variables */
static TileManager * undo_tiles = NULL;
static TileManager * canvas_tiles = NULL;
/***********************************************************************/
/* paint buffers variables */
static TempBuf * orig_buf = NULL;
static TempBuf * canvas_buf = NULL;
/* brush buffers */
static MaskBuf * pressure_brush;
1997-11-25 06:05:25 +08:00
static MaskBuf * solid_brush;
static MaskBuf * scale_brush = NULL;
static MaskBuf * scale_pixmap = NULL;
static MaskBuf * kernel_brushes[5][5];
1997-11-25 06:05:25 +08:00
/* paint buffers utility functions */
static void free_paint_buffers (void);
/* brush pipe utility functions */
static void paint_line_pixmap_mask (GImage *, GimpDrawable *,
TempBuf *, TempBuf *, guchar *,
int, int, int, int, int);
1997-11-25 06:05:25 +08:00
/***********************************************************************/
#define KERNEL_WIDTH 3
#define KERNEL_HEIGHT 3
/* Brush pixel subsampling kernels */
static const int subsample[5][5][9] = {
{
{ 64, 64, 0, 64, 64, 0, 0, 0, 0, },
{ 32, 96, 0, 32, 96, 0, 0, 0, 0, },
{ 0, 128, 0, 0, 128, 0, 0, 0, 0, },
{ 0, 96, 32, 0, 96, 32, 0, 0, 0, },
{ 0, 64, 64, 0, 64, 64, 0, 0, 0, },
},
{
{ 32, 32, 0, 96, 96, 0, 0, 0, 0, },
{ 16, 48, 0, 48, 144, 0, 0, 0, 0, },
{ 0, 64, 0, 0, 192, 0, 0, 0, 0, },
{ 0, 48, 16, 0, 144, 48, 0, 0, 0, },
{ 0, 32, 32, 0, 96, 96, 0, 0, 0, },
},
{
{ 0, 0, 0, 128, 128, 0, 0, 0, 0, },
{ 0, 0, 0, 64, 192, 0, 0, 0, 0, },
{ 0, 0, 0, 0, 256, 0, 0, 0, 0, },
{ 0, 0, 0, 0, 192, 64, 0, 0, 0, },
{ 0, 0, 0, 0, 128, 128, 0, 0, 0, },
},
{
{ 0, 0, 0, 96, 96, 0, 32, 32, 0, },
{ 0, 0, 0, 48, 144, 0, 16, 48, 0, },
{ 0, 0, 0, 0, 192, 0, 0, 64, 0, },
{ 0, 0, 0, 0, 144, 48, 0, 48, 16, },
{ 0, 0, 0, 0, 96, 96, 0, 32, 32, },
},
{
{ 0, 0, 0, 64, 64, 0, 64, 64, 0, },
{ 0, 0, 0, 32, 96, 0, 32, 96, 0, },
{ 0, 0, 0, 0, 128, 0, 0, 128, 0, },
{ 0, 0, 0, 0, 96, 32, 0, 96, 32, },
{ 0, 0, 0, 0, 64, 64, 0, 64, 64, },
},
1997-11-25 06:05:25 +08:00
};
static void
paint_core_sample_color (GimpDrawable *drawable,
1999-10-27 02:27:27 +08:00
int x,
int y,
int state)
{
1999-10-27 02:27:27 +08:00
guchar *color;
if ((color = gimp_drawable_get_color_at (drawable, x, y)))
{
if ((state & GDK_CONTROL_MASK))
1999-10-27 02:27:27 +08:00
gimp_context_set_foreground (gimp_context_get_user (),
color[RED_PIX],
color[GREEN_PIX],
color[BLUE_PIX]);
else
1999-10-27 02:27:27 +08:00
gimp_context_set_background (gimp_context_get_user (),
color[RED_PIX],
color[GREEN_PIX],
color[BLUE_PIX]);
1999-10-27 02:27:27 +08:00
g_free (color);
}
}
1997-11-25 06:05:25 +08:00
void
paint_core_button_press (Tool *tool,
GdkEventButton *bevent,
gpointer gdisp_ptr)
1997-11-25 06:05:25 +08:00
{
PaintCore * paint_core;
GDisplay * gdisp;
gboolean draw_line;
1997-11-25 06:05:25 +08:00
double x, y;
GimpDrawable *drawable;
1997-11-25 06:05:25 +08:00
gdisp = (GDisplay *) gdisp_ptr;
paint_core = (PaintCore *) tool->private;
gdisplay_untransform_coords_f (gdisp, (double) bevent->x, (double) bevent->y, &x, &y, TRUE);
drawable = gimage_active_drawable (gdisp->gimage);
1997-11-25 06:05:25 +08:00
if (! paint_core_init (paint_core, drawable, x, y))
1997-11-25 06:05:25 +08:00
return;
draw_line = FALSE;
paint_core->curpressure = bevent->pressure;
paint_core->curxtilt = bevent->xtilt;
paint_core->curytilt = bevent->ytilt;
#ifdef GTK_HAVE_SIX_VALUATORS
paint_core->curwheel = bevent->wheel;
#endif /* GTK_HAVE_SIX_VALUATORS */
1997-11-25 06:05:25 +08:00
paint_core->state = bevent->state;
if (gdisp_ptr != tool->gdisp_ptr)
{
/* initialize the statusbar display */
paint_core->context_id =
gtk_statusbar_get_context_id (GTK_STATUSBAR (gdisp->statusbar), "paint");
}
/* if this is a new image, reinit the core vals */
if ((gdisp_ptr != tool->gdisp_ptr) || ! (bevent->state & GDK_SHIFT_MASK))
1997-11-25 06:05:25 +08:00
{
/* initialize some values */
paint_core->startx = paint_core->lastx = paint_core->curx = x;
paint_core->starty = paint_core->lasty = paint_core->cury = y;
paint_core->startpressure = paint_core->lastpressure = paint_core->curpressure;
paint_core->startytilt = paint_core->lastytilt = paint_core->curytilt;
paint_core->startxtilt = paint_core->lastxtilt = paint_core->curxtilt;
#ifdef GTK_HAVE_SIX_VALUATORS
paint_core->startwheel = paint_core->lastwheel = paint_core->curwheel;
#endif /* GTK_HAVE_SIX_VALUATORS */
1997-11-25 06:05:25 +08:00
}
1997-11-25 06:05:25 +08:00
/* If shift is down and this is not the first paint
* stroke, then draw a line from the last coords to the pointer
*/
else if (bevent->state & GDK_SHIFT_MASK)
1997-11-25 06:05:25 +08:00
{
draw_line = TRUE;
1997-11-25 06:05:25 +08:00
paint_core->startx = paint_core->lastx;
paint_core->starty = paint_core->lasty;
paint_core->startpressure = paint_core->lastpressure;
paint_core->startxtilt = paint_core->lastxtilt;
paint_core->startytilt = paint_core->lastytilt;
#ifdef GTK_HAVE_SIX_VALUATORS
paint_core->startwheel = paint_core->lastwheel;
#endif /* GTK_HAVE_SIX_VALUATORS */
/* Restrict to multiples of 15 degrees if ctrl is pressed */
if (bevent->state & GDK_CONTROL_MASK)
{
int tangens2[6] = { 34, 106, 196, 334, 618, 1944 };
int cosinus[7] = { 256, 247, 222, 181, 128, 66, 0 };
int dx, dy, i, radius, frac;
dx = paint_core->curx - paint_core->lastx;
dy = paint_core->cury - paint_core->lasty;
if (dy)
{
radius = sqrt (SQR (dx) + SQR (dy));
frac = abs ((dx << 8) / dy);
for (i = 0; i < 6; i++)
{
if (frac < tangens2[i])
break;
}
dx = dx > 0 ? (cosinus[6-i] * radius) >> 8 : - ((cosinus[6-i] * radius) >> 8);
dy = dy > 0 ? (cosinus[i] * radius) >> 8 : - ((cosinus[i] * radius) >> 8);
}
paint_core->curx = paint_core->lastx + dx;
paint_core->cury = paint_core->lasty + dy;
}
1997-11-25 06:05:25 +08:00
}
tool->state = ACTIVE;
tool->gdisp_ptr = gdisp_ptr;
tool->paused_count = 0;
/* pause the current selection and grab the pointer */
gdisplays_selection_visibility (gdisp->gimage, SelectionPause);
1997-11-25 06:05:25 +08:00
/* add motion memory if perfectmouse is set */
if (perfectmouse != 0)
1997-11-25 06:05:25 +08:00
gdk_pointer_grab (gdisp->canvas->window, FALSE,
GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
NULL, NULL, bevent->time);
else
gdk_pointer_grab (gdisp->canvas->window, FALSE,
GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON1_MOTION_MASK |
GDK_BUTTON_RELEASE_MASK,
1997-11-25 06:05:25 +08:00
NULL, NULL, bevent->time);
/* Let the specific painting function initialize itself */
(* paint_core->paint_func) (paint_core, drawable, INIT_PAINT);
1997-11-25 06:05:25 +08:00
if (paint_core->pick_colors
&& !(bevent->state & GDK_SHIFT_MASK)
&& (bevent->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)))
{
paint_core_sample_color (drawable, x, y, bevent->state);
paint_core->pick_state = TRUE;
return;
}
else
paint_core->pick_state = FALSE;
1997-11-25 06:05:25 +08:00
/* Paint to the image */
if (draw_line)
{
draw_core_pause (paint_core->core, tool);
paint_core_interpolate (paint_core, drawable);
1997-11-25 06:05:25 +08:00
paint_core->lastx = paint_core->curx;
paint_core->lasty = paint_core->cury;
paint_core->lastpressure = paint_core->curpressure;
paint_core->lastxtilt = paint_core->curxtilt;
paint_core->lastytilt = paint_core->curytilt;
#ifdef GTK_HAVE_SIX_VALUATORS
paint_core->lastwheel = paint_core->curwheel;
#endif /* GTK_HAVE_SIX_VALUATORS */
1997-11-25 06:05:25 +08:00
}
else
{
/* If we current point == last point, check if the brush
* wants to be painted in that case. (Direction dependent
* pixmap brush pipes don't, as they don't know which
* pixmap to select.)
*/
if (paint_core->lastx != paint_core->curx
|| paint_core->lasty != paint_core->cury
|| (* GIMP_BRUSH_CLASS (GTK_OBJECT (paint_core->brush)
->klass)->want_null_motion) (paint_core))
{
if (paint_core->flags & TOOL_CAN_HANDLE_CHANGING_BRUSH)
paint_core->brush =
(* GIMP_BRUSH_CLASS (GTK_OBJECT (paint_core->brush)
->klass)->select_brush) (paint_core);
(* paint_core->paint_func) (paint_core, drawable, MOTION_PAINT);
}
}
if (paint_core->flags & TOOL_TRACES_ON_WINDOW)
(* paint_core->paint_func) (paint_core, drawable, PRETRACE_PAINT);
gdisplay_flush_now (gdisp);
if (paint_core->flags & TOOL_TRACES_ON_WINDOW)
(* paint_core->paint_func) (paint_core, drawable, POSTTRACE_PAINT);
1997-11-25 06:05:25 +08:00
}
void
paint_core_button_release (Tool *tool,
GdkEventButton *bevent,
gpointer gdisp_ptr)
1997-11-25 06:05:25 +08:00
{
GDisplay * gdisp;
GImage * gimage;
PaintCore * paint_core;
gdisp = (GDisplay *) gdisp_ptr;
gimage = gdisp->gimage;
paint_core = (PaintCore *) tool->private;
/* resume the current selection and ungrab the pointer */
gdisplays_selection_visibility (gdisp->gimage, SelectionResume);
1997-11-25 06:05:25 +08:00
gdk_pointer_ungrab (bevent->time);
gdk_flush ();
/* Let the specific painting function finish up */
(* paint_core->paint_func) (paint_core, gimage_active_drawable (gdisp->gimage), FINISH_PAINT);
/* Set tool state to inactive -- no longer painting */
draw_core_stop (paint_core->core, tool);
1997-11-25 06:05:25 +08:00
tool->state = INACTIVE;
paint_core->pick_state = FALSE;
1997-11-25 06:05:25 +08:00
paint_core_finish (paint_core, gimage_active_drawable (gdisp->gimage), tool->ID);
gdisplays_flush ();
}
void
paint_core_motion (Tool *tool,
GdkEventMotion *mevent,
gpointer gdisp_ptr)
1997-11-25 06:05:25 +08:00
{
GDisplay * gdisp;
PaintCore * paint_core;
gdisp = (GDisplay *) gdisp_ptr;
paint_core = (PaintCore *) tool->private;
gdisplay_untransform_coords_f (gdisp, (double) mevent->x, (double) mevent->y,
&paint_core->curx, &paint_core->cury, TRUE);
if (paint_core->pick_state)
{
paint_core_sample_color (gimage_active_drawable (gdisp->gimage),
paint_core->curx, paint_core->cury, mevent->state);
return;
}
paint_core->curpressure = mevent->pressure;
paint_core->curxtilt = mevent->xtilt;
paint_core->curytilt = mevent->ytilt;
#ifdef GTK_HAVE_SIX_VALUATORS
paint_core->curwheel = mevent->wheel;
#endif /* GTK_HAVE_SIX_VALUATORS */
1997-11-25 06:05:25 +08:00
paint_core->state = mevent->state;
paint_core_interpolate (paint_core, gimage_active_drawable (gdisp->gimage));
if (paint_core->flags & TOOL_TRACES_ON_WINDOW)
(* paint_core->paint_func) (paint_core, gimage_active_drawable (gdisp->gimage), PRETRACE_PAINT);
gdisplay_flush_now (gdisp);
if (paint_core->flags & TOOL_TRACES_ON_WINDOW)
(* paint_core->paint_func) (paint_core, gimage_active_drawable (gdisp->gimage), POSTTRACE_PAINT);
1997-11-25 06:05:25 +08:00
paint_core->lastx = paint_core->curx;
paint_core->lasty = paint_core->cury;
paint_core->lastpressure = paint_core->curpressure;
paint_core->lastxtilt = paint_core->curxtilt;
paint_core->lastytilt = paint_core->curytilt;
#ifdef GTK_HAVE_SIX_VALUATORS
paint_core->lastwheel = paint_core->curwheel;
#endif /* GTK_HAVE_SIX_VALUATORS */
1997-11-25 06:05:25 +08:00
}
void
paint_core_cursor_update (Tool *tool,
GdkEventMotion *mevent,
gpointer gdisp_ptr)
1997-11-25 06:05:25 +08:00
{
GDisplay *gdisp;
Layer *layer;
PaintCore * paint_core;
1997-11-25 06:05:25 +08:00
GdkCursorType ctype = GDK_TOP_LEFT_ARROW;
1999-05-15 08:36:42 +08:00
int x, y;
gchar status_str[STATUSBAR_SIZE];
1997-11-25 06:05:25 +08:00
gdisp = (GDisplay *) gdisp_ptr;
paint_core = (PaintCore *) tool->private;
1997-11-25 06:05:25 +08:00
/* undraw the current tool */
draw_core_pause (paint_core->core, tool);
if (paint_core->context_id)
gtk_statusbar_pop (GTK_STATUSBAR (gdisp->statusbar), paint_core->context_id);
if ((layer = gimage_get_active_layer (gdisp->gimage)))
{
/* If shift is down and this is not the first paint stroke, draw a line */
if (gdisp_ptr == tool->gdisp_ptr && (mevent->state & GDK_SHIFT_MASK))
{
gdouble dx, dy, d;
ctype = GDK_PENCIL;
/* Get the current coordinates */
gdisplay_untransform_coords_f (gdisp,
(double) mevent->x,
(double) mevent->y,
&paint_core->curx,
&paint_core->cury, TRUE);
1999-05-15 08:36:42 +08:00
dx = paint_core->curx - paint_core->lastx;
dy = paint_core->cury - paint_core->lasty;
/* Restrict to multiples of 15 degrees if ctrl is pressed */
if (mevent->state & GDK_CONTROL_MASK)
{
int idx = dx;
int idy = dy;
int tangens2[6] = { 34, 106, 196, 334, 618, 1944 };
int cosinus[7] = { 256, 247, 222, 181, 128, 66, 0 };
int i, radius, frac;
if (idy)
{
radius = sqrt (SQR (idx) + SQR (idy));
frac = abs ((idx << 8) / idy);
for (i = 0; i < 6; i++)
{
if (frac < tangens2[i])
break;
}
dx = idx > 0 ? (cosinus[6-i] * radius) >> 8 : - ((cosinus[6-i] * radius) >> 8);
dy = idy > 0 ? (cosinus[i] * radius) >> 8 : - ((cosinus[i] * radius) >> 8);
}
paint_core->curx = paint_core->lastx + dx;
paint_core->cury = paint_core->lasty + dy;
}
/* show distance in statusbar */
if (gdisp->dot_for_dot)
{
d = sqrt (SQR (dx) + SQR (dy));
g_snprintf (status_str, STATUSBAR_SIZE, "%.1f %s", d, _("pixels"));
}
else
{
gchar *format_str = g_strdup_printf ("%%.%df %s",
gimp_unit_get_digits (gdisp->gimage->unit),
gimp_unit_get_symbol (gdisp->gimage->unit));
d = gimp_unit_get_factor (gdisp->gimage->unit) *
sqrt (SQR (dx / gdisp->gimage->xresolution) + SQR (dy / gdisp->gimage->yresolution));
g_snprintf (status_str, STATUSBAR_SIZE, format_str, d);
g_free (format_str);
}
gtk_statusbar_push (GTK_STATUSBAR (gdisp->statusbar), paint_core->context_id,
status_str);
if (paint_core->core->gc == NULL)
draw_core_start (paint_core->core, gdisp->canvas->window, tool);
else
{
/* is this a bad hack ? */
paint_core->core->paused_count = 0;
draw_core_resume (paint_core->core, tool);
}
}
/* If Ctrl or Mod1 is pressed, pick colors */
else if (paint_core->pick_colors
&& !(mevent->state & GDK_SHIFT_MASK)
&& (mevent->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)))
{
ctype = GIMP_COLOR_PICKER_CURSOR;
}
/* Normal operation -- no modifier pressed or first stroke */
else
{
int off_x, off_y;
drawable_offsets (GIMP_DRAWABLE(layer), &off_x, &off_y);
gdisplay_untransform_coords (gdisp, (double) mevent->x, (double) mevent->y,
&x, &y, TRUE, FALSE);
if (x >= off_x && y >= off_y &&
x < (off_x + drawable_width (GIMP_DRAWABLE(layer))) &&
y < (off_y + drawable_height (GIMP_DRAWABLE(layer))))
{
/* One more test--is there a selected region?
* if so, is cursor inside?
*/
if (gimage_mask_is_empty (gdisp->gimage))
ctype = GDK_PENCIL;
else if (gimage_mask_value (gdisp->gimage, x, y))
ctype = GDK_PENCIL;
}
}
gdisplay_install_tool_cursor (gdisp, ctype);
}
1997-11-25 06:05:25 +08:00
}
void
namespace cleanups. 1999-06-21 Michael Natterer <mitschel@cs.tu-berlin.de> * app/context_manager.c: namespace cleanups. * app/commands.[ch] * app/menus.c: moved the "Toggle Selection" menu entry to "View", sprinkled some separators and made the layers/channels/paths popup menus consistent with Tigert's last ops buttons change. * app/fileops.c * app/plug_in.c: check for gdisplay_active() returning NULL in some more places. * app/[all tool related files]: - Turned the ToolAction and ToolState #define's into typedef'ed enums, so the compiler can do some more sanity checking. - Removed one more unused global variable "active_tool_layer". - Removed some #include's from tools.c. - Standardized the individual tools' structure names. - Moved showing/hiding the tool options to separate functions. - Stuff... * app/commands.c * app/disp_callbacks.c * app/gdisplay.c * app/tools.c: fixed the segfaults which happened when the image of one of the tools which have dialogs (levels/posterize/...) was deleted. My approach was to do stricter sanity checking and to set some gdisplay pointers correctly where appropriate, so I can't tell exactly where the bug was. The curves tool now(??) updates on every _second_ display change only, which is really obscure. Finding/changing the display to operate on should definitely be done by connecting to the user context's "display_changed" signal. * app/gimpset.c: emit the "remove" signal _after_ removing the pointer from the set. If this was not a bug but a feature, please let me know, we'll need two signals then.
1999-06-22 06:12:07 +08:00
paint_core_control (Tool *tool,
ToolAction action,
gpointer gdisp_ptr)
1997-11-25 06:05:25 +08:00
{
PaintCore * paint_core;
GDisplay *gdisp;
GimpDrawable *drawable;
1997-11-25 06:05:25 +08:00
gdisp = (GDisplay *) gdisp_ptr;
paint_core = (PaintCore *) tool->private;
drawable = gimage_active_drawable (gdisp->gimage);
1997-11-25 06:05:25 +08:00
switch (action)
{
namespace cleanups. 1999-06-21 Michael Natterer <mitschel@cs.tu-berlin.de> * app/context_manager.c: namespace cleanups. * app/commands.[ch] * app/menus.c: moved the "Toggle Selection" menu entry to "View", sprinkled some separators and made the layers/channels/paths popup menus consistent with Tigert's last ops buttons change. * app/fileops.c * app/plug_in.c: check for gdisplay_active() returning NULL in some more places. * app/[all tool related files]: - Turned the ToolAction and ToolState #define's into typedef'ed enums, so the compiler can do some more sanity checking. - Removed one more unused global variable "active_tool_layer". - Removed some #include's from tools.c. - Standardized the individual tools' structure names. - Moved showing/hiding the tool options to separate functions. - Stuff... * app/commands.c * app/disp_callbacks.c * app/gdisplay.c * app/tools.c: fixed the segfaults which happened when the image of one of the tools which have dialogs (levels/posterize/...) was deleted. My approach was to do stricter sanity checking and to set some gdisplay pointers correctly where appropriate, so I can't tell exactly where the bug was. The curves tool now(??) updates on every _second_ display change only, which is really obscure. Finding/changing the display to operate on should definitely be done by connecting to the user context's "display_changed" signal. * app/gimpset.c: emit the "remove" signal _after_ removing the pointer from the set. If this was not a bug but a feature, please let me know, we'll need two signals then.
1999-06-22 06:12:07 +08:00
case PAUSE:
1997-11-25 06:05:25 +08:00
break;
namespace cleanups. 1999-06-21 Michael Natterer <mitschel@cs.tu-berlin.de> * app/context_manager.c: namespace cleanups. * app/commands.[ch] * app/menus.c: moved the "Toggle Selection" menu entry to "View", sprinkled some separators and made the layers/channels/paths popup menus consistent with Tigert's last ops buttons change. * app/fileops.c * app/plug_in.c: check for gdisplay_active() returning NULL in some more places. * app/[all tool related files]: - Turned the ToolAction and ToolState #define's into typedef'ed enums, so the compiler can do some more sanity checking. - Removed one more unused global variable "active_tool_layer". - Removed some #include's from tools.c. - Standardized the individual tools' structure names. - Moved showing/hiding the tool options to separate functions. - Stuff... * app/commands.c * app/disp_callbacks.c * app/gdisplay.c * app/tools.c: fixed the segfaults which happened when the image of one of the tools which have dialogs (levels/posterize/...) was deleted. My approach was to do stricter sanity checking and to set some gdisplay pointers correctly where appropriate, so I can't tell exactly where the bug was. The curves tool now(??) updates on every _second_ display change only, which is really obscure. Finding/changing the display to operate on should definitely be done by connecting to the user context's "display_changed" signal. * app/gimpset.c: emit the "remove" signal _after_ removing the pointer from the set. If this was not a bug but a feature, please let me know, we'll need two signals then.
1999-06-22 06:12:07 +08:00
case RESUME:
1997-11-25 06:05:25 +08:00
break;
namespace cleanups. 1999-06-21 Michael Natterer <mitschel@cs.tu-berlin.de> * app/context_manager.c: namespace cleanups. * app/commands.[ch] * app/menus.c: moved the "Toggle Selection" menu entry to "View", sprinkled some separators and made the layers/channels/paths popup menus consistent with Tigert's last ops buttons change. * app/fileops.c * app/plug_in.c: check for gdisplay_active() returning NULL in some more places. * app/[all tool related files]: - Turned the ToolAction and ToolState #define's into typedef'ed enums, so the compiler can do some more sanity checking. - Removed one more unused global variable "active_tool_layer". - Removed some #include's from tools.c. - Standardized the individual tools' structure names. - Moved showing/hiding the tool options to separate functions. - Stuff... * app/commands.c * app/disp_callbacks.c * app/gdisplay.c * app/tools.c: fixed the segfaults which happened when the image of one of the tools which have dialogs (levels/posterize/...) was deleted. My approach was to do stricter sanity checking and to set some gdisplay pointers correctly where appropriate, so I can't tell exactly where the bug was. The curves tool now(??) updates on every _second_ display change only, which is really obscure. Finding/changing the display to operate on should definitely be done by connecting to the user context's "display_changed" signal. * app/gimpset.c: emit the "remove" signal _after_ removing the pointer from the set. If this was not a bug but a feature, please let me know, we'll need two signals then.
1999-06-22 06:12:07 +08:00
case HALT:
(* paint_core->paint_func) (paint_core, drawable, FINISH_PAINT);
draw_core_stop (paint_core->core, tool);
1997-11-25 06:05:25 +08:00
paint_core_cleanup ();
break;
namespace cleanups. 1999-06-21 Michael Natterer <mitschel@cs.tu-berlin.de> * app/context_manager.c: namespace cleanups. * app/commands.[ch] * app/menus.c: moved the "Toggle Selection" menu entry to "View", sprinkled some separators and made the layers/channels/paths popup menus consistent with Tigert's last ops buttons change. * app/fileops.c * app/plug_in.c: check for gdisplay_active() returning NULL in some more places. * app/[all tool related files]: - Turned the ToolAction and ToolState #define's into typedef'ed enums, so the compiler can do some more sanity checking. - Removed one more unused global variable "active_tool_layer". - Removed some #include's from tools.c. - Standardized the individual tools' structure names. - Moved showing/hiding the tool options to separate functions. - Stuff... * app/commands.c * app/disp_callbacks.c * app/gdisplay.c * app/tools.c: fixed the segfaults which happened when the image of one of the tools which have dialogs (levels/posterize/...) was deleted. My approach was to do stricter sanity checking and to set some gdisplay pointers correctly where appropriate, so I can't tell exactly where the bug was. The curves tool now(??) updates on every _second_ display change only, which is really obscure. Finding/changing the display to operate on should definitely be done by connecting to the user context's "display_changed" signal. * app/gimpset.c: emit the "remove" signal _after_ removing the pointer from the set. If this was not a bug but a feature, please let me know, we'll need two signals then.
1999-06-22 06:12:07 +08:00
default:
break;
1997-11-25 06:05:25 +08:00
}
}
void
paint_core_draw (Tool *tool)
1997-11-25 06:05:25 +08:00
{
GDisplay *gdisp;
PaintCore * paint_core;
1999-05-15 08:36:42 +08:00
int tx1, ty1, tx2, ty2;
paint_core = (PaintCore *) tool->private;
/* if shift was never used, paint_core->core->gc is NULL
and we don't care about a redraw */
if (paint_core->core->gc != NULL)
{
gdisp = (GDisplay *) tool->gdisp_ptr;
1999-05-15 08:36:42 +08:00
gdisplay_transform_coords (gdisp, paint_core->lastx, paint_core->lasty,
&tx1, &ty1, 1);
gdisplay_transform_coords (gdisp, paint_core->curx, paint_core->cury,
&tx2, &ty2, 1);
/* Draw start target */
gdk_draw_line (gdisp->canvas->window, paint_core->core->gc,
1999-05-15 08:36:42 +08:00
tx1 - (TARGET_WIDTH >> 1), ty1,
tx1 + (TARGET_WIDTH >> 1), ty1);
gdk_draw_line (gdisp->canvas->window, paint_core->core->gc,
1999-05-15 08:36:42 +08:00
tx1, ty1 - (TARGET_HEIGHT >> 1),
tx1, ty1 + (TARGET_HEIGHT >> 1));
/* Draw end target */
gdk_draw_line (gdisp->canvas->window, paint_core->core->gc,
1999-05-15 08:36:42 +08:00
tx2 - (TARGET_WIDTH >> 1), ty2,
tx2 + (TARGET_WIDTH >> 1), ty2);
gdk_draw_line (gdisp->canvas->window, paint_core->core->gc,
1999-05-15 08:36:42 +08:00
tx2, ty2 - (TARGET_HEIGHT >> 1),
tx2, ty2 + (TARGET_HEIGHT >> 1));
/* Draw the line between the start and end coords */
gdk_draw_line (gdisp->canvas->window, paint_core->core->gc,
1999-05-15 08:36:42 +08:00
tx1, ty1, tx2, ty2);
}
1997-11-25 06:05:25 +08:00
return;
}
1997-11-25 06:05:25 +08:00
Tool *
paint_core_new (ToolType type)
1997-11-25 06:05:25 +08:00
{
Tool * tool;
PaintCore * private;
tool = tools_new_tool (type);
private = g_new (PaintCore, 1);
1997-11-25 06:05:25 +08:00
private->core = draw_core_new (paint_core_draw);
1999-05-15 20:13:43 +08:00
private->pick_colors = FALSE;
private->flags = 0;
private->context_id = 0;
1997-11-25 06:05:25 +08:00
tool->private = (void *) private;
tool->button_press_func = paint_core_button_press;
1997-11-25 06:05:25 +08:00
tool->button_release_func = paint_core_button_release;
tool->motion_func = paint_core_motion;
tool->cursor_update_func = paint_core_cursor_update;
tool->control_func = paint_core_control;
1997-11-25 06:05:25 +08:00
return tool;
}
void
paint_core_free (Tool *tool)
1997-11-25 06:05:25 +08:00
{
PaintCore * paint_core;
paint_core = (PaintCore *) tool->private;
/* Make sure the selection core is not visible */
if (tool->state == ACTIVE && paint_core->core)
draw_core_stop (paint_core->core, tool);
/* Free the selection core */
if (paint_core->core)
draw_core_free (paint_core->core);
/* Cleanup memory */
paint_core_cleanup ();
/* Free the paint core */
g_free (paint_core);
}
int
paint_core_init (PaintCore *paint_core,
GimpDrawable *drawable,
double x,
double y)
1997-11-25 06:05:25 +08:00
{
app/airbrush.c app/apptypes.h app/brushes_cmds.c 1999-11-14 Michael Natterer <mitch@gimp.org> * app/airbrush.c * app/apptypes.h * app/brushes_cmds.c * tools/pdbgen/pdb/brushes.pdb * app/bucket_fill.c * app/clone.c * app/gimpbrushpipe.c * app/paint_core.c * app/patterns.h * app/patterns_cmds.c * tools/pdbgen/pdb/patterns.pdb: removed the GimpBrushP and GPatternP types and use ordinary pointers instead. The following stuff makes the "no_data" behaviour consistent. As a side-effect it should make the gimp work when there are _really_ no brushes/patterns/gradients. * app/brush_select.c * app/pattern_select.c: set the initial brush/pattern name to "No Brushes/Patterns available" instead of "Active". * app/devices.c: set the device contexts' brush/pattern/gradient names if we started with no_data, so we find them on refresh. * app/gimpbrushlist.c: set the name of the standard_brush to "Standard". * app/gimpcontext.c: don't replace the current brush/pattern/gradient's name if the new one to be set is the standard one. Together with the change in devices.c, this ensures that we get what is set in devicerc. Minor fixes. * app/gradient.c: changed gradients_init() to work like the other data init functions. Only insert a default gradient in the gradients list when the editor is opened (this means that the gradients now behave like brushes/patterns when we start with "no_data"). New function gradient_update() avoids tons of useless redraws of all clist gradient previews whenever the gradient editor wants to update it's large preview. * app/gradient_select.c: don't segfault when the user tries to drag from an empty gradient list. * app/patterns.c: set the index of the standard_pattern to -1 to indicate that it's not part of the pattern list.
1999-11-14 18:50:19 +08:00
static GimpBrush *brush = NULL;
1997-11-25 06:05:25 +08:00
paint_core->curx = x;
paint_core->cury = y;
/* Set up some defaults for non-gui use */
if (paint_core == &non_gui_paint_core)
{
paint_core->startpressure = paint_core->lastpressure = paint_core->curpressure = 0.5;
paint_core->startxtilt = paint_core->lastxtilt = paint_core->curxtilt = 0;
paint_core->startytilt = paint_core->lastytilt = paint_core->curytilt = 0;
#ifdef GTK_HAVE_SIX_VALUATORS
paint_core->startwheel = paint_core->lastwheel = paint_core->curwheel = 0.5;
#endif /* GTK_HAVE_SIX_VALUATORS */
}
1997-11-25 06:05:25 +08:00
/* Each buffer is the same size as the maximum bounds of the active brush... */
1999-10-27 02:27:27 +08:00
if (brush && brush != gimp_context_get_brush (NULL))
{
gtk_signal_disconnect_by_func (GTK_OBJECT (brush),
GTK_SIGNAL_FUNC (paint_core_invalidate_cache),
NULL);
gtk_object_unref (GTK_OBJECT (brush));
}
if (!(brush = gimp_context_get_brush (NULL)))
1997-11-25 06:05:25 +08:00
{
g_message (_("No brushes available for use with this tool."));
1997-11-25 06:05:25 +08:00
return FALSE;
}
1999-10-27 02:27:27 +08:00
gtk_object_ref (GTK_OBJECT (brush));
gtk_signal_connect (GTK_OBJECT (brush), "dirty",
GTK_SIGNAL_FUNC (paint_core_invalidate_cache),
NULL);
1997-11-25 06:05:25 +08:00
First version of per-tool paint options. No PDB interface yet. The tool 1999-04-22 Michael Natterer <mitschel@cs.tu-berlin.de> First version of per-tool paint options. No PDB interface yet. The tool options dialog got rather big when in per-tool mode, so it will probably have to become a notebook. It's not yet 100% consistent. If switched off, everything should behave exactly like before. * app/Makefile.am * app/paint_options.h: new file * app/tool_options.c: PaintOptions gui. Maintain a list of all paint tools' ToolOptions to enable switching between global and per-tool paint options. * app/brush_select.[ch]: changed packing boxes, tables, ... The paint options in the brush selection can be hidden now. Moved create_paint_mode_menu() to paint_options.h and tool_options.c and renamed it to paint_mode_menu_new(). * app/gimage_mask.c * app/gimpbrush.[ch] * app/gimpbrushlist.[ch] * app/paint_core.c: moved gimp_brush_[set|get]_spacing() from gimpbrushlist.[ch] to gimpbrush.[ch]. Moved gimp_brush_[get|set]_[opacity|paint_mode]() to paint_options.h and tool_options.c and renamed them to paint_options_*_*(). They are "global paint options" now. * app/airbrush.c * app/blend.c * app/bucket_fill.c * app/clone.c * app/convolve.c * app/eraser.c * app/ink.c * app/paintbrush.c * app/pencil.c: all paint tools' options are derived from "PaintOptions" now. Opacity and paint mode are obtained through macros which take into account the current paint options mode. * app/buildmenu.h: #include <gtk/gtk.h> * app/color_picker.c * app/text_tool.c: changed spacings. * app/gimprc.[ch]: new gimprc option "global-paint-options" * app/preferences_dialog.c: Added a "Tool Options" page. Code cleanup. Some work on the convenience constructors test site. * app/tools.c: fixed "unused variable" warning.
1999-04-22 22:34:00 +08:00
paint_core->spacing = (double) gimp_brush_get_spacing (brush) / 100.0;
paint_core->brush = brush;
1997-11-25 06:05:25 +08:00
/* free the block structures */
if (undo_tiles)
tile_manager_destroy (undo_tiles);
if (canvas_tiles)
tile_manager_destroy (canvas_tiles);
/* Allocate the undo structure */
undo_tiles = tile_manager_new (drawable_width (drawable),
drawable_height (drawable),
drawable_bytes (drawable));
1997-11-25 06:05:25 +08:00
/* Allocate the canvas blocks structure */
canvas_tiles = tile_manager_new (drawable_width (drawable),
drawable_height (drawable), 1);
1997-11-25 06:05:25 +08:00
/* Get the initial undo extents */
paint_core->x1 = paint_core->x2 = paint_core->curx;
paint_core->y1 = paint_core->y2 = paint_core->cury;
paint_core->distance = 0.0;
paint_core->pixel_dist = 0.0;
1997-11-25 06:05:25 +08:00
return TRUE;
}
void
paint_core_get_color_from_gradient (PaintCore *paint_core,
double gradient_length,
double *r,
double *g,
double *b,
double *a,
int mode)
{
double y;
double distance; /* distance in current brush stroke */
distance = paint_core->pixel_dist;
y = ((double) distance / gradient_length);
/* for the once modes, set y close to 1.0 after the first chunk */
if ( (mode == ONCE_FORWARD || mode == ONCE_BACKWARDS) && y >= 1.0 )
y = 0.9999999;
if ( (((int)y & 1) && mode != LOOP_SAWTOOTH) || mode == ONCE_BACKWARDS )
y = 1.0 - (y - (int)y);
else
y = y - (int)y;
1999-10-27 02:27:27 +08:00
gradient_get_color_at (gimp_context_get_gradient (NULL), y, r, g, b, a);
}
1997-11-25 06:05:25 +08:00
void
paint_core_interpolate (PaintCore *paint_core,
GimpDrawable *drawable)
1997-11-25 06:05:25 +08:00
{
double n;
vector2d delta;
#ifdef GTK_HAVE_SIX_VALUATORS
double dpressure, dxtilt, dytilt, dwheel;
#else /* !GTK_HAVE_SIX_VALUATORS */
double dpressure, dxtilt, dytilt;
#endif /* GTK_HAVE_SIX_VALUATORS */
/* double spacing; */
/* double lastscale, curscale; */
1997-11-25 06:05:25 +08:00
double left;
double t;
double initial;
double dist;
double total;
double pixel_dist;
double pixel_initial;
double xd, yd;
double mag;
delta.x = paint_core->curx - paint_core->lastx;
delta.y = paint_core->cury - paint_core->lasty;
dpressure = paint_core->curpressure - paint_core->lastpressure;
dxtilt = paint_core->curxtilt - paint_core->lastxtilt;
dytilt = paint_core->curytilt - paint_core->lastytilt;
#ifdef GTK_HAVE_SIX_VALUATORS
dwheel = paint_core->curwheel - paint_core->lastwheel;
#endif /* GTK_HAVE_SIX_VALUATORS */
1997-11-25 06:05:25 +08:00
/* return if there has been no motion */
#ifdef GTK_HAVE_SIX_VALUATORS
if (!delta.x && !delta.y && !dpressure && !dxtilt && !dytilt && !dwheel)
#else /* !GTK_HAVE_SIX_VALUATORS */
if (!delta.x && !delta.y && !dpressure && !dxtilt && !dytilt)
#endif /* GTK_HAVE_SIX_VALUATORS */
1997-11-25 06:05:25 +08:00
return;
/* calculate the distance traveled in the coordinate space of the brush */
mag = vector2d_magnitude (&(paint_core->brush->x_axis));
xd = vector2d_dot_product(&delta, &(paint_core->brush->x_axis)) / (mag*mag);
mag = vector2d_magnitude (&(paint_core->brush->y_axis));
yd = vector2d_dot_product(&delta, &(paint_core->brush->y_axis)) / (mag*mag);
dist = 0.5 * sqrt (xd*xd + yd*yd);
1997-11-25 06:05:25 +08:00
total = dist + paint_core->distance;
initial = paint_core->distance;
pixel_dist = vector2d_magnitude (&delta);
pixel_initial = paint_core->pixel_dist;
/* FIXME: need to adapt the spacing to the size */
/* lastscale = MIN (paint_core->lastpressure, 1/256); */
/* curscale = MIN (paint_core->curpressure, 1/256); */
/* spacing = paint_core->spacing * sqrt (0.5 * (lastscale + curscale)); */
1997-11-25 06:05:25 +08:00
while (paint_core->distance < total)
{
n = (int) (paint_core->distance / paint_core->spacing + 1.0 + EPSILON);
left = n * paint_core->spacing - paint_core->distance;
1997-11-25 06:05:25 +08:00
paint_core->distance += left;
if (paint_core->distance <= (total+EPSILON))
1997-11-25 06:05:25 +08:00
{
t = (paint_core->distance - initial) / dist;
paint_core->curx = paint_core->lastx + delta.x * t;
paint_core->cury = paint_core->lasty + delta.y * t;
paint_core->pixel_dist = pixel_initial + pixel_dist * t;
paint_core->curpressure = paint_core->lastpressure + dpressure * t;
paint_core->curxtilt = paint_core->lastxtilt + dxtilt * t;
paint_core->curytilt = paint_core->lastytilt + dytilt * t;
#ifdef GTK_HAVE_SIX_VALUATORS
paint_core->curwheel = paint_core->lastwheel + dwheel * t;
#endif /* GTK_HAVE_SIX_VALUATORS */
if (paint_core->flags & TOOL_CAN_HANDLE_CHANGING_BRUSH)
paint_core->brush =
(* GIMP_BRUSH_CLASS (GTK_OBJECT (paint_core->brush)
->klass)->select_brush) (paint_core);
(* paint_core->paint_func) (paint_core, drawable, MOTION_PAINT);
1997-11-25 06:05:25 +08:00
}
}
paint_core->distance = total;
paint_core->pixel_dist = pixel_initial + pixel_dist;
paint_core->curx = paint_core->lastx + delta.x;
paint_core->cury = paint_core->lasty + delta.y;
paint_core->curpressure = paint_core->lastpressure + dpressure;
paint_core->curxtilt = paint_core->lastxtilt + dxtilt;
paint_core->curytilt = paint_core->lastytilt + dytilt;
#ifdef GTK_HAVE_SIX_VALUATORS
paint_core->curwheel = paint_core->lastwheel + dwheel;
#endif /* GTK_HAVE_SIX_VALUATORS */
1997-11-25 06:05:25 +08:00
}
void
paint_core_finish (PaintCore *paint_core,
GimpDrawable *drawable,
int tool_id)
1997-11-25 06:05:25 +08:00
{
GImage *gimage;
PaintUndo *pu;
if (! (gimage = drawable_gimage (drawable)))
1997-11-25 06:05:25 +08:00
return;
/* Determine if any part of the image has been altered--
* if nothing has, then just return...
*/
1997-11-25 06:05:25 +08:00
if ((paint_core->x2 == paint_core->x1) || (paint_core->y2 == paint_core->y1))
return;
undo_push_group_start (gimage, PAINT_CORE_UNDO);
pu = (PaintUndo *) g_malloc (sizeof (PaintUndo));
pu->tool_ID = tool_id;
pu->lastx = paint_core->startx;
pu->lasty = paint_core->starty;
pu->lastpressure = paint_core->startpressure;
pu->lastxtilt = paint_core->startxtilt;
pu->lastytilt = paint_core->startytilt;
#ifdef GTK_HAVE_SIX_VALUATORS
pu->lastwheel = paint_core->startwheel;
#endif /* GTK_HAVE_SIX_VALUATORS */
1997-11-25 06:05:25 +08:00
/* Push a paint undo */
undo_push_paint (gimage, pu);
/* push an undo */
drawable_apply_image (drawable, paint_core->x1, paint_core->y1,
1997-11-25 06:05:25 +08:00
paint_core->x2, paint_core->y2, undo_tiles, TRUE);
undo_tiles = NULL;
/* push the group end */
undo_push_group_end (gimage);
/* invalidate the drawable--have to do it here, because
* it is not done during the actual painting.
*/
drawable_invalidate_preview (drawable);
1997-11-25 06:05:25 +08:00
}
void
paint_core_cleanup ()
{
/* CLEANUP */
/* If the undo tiles exist, nuke them */
if (undo_tiles)
{
tile_manager_destroy (undo_tiles);
undo_tiles = NULL;
}
/* If the canvas blocks exist, nuke them */
if (canvas_tiles)
{
tile_manager_destroy (canvas_tiles);
canvas_tiles = NULL;
}
/* Free the temporary buffers if they exist */
free_paint_buffers ();
}
/************************/
/* Painting functions */
/************************/
TempBuf *
paint_core_get_paint_area (PaintCore *paint_core,
GimpDrawable *drawable,
gdouble scale)
1997-11-25 06:05:25 +08:00
{
int x, y;
int x1, y1, x2, y2;
int bytes;
int dwidth, dheight;
int bwidth, bheight;
1997-11-25 06:05:25 +08:00
bytes = drawable_has_alpha (drawable) ?
drawable_bytes (drawable) : drawable_bytes (drawable) + 1;
1997-11-25 06:05:25 +08:00
paint_core_calculate_brush_size (paint_core->brush->mask, scale,
&bwidth, &bheight);
1997-11-25 06:05:25 +08:00
/* adjust the x and y coordinates to the upper left corner of the brush */
x = (int) paint_core->curx - (bwidth >> 1);
y = (int) paint_core->cury - (bheight >> 1);
1997-11-25 06:05:25 +08:00
dwidth = drawable_width (drawable);
dheight = drawable_height (drawable);
x1 = BOUNDS (x - 1, 0, dwidth);
y1 = BOUNDS (y - 1, 0, dheight);
x2 = BOUNDS (x + bwidth + 1, 0, dwidth);
y2 = BOUNDS (y + bheight + 1, 0, dheight);
1997-11-25 06:05:25 +08:00
/* configure the canvas buffer */
if ((x2 - x1) && (y2 - y1))
canvas_buf = temp_buf_resize (canvas_buf, bytes, x1, y1,
(x2 - x1), (y2 - y1));
else
return NULL;
return canvas_buf;
}
TempBuf *
paint_core_get_orig_image (PaintCore *paint_core,
GimpDrawable *drawable,
int x1, int y1, int x2, int y2)
1997-11-25 06:05:25 +08:00
{
PixelRegion srcPR, destPR;
Tile *undo_tile;
int h;
int refd;
1997-11-25 06:05:25 +08:00
int pixelwidth;
int dwidth, dheight;
1997-11-25 06:05:25 +08:00
unsigned char * s, * d;
void * pr;
orig_buf = temp_buf_resize (orig_buf, drawable_bytes (drawable),
1997-11-25 06:05:25 +08:00
x1, y1, (x2 - x1), (y2 - y1));
dwidth = drawable_width (drawable);
dheight = drawable_height (drawable);
x1 = BOUNDS (x1, 0, dwidth);
y1 = BOUNDS (y1, 0, dheight);
x2 = BOUNDS (x2, 0, dwidth);
y2 = BOUNDS (y2, 0, dheight);
1997-11-25 06:05:25 +08:00
/* configure the pixel regions */
pixel_region_init (&srcPR, drawable_data (drawable), x1, y1,
(x2 - x1), (y2 - y1), FALSE);
1997-11-25 06:05:25 +08:00
destPR.bytes = orig_buf->bytes;
destPR.x = 0; destPR.y = 0;
destPR.w = (x2 - x1); destPR.h = (y2 - y1);
destPR.rowstride = orig_buf->bytes * orig_buf->width;
destPR.data = temp_buf_data (orig_buf) +
(y1 - orig_buf->y) * destPR.rowstride + (x1 - orig_buf->x) * destPR.bytes;
for (pr = pixel_regions_register (2, &srcPR, &destPR);
pr != NULL;
pr = pixel_regions_process (pr))
1997-11-25 06:05:25 +08:00
{
/* If the undo tile corresponding to this location is valid, use it */
undo_tile = tile_manager_get_tile (undo_tiles, srcPR.x, srcPR.y,
FALSE, FALSE);
1998-08-12 01:35:34 +08:00
if (tile_is_valid (undo_tile) == TRUE)
1997-11-25 06:05:25 +08:00
{
refd = 1;
undo_tile = tile_manager_get_tile (undo_tiles, srcPR.x, srcPR.y,
TRUE, FALSE);
s = (unsigned char*)tile_data_pointer (undo_tile, 0, 0) +
1998-08-12 01:35:34 +08:00
srcPR.rowstride * (srcPR.y % TILE_HEIGHT) +
srcPR.bytes * (srcPR.x % TILE_WIDTH); /* dubious... */
1997-11-25 06:05:25 +08:00
}
else
{
refd = 0;
1997-11-25 06:05:25 +08:00
s = srcPR.data;
}
d = destPR.data;
pixelwidth = srcPR.w * srcPR.bytes;
h = srcPR.h;
while (h --)
{
memcpy (d, s, pixelwidth);
s += srcPR.rowstride;
d += destPR.rowstride;
}
if (refd)
tile_release (undo_tile, FALSE);
1997-11-25 06:05:25 +08:00
}
return orig_buf;
}
void
paint_core_paste_canvas (PaintCore *paint_core,
GimpDrawable *drawable,
int brush_opacity,
int image_opacity,
LayerModeEffects paint_mode,
BrushApplicationMode brush_hardness,
gdouble brush_scale,
PaintApplicationMode mode)
1997-11-25 06:05:25 +08:00
{
MaskBuf *brush_mask;
/* get the brush mask */
brush_mask = paint_core_get_brush_mask (paint_core, brush_hardness, brush_scale);
1997-11-25 06:05:25 +08:00
/* paste the canvas buf */
paint_core_paste (paint_core, brush_mask, drawable,
1997-11-25 06:05:25 +08:00
brush_opacity, image_opacity, paint_mode, mode);
}
/* Similar to paint_core_paste_canvas, but replaces the alpha channel
rather than using it to composite (i.e. transparent over opaque
becomes transparent rather than opauqe. */
void
paint_core_replace_canvas (PaintCore *paint_core,
GimpDrawable *drawable,
int brush_opacity,
int image_opacity,
BrushApplicationMode brush_hardness,
gdouble brush_scale,
PaintApplicationMode mode)
{
MaskBuf *brush_mask;
/* get the brush mask */
brush_mask = paint_core_get_brush_mask (paint_core, brush_hardness, brush_scale);
/* paste the canvas buf */
paint_core_replace (paint_core, brush_mask, drawable,
brush_opacity, image_opacity, mode);
}
1999-10-27 02:27:27 +08:00
static MaskBuf *last_brush_mask = NULL;
static gboolean cache_invalid = FALSE;
1999-10-27 02:27:27 +08:00
static void
paint_core_invalidate_cache (GimpBrush *brush,
gpointer *blah)
{
1999-10-27 02:27:27 +08:00
/* Make sure we don't cache data for a brush that has changed */
if (last_brush_mask == brush->mask)
1999-10-27 02:27:27 +08:00
cache_invalid = TRUE;
}
1997-11-25 06:05:25 +08:00
/************************************************************
* LOCAL FUNCTION DEFINITIONS *
************************************************************/
static void
paint_core_calculate_brush_size (MaskBuf *mask,
double scale,
int *width,
int *height)
{
if (current_device == GDK_CORE_POINTER)
{
*width = mask->width;
*height = mask->height;
}
else
{
double ratio;
if (scale < 1/256)
ratio = 1/16;
else
ratio = sqrt (scale);
*width = MAX ((int)(mask->width * ratio + 0.5), 1);
*height = MAX ((int)(mask->height * ratio + 0.5), 1);
}
}
1997-11-25 06:05:25 +08:00
static MaskBuf *
paint_core_subsample_mask (MaskBuf *mask,
double x,
double y)
1997-11-25 06:05:25 +08:00
{
MaskBuf * dest;
double left;
unsigned char * m, * d;
const int * k;
1997-11-25 06:05:25 +08:00
int index1, index2;
const int * kernel;
1997-11-25 06:05:25 +08:00
int new_val;
int i, j;
int r, s;
x += (x < 0) ? mask->width : 0;
left = x - floor(x) + 0.125;
1997-11-25 06:05:25 +08:00
index1 = (int) (left * 4);
y += (y < 0) ? mask->height : 0;
left = y - floor(y) + 0.125;
1997-11-25 06:05:25 +08:00
index2 = (int) (left * 4);
kernel = subsample[index2][index1];
if ((mask == last_brush_mask) && kernel_brushes[index2][index1] &&
!cache_invalid)
1997-11-25 06:05:25 +08:00
return kernel_brushes[index2][index1];
else if (mask != last_brush_mask || cache_invalid)
for (i = 0; i < 5; i++)
for (j = 0; j < 5; j++)
1997-11-25 06:05:25 +08:00
{
if (kernel_brushes[i][j])
mask_buf_free (kernel_brushes[i][j]);
kernel_brushes[i][j] = NULL;
}
last_brush_mask = mask;
1999-10-27 02:27:27 +08:00
cache_invalid = FALSE;
1997-11-25 06:05:25 +08:00
kernel_brushes[index2][index1] = mask_buf_new (mask->width + 2, mask->height + 2);
dest = kernel_brushes[index2][index1];
m = mask_buf_data (mask);
for (i = 0; i < mask->height; i++)
{
for (j = 0; j < mask->width; j++)
{
k = kernel;
for (r = 0; r < KERNEL_HEIGHT; r++)
{
d = mask_buf_data (dest) + (i+r) * dest->width + j;
s = KERNEL_WIDTH;
while (s--)
{
new_val = *d + ((*m * *k++ + 128) >> 8);
*d++ = MINIMUM (new_val, 255);
1997-11-25 06:05:25 +08:00
}
}
m++;
}
}
return dest;
}
/* #define FANCY_PRESSURE */
static MaskBuf *
paint_core_pressurize_mask (MaskBuf *brush_mask,
double x,
double y,
double pressure)
{
static MaskBuf *last_brush = NULL;
static unsigned char mapi[256];
unsigned char *source;
unsigned char *dest;
MaskBuf *subsample_mask;
int i;
#ifdef FANCY_PRESSURE
static double map[256];
double ds,s,c;
#endif
/* Get the raw subsampled mask */
subsample_mask = paint_core_subsample_mask (brush_mask, x, y);
/* Special case pressure = 0.5 */
if ((int)(pressure*100+0.5) == 50)
return subsample_mask;
/* Make sure we have the right sized buffer */
if (brush_mask != last_brush)
{
if (pressure_brush)
mask_buf_free (pressure_brush);
pressure_brush = mask_buf_new (brush_mask->width + 2,
brush_mask->height +2);
}
#ifdef FANCY_PRESSURE
/* Create the pressure profile
It is: I'(I) = tanh(20*(pressure-0.5)*I) : pressure > 0.5
I'(I) = 1 - tanh(20*(0.5-pressure)*(1-I)) : pressure < 0.5
It looks like:
low pressure medium pressure high pressure
| / --
| / /
/ / |
-- / |
*/
ds = (pressure - 0.5)*(20./256.);
s = 0;
c = 1.0;
if (ds > 0)
{
for (i=0;i<256;i++)
{
map[i] = s/c;
s += c*ds;
c += s*ds;
}
for (i=0;i<256;i++)
mapi[i] = (int)(255*map[i]/map[255]);
}
else
{
ds = -ds;
for (i=255;i>=0;i--)
{
map[i] = s/c;
s += c*ds;
c += s*ds;
}
for (i=0;i<256;i++)
mapi[i] = (int)(255*(1-map[i]/map[0]));
}
#else /* ! FANCY_PRESSURE */
for (i=0;i<256;i++)
{
int tmp = (pressure/0.5)*i;
if (tmp > 255)
mapi[i] = 255;
else
mapi[i] = tmp;
}
#endif /* FANCY_PRESSURE */
/* Now convert the brush */
source = mask_buf_data (subsample_mask);
dest = mask_buf_data (pressure_brush);
i = subsample_mask->width * subsample_mask->height;
while (i--)
{
*dest++ = mapi[(*source++)];
}
return pressure_brush;
}
1997-11-25 06:05:25 +08:00
static MaskBuf *
paint_core_solidify_mask (MaskBuf *brush_mask)
1997-11-25 06:05:25 +08:00
{
static MaskBuf *last_brush = NULL;
int i, j;
unsigned char * data, * src;
if (brush_mask == last_brush && !cache_invalid)
1997-11-25 06:05:25 +08:00
return solid_brush;
last_brush = brush_mask;
1997-11-25 06:05:25 +08:00
if (solid_brush)
mask_buf_free (solid_brush);
1997-11-25 06:05:25 +08:00
solid_brush = mask_buf_new (brush_mask->width + 2, brush_mask->height + 2);
/* get the data and advance one line into it */
data = mask_buf_data (solid_brush) + solid_brush->width;
src = mask_buf_data (brush_mask);
for (i = 0; i < brush_mask->height; i++)
{
data++;
for (j = 0; j < brush_mask->width; j++)
{
*data++ = (*src++) ? OPAQUE_OPACITY : TRANSPARENT_OPACITY;
1997-11-25 06:05:25 +08:00
}
data++;
}
return solid_brush;
}
static MaskBuf *
paint_core_scale_mask (MaskBuf *brush_mask,
gdouble scale)
{
static MaskBuf *last_brush = NULL;
static gint last_width = 0.0;
static gint last_height = 0.0;
gint dest_width, dest_height;
if (scale == 0.0)
return NULL;
if (scale == 1.0)
return brush_mask;
paint_core_calculate_brush_size (brush_mask, scale,
&dest_width, &dest_height);
if (brush_mask == last_brush && !cache_invalid &&
dest_width == last_width && dest_height == last_height)
return scale_brush;
if (scale_brush)
mask_buf_free (scale_brush);
last_brush = brush_mask;
last_width = dest_width;
last_height = dest_height;
scale_brush = brush_scale_mask (brush_mask, dest_width, dest_height);
cache_invalid = TRUE;
return scale_brush;
}
static MaskBuf *
paint_core_scale_pixmap (MaskBuf *brush_mask,
gdouble scale)
{
static MaskBuf *last_brush = NULL;
static gint last_width = 0.0;
static gint last_height = 0.0;
gint dest_width, dest_height;
if (scale == 0.0)
return NULL;
if (scale == 1.0)
return brush_mask;
paint_core_calculate_brush_size (brush_mask, scale,
&dest_width, &dest_height);
if (brush_mask == last_brush && !cache_invalid &&
dest_width == last_width && dest_height == last_height)
return scale_pixmap;
if (scale_pixmap)
mask_buf_free (scale_pixmap);
last_brush = brush_mask;
last_width = dest_width;
last_height = dest_height;
scale_pixmap = brush_scale_pixmap (brush_mask, dest_width, dest_height);
cache_invalid = TRUE;
return scale_pixmap;
}
1997-11-25 06:05:25 +08:00
static MaskBuf *
paint_core_get_brush_mask (PaintCore *paint_core,
BrushApplicationMode brush_hardness,
gdouble scale)
1997-11-25 06:05:25 +08:00
{
MaskBuf * mask;
1997-11-25 06:05:25 +08:00
if (current_device == GDK_CORE_POINTER)
mask = paint_core->brush->mask;
else
mask = paint_core_scale_mask (paint_core->brush->mask, scale);
1997-11-25 06:05:25 +08:00
switch (brush_hardness)
{
case SOFT:
mask = paint_core_subsample_mask (mask, paint_core->curx, paint_core->cury);
1997-11-25 06:05:25 +08:00
break;
case HARD:
mask = paint_core_solidify_mask (mask);
1997-11-25 06:05:25 +08:00
break;
case PRESSURE:
mask = paint_core_pressurize_mask (mask, paint_core->curx, paint_core->cury,
paint_core->curpressure);
break;
1997-11-25 06:05:25 +08:00
default:
break;
}
return mask;
1997-11-25 06:05:25 +08:00
}
static void
paint_core_paste (PaintCore *paint_core,
MaskBuf *brush_mask,
GimpDrawable *drawable,
int brush_opacity,
int image_opacity,
LayerModeEffects paint_mode,
PaintApplicationMode mode)
1997-11-25 06:05:25 +08:00
{
GImage *gimage;
PixelRegion srcPR;
TileManager *alt = NULL;
int offx, offy;
if (! (gimage = drawable_gimage (drawable)))
1997-11-25 06:05:25 +08:00
return;
/* set undo blocks */
set_undo_tiles (drawable,
1997-11-25 06:05:25 +08:00
canvas_buf->x, canvas_buf->y,
canvas_buf->width, canvas_buf->height);
/* If the mode is CONSTANT:
* combine the canvas buf, the brush mask to the canvas tiles
*/
if (mode == CONSTANT)
{
/* initialize any invalid canvas tiles */
set_canvas_tiles (canvas_buf->x, canvas_buf->y,
canvas_buf->width, canvas_buf->height);
brush_to_canvas_tiles (paint_core, brush_mask, brush_opacity);
canvas_tiles_to_canvas_buf (paint_core);
1997-11-25 06:05:25 +08:00
alt = undo_tiles;
}
/* Otherwise:
* combine the canvas buf and the brush mask to the canvas buf
*/
else /* mode != CONSTANT */
brush_to_canvas_buf (paint_core, brush_mask, brush_opacity);
1997-11-25 06:05:25 +08:00
/* intialize canvas buf source pixel regions */
srcPR.bytes = canvas_buf->bytes;
srcPR.x = 0; srcPR.y = 0;
srcPR.w = canvas_buf->width;
srcPR.h = canvas_buf->height;
srcPR.rowstride = canvas_buf->width * canvas_buf->bytes;
srcPR.data = temp_buf_data (canvas_buf);
/* apply the paint area to the gimage */
gimage_apply_image (gimage, drawable, &srcPR,
1997-11-25 06:05:25 +08:00
FALSE, image_opacity, paint_mode,
alt, /* specify an alternative src1 */
canvas_buf->x, canvas_buf->y);
/* Update the undo extents */
paint_core->x1 = MINIMUM (paint_core->x1, canvas_buf->x);
paint_core->y1 = MINIMUM (paint_core->y1, canvas_buf->y);
paint_core->x2 = MAXIMUM (paint_core->x2, (canvas_buf->x + canvas_buf->width));
paint_core->y2 = MAXIMUM (paint_core->y2, (canvas_buf->y + canvas_buf->height));
/* Update the gimage--it is important to call gdisplays_update_area
* instead of drawable_update because we don't want the drawable
* preview to be constantly invalidated
*/
drawable_offsets (drawable, &offx, &offy);
gdisplays_update_area (gimage, canvas_buf->x + offx, canvas_buf->y + offy,
canvas_buf->width, canvas_buf->height);
}
/* This works similarly to paint_core_paste. However, instead of combining
the canvas to the paint core drawable using one of the combination
modes, it uses a "replace" mode (i.e. transparent pixels in the
canvas erase the paint core drawable).
When not drawing on alpha-enabled images, it just paints using NORMAL
mode.
*/
static void
paint_core_replace (PaintCore *paint_core,
MaskBuf *brush_mask,
GimpDrawable *drawable,
int brush_opacity,
int image_opacity,
PaintApplicationMode mode)
{
GImage *gimage;
PixelRegion srcPR, maskPR;
TileManager *alt = NULL;
int offx, offy;
if (!drawable_has_alpha (drawable))
{
paint_core_paste (paint_core, brush_mask, drawable,
brush_opacity, image_opacity, NORMAL_MODE,
mode);
return;
}
if (! (gimage = drawable_gimage (drawable)))
return;
/* set undo blocks */
set_undo_tiles (drawable,
canvas_buf->x, canvas_buf->y,
canvas_buf->width, canvas_buf->height);
if (mode == CONSTANT)
{
/* initialize any invalid canvas tiles */
set_canvas_tiles (canvas_buf->x, canvas_buf->y,
canvas_buf->width, canvas_buf->height);
/* combine the brush mask and the canvas tiles */
brush_to_canvas_tiles (paint_core, brush_mask, brush_opacity);
/* set the alt source as the unaltered undo_tiles */
alt = undo_tiles;
/* initialize the maskPR from the canvas tiles */
pixel_region_init (&maskPR, canvas_tiles,
canvas_buf->x, canvas_buf->y,
canvas_buf->width, canvas_buf->height, FALSE);
}
else
{
/* The mask is just the brush mask */
maskPR.bytes = 1;
maskPR.x = 0; maskPR.y = 0;
maskPR.w = canvas_buf->width;
maskPR.h = canvas_buf->height;
maskPR.rowstride = maskPR.bytes * brush_mask->width;
maskPR.data = mask_buf_data (brush_mask);
}
/* intialize canvas buf source pixel regions */
srcPR.bytes = canvas_buf->bytes;
srcPR.x = 0; srcPR.y = 0;
srcPR.w = canvas_buf->width;
srcPR.h = canvas_buf->height;
srcPR.rowstride = canvas_buf->width * canvas_buf->bytes;
srcPR.data = temp_buf_data (canvas_buf);
/* apply the paint area to the gimage */
gimage_replace_image (gimage, drawable, &srcPR,
FALSE, image_opacity,
&maskPR,
canvas_buf->x, canvas_buf->y);
/* Update the undo extents */
paint_core->x1 = MINIMUM (paint_core->x1, canvas_buf->x);
paint_core->y1 = MINIMUM (paint_core->y1, canvas_buf->y);
paint_core->x2 = MAXIMUM (paint_core->x2, (canvas_buf->x + canvas_buf->width));
paint_core->y2 = MAXIMUM (paint_core->y2, (canvas_buf->y + canvas_buf->height));
/* Update the gimage--it is important to call gdisplays_update_area
1997-11-25 06:05:25 +08:00
* instead of drawable_update because we don't want the drawable
* preview to be constantly invalidated
*/
drawable_offsets (drawable, &offx, &offy);
gdisplays_update_area (gimage, canvas_buf->x + offx, canvas_buf->y + offy,
1997-11-25 06:05:25 +08:00
canvas_buf->width, canvas_buf->height);
}
static void
canvas_tiles_to_canvas_buf(PaintCore *paint_core)
{
PixelRegion srcPR, maskPR;
/* combine the canvas tiles and the canvas buf */
srcPR.bytes = canvas_buf->bytes;
srcPR.x = 0; srcPR.y = 0;
srcPR.w = canvas_buf->width;
srcPR.h = canvas_buf->height;
srcPR.rowstride = canvas_buf->width * canvas_buf->bytes;
srcPR.data = temp_buf_data (canvas_buf);
pixel_region_init (&maskPR, canvas_tiles,
canvas_buf->x, canvas_buf->y,
canvas_buf->width, canvas_buf->height, FALSE);
/* apply the canvas tiles to the canvas buf */
apply_mask_to_region (&srcPR, &maskPR, OPAQUE_OPACITY);
}
static void
brush_to_canvas_tiles (PaintCore *paint_core,
MaskBuf *brush_mask,
int brush_opacity)
{
PixelRegion srcPR, maskPR;
int x, y;
int xoff, yoff;
/* combine the brush mask and the canvas tiles */
pixel_region_init (&srcPR, canvas_tiles,
canvas_buf->x, canvas_buf->y,
canvas_buf->width, canvas_buf->height, TRUE);
x = (int) paint_core->curx - (brush_mask->width >> 1);
y = (int) paint_core->cury - (brush_mask->height >> 1);
xoff = (x < 0) ? -x : 0;
yoff = (y < 0) ? -y : 0;
maskPR.bytes = 1;
maskPR.x = 0; maskPR.y = 0;
maskPR.w = srcPR.w;
maskPR.h = srcPR.h;
maskPR.rowstride = maskPR.bytes * brush_mask->width;
maskPR.data = mask_buf_data (brush_mask) + yoff * maskPR.rowstride + xoff * maskPR.bytes;
/* combine the mask to the canvas tiles */
combine_mask_and_region (&srcPR, &maskPR, brush_opacity);
}
static void
brush_to_canvas_buf (PaintCore *paint_core,
MaskBuf *brush_mask,
int brush_opacity)
{
PixelRegion srcPR, maskPR;
int x, y;
int xoff, yoff;
x = (int) paint_core->curx - (brush_mask->width >> 1);
y = (int) paint_core->cury - (brush_mask->height >> 1);
xoff = (x < 0) ? -x : 0;
yoff = (y < 0) ? -y : 0;
/* combine the canvas buf and the brush mask to the canvas buf */
srcPR.bytes = canvas_buf->bytes;
srcPR.x = 0; srcPR.y = 0;
srcPR.w = canvas_buf->width;
srcPR.h = canvas_buf->height;
srcPR.rowstride = canvas_buf->width * canvas_buf->bytes;
srcPR.data = temp_buf_data (canvas_buf);
maskPR.bytes = 1;
maskPR.x = 0; maskPR.y = 0;
maskPR.w = srcPR.w;
maskPR.h = srcPR.h;
maskPR.rowstride = maskPR.bytes * brush_mask->width;
maskPR.data = mask_buf_data (brush_mask) + yoff * maskPR.rowstride + xoff * maskPR.bytes;
/* apply the mask */
apply_mask_to_region (&srcPR, &maskPR, brush_opacity);
}
#if 0
1997-11-25 06:05:25 +08:00
static void
paint_to_canvas_tiles (PaintCore *paint_core,
MaskBuf *brush_mask,
int brush_opacity)
1997-11-25 06:05:25 +08:00
{
PixelRegion srcPR, maskPR;
int x, y;
int xoff, yoff;
/* combine the brush mask and the canvas tiles */
pixel_region_init (&srcPR, canvas_tiles,
canvas_buf->x, canvas_buf->y,
canvas_buf->width, canvas_buf->height, TRUE);
x = (int) paint_core->curx - (brush_mask->width >> 1);
y = (int) paint_core->cury - (brush_mask->height >> 1);
xoff = (x < 0) ? -x : 0;
yoff = (y < 0) ? -y : 0;
maskPR.bytes = 1;
maskPR.x = 0; maskPR.y = 0;
maskPR.w = srcPR.w;
maskPR.h = srcPR.h;
maskPR.rowstride = maskPR.bytes * brush_mask->width;
maskPR.data = mask_buf_data (brush_mask) + yoff * maskPR.rowstride + xoff * maskPR.bytes;
/* combine the mask and canvas tiles */
combine_mask_and_region (&srcPR, &maskPR, brush_opacity);
/* combine the canvas tiles and the canvas buf */
srcPR.bytes = canvas_buf->bytes;
srcPR.x = 0; srcPR.y = 0;
srcPR.w = canvas_buf->width;
srcPR.h = canvas_buf->height;
srcPR.rowstride = canvas_buf->width * canvas_buf->bytes;
srcPR.data = temp_buf_data (canvas_buf);
pixel_region_init (&maskPR, canvas_tiles,
canvas_buf->x, canvas_buf->y,
canvas_buf->width, canvas_buf->height, FALSE);
/* apply the canvas tiles to the canvas buf */
apply_mask_to_region (&srcPR, &maskPR, OPAQUE_OPACITY);
1997-11-25 06:05:25 +08:00
}
static void
paint_to_canvas_buf (PaintCore *paint_core,
MaskBuf *brush_mask,
int brush_opacity)
1997-11-25 06:05:25 +08:00
{
PixelRegion srcPR, maskPR;
int x, y;
int xoff, yoff;
x = (int) paint_core->curx - (brush_mask->width >> 1);
y = (int) paint_core->cury - (brush_mask->height >> 1);
xoff = (x < 0) ? -x : 0;
yoff = (y < 0) ? -y : 0;
1997-11-25 06:05:25 +08:00
/* combine the canvas buf and the brush mask to the canvas buf */
srcPR.bytes = canvas_buf->bytes;
srcPR.x = 0; srcPR.y = 0;
srcPR.w = canvas_buf->width;
srcPR.h = canvas_buf->height;
srcPR.rowstride = canvas_buf->width * canvas_buf->bytes;
srcPR.data = temp_buf_data (canvas_buf);
maskPR.bytes = 1;
maskPR.x = 0; maskPR.y = 0;
maskPR.w = srcPR.w;
maskPR.h = srcPR.h;
maskPR.rowstride = maskPR.bytes * brush_mask->width;
maskPR.data = mask_buf_data (brush_mask) + yoff * maskPR.rowstride + xoff * maskPR.bytes;
1997-11-25 06:05:25 +08:00
/* apply the mask */
apply_mask_to_region (&srcPR, &maskPR, brush_opacity);
}
#endif
1997-11-25 06:05:25 +08:00
static void
set_undo_tiles (GimpDrawable *drawable,
int x, int y, int w, int h)
1997-11-25 06:05:25 +08:00
{
int i, j;
Tile *src_tile;
Tile *dest_tile;
if (undo_tiles == NULL)
{
1999-11-23 06:38:02 +08:00
g_warning ("set_undo_tiles: undo_tiles is null");
return;
}
1997-11-25 06:05:25 +08:00
for (i = y; i < (y + h); i += (TILE_HEIGHT - (i % TILE_HEIGHT)))
{
for (j = x; j < (x + w); j += (TILE_WIDTH - (j % TILE_WIDTH)))
{
dest_tile = tile_manager_get_tile (undo_tiles, j, i, FALSE, FALSE);
1998-08-12 01:35:34 +08:00
if (tile_is_valid (dest_tile) == FALSE)
1997-11-25 06:05:25 +08:00
{
src_tile = tile_manager_get_tile (drawable_data (drawable), j, i, TRUE, FALSE);
tile_manager_map_tile (undo_tiles, j, i, src_tile);
tile_release (src_tile, FALSE);
1997-11-25 06:05:25 +08:00
}
}
}
}
static void
set_canvas_tiles (int x, int y, int w, int h)
1997-11-25 06:05:25 +08:00
{
int i, j;
Tile *tile;
for (i = y; i < (y + h); i += (TILE_HEIGHT - (i % TILE_HEIGHT)))
{
for (j = x; j < (x + w); j += (TILE_WIDTH - (j % TILE_WIDTH)))
{
tile = tile_manager_get_tile (canvas_tiles, j, i, FALSE, FALSE);
1998-08-12 01:35:34 +08:00
if (tile_is_valid (tile) == FALSE)
1997-11-25 06:05:25 +08:00
{
tile = tile_manager_get_tile (canvas_tiles, j, i, TRUE, TRUE);
1998-08-12 01:35:34 +08:00
memset (tile_data_pointer (tile, 0, 0), 0,
tile_size (tile));
tile_release (tile, TRUE);
1997-11-25 06:05:25 +08:00
}
}
}
}
/*****************************************************/
/* Paint buffers utility functions */
/*****************************************************/
static void
free_paint_buffers ()
{
if (orig_buf)
temp_buf_free (orig_buf);
orig_buf = NULL;
if (canvas_buf)
temp_buf_free (canvas_buf);
canvas_buf = NULL;
}
/**************************************************/
/* Brush pipe utility functions */
/**************************************************/
void
paint_core_color_area_with_pixmap (PaintCore *paint_core,
GImage *dest,
GimpDrawable *drawable,
TempBuf *area,
gdouble scale,
int mode)
{
PixelRegion destPR;
void *pr;
guchar *d;
int ulx, uly, offsetx, offsety, y;
TempBuf *pixmap_mask;
TempBuf *brush_mask;
g_return_if_fail (GIMP_IS_BRUSH_PIXMAP (paint_core->brush));
/* scale the brushes */
pixmap_mask =
paint_core_scale_pixmap (gimp_brush_pixmap_pixmap (GIMP_BRUSH_PIXMAP (paint_core->brush)), scale);
if (mode == SOFT)
brush_mask = paint_core_scale_mask (paint_core->brush->mask, scale);
else
brush_mask = NULL;
destPR.bytes = area->bytes;
destPR.x = 0; destPR.y = 0;
destPR.w = area->width;
destPR.h = area->height;
destPR.rowstride = destPR.bytes * area->width;
destPR.data = temp_buf_data (area);
pr = pixel_regions_register (1, &destPR);
/* Calculate upper left corner of brush as in
* paint_core_get_paint_area. Ugly to have to do this here, too.
*/
ulx = (int) paint_core->curx - (pixmap_mask->width >> 1);
uly = (int) paint_core->cury - (pixmap_mask->height >> 1);
offsetx = area->x - ulx;
offsety = area->y - uly;
for (; pr != NULL; pr = pixel_regions_process (pr))
{
d = destPR.data;
for (y = 0; y < destPR.h; y++)
{
paint_line_pixmap_mask (dest, drawable, pixmap_mask, brush_mask,
d, offsetx, y + offsety,
destPR.bytes, destPR.w, mode);
d += destPR.rowstride;
}
}
}
static void
paint_line_pixmap_mask (GImage *dest,
GimpDrawable *drawable,
TempBuf *pixmap_mask,
TempBuf *brush_mask,
guchar *d,
int x,
int y,
int bytes,
int width,
int mode)
{
guchar *b, *p;
int x_index;
gdouble alpha;
gdouble factor = 0.00392156986; /* 1.0/255.0 */
gint i;
guchar *mask;
/* Make sure x, y are positive */
while (x < 0)
x += pixmap_mask->width;
while (y < 0)
y += pixmap_mask->height;
/* Point to the approriate scanline */
b = temp_buf_data (pixmap_mask) +
(y % pixmap_mask->height) * pixmap_mask->width * pixmap_mask->bytes;
if (mode == SOFT && brush_mask)
{
/* ditto, except for the brush mask, so we can pre-multiply the alpha value */
mask = temp_buf_data (brush_mask) +
(y % brush_mask->height) * brush_mask->width;
for (i = 0; i < width; i++)
{
/* attempt to avoid doing this calc twice in the loop */
x_index = ((i + x) % pixmap_mask->width);
p = b + x_index * pixmap_mask->bytes;
d[bytes-1] = mask[x_index];
/* multiply alpha into the pixmap data */
/* maybe we could do this at tool creation or brush switch time? */
/* and compute it for the whole brush at once and cache it? */
alpha = d[bytes-1] * factor;
if (alpha)
{
d[0] *= alpha;
d[1] *= alpha;
d[2] *= alpha;
}
/* printf("i: %i d->r: %i d->g: %i d->b: %i d->a: %i\n",i,(int)d[0], (int)d[1], (int)d[2], (int)d[3]); */
gimage_transform_color (dest, drawable, p, d, RGB);
d += bytes;
}
}
else
{
for (i = 0; i < width; i++)
{
/* attempt to avoid doing this calc twice in the loop */
x_index = ((i + x) % pixmap_mask->width);
p = b + x_index * pixmap_mask->bytes;
d[bytes-1] = 255;
/* multiply alpha into the pixmap data */
/* maybe we could do this at tool creation or brush switch time? */
/* and compute it for the whole brush at once and cache it? */
gimage_transform_color (dest, drawable, p, d, RGB);
d += bytes;
}
}
}