gimpink.c 43.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/* 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.
 */
Sven Neumann's avatar
Sven Neumann committed
18

19 20
#include "config.h"

21
#include <stdlib.h>
Tor Lillqvist's avatar
Tor Lillqvist committed
22
#include <string.h>
23

24
#include <gtk/gtk.h>
Sven Neumann's avatar
Sven Neumann committed
25

26
#include "libgimpmath/gimpmath.h"
27
#include "libgimpwidgets/gimpwidgets.h"
28

Michael Natterer's avatar
Michael Natterer committed
29
#include "tools-types.h"
Sven Neumann's avatar
Sven Neumann committed
30

31 32
#include "paint-funcs/paint-funcs.h"

33 34 35 36 37
#include "core/gimpcontext.h"
#include "core/gimpdrawable.h"
#include "core/gimpimage.h"
#include "core/gimpimage-mask.h"

38
#include "drawable.h"
39
#include "gimprc.h"
40
#include "undo.h"
41
#include "gdisplay.h"
42 43 44 45
#include "pixel_region.h"
#include "temp_buf.h"
#include "tile.h"
#include "tile_manager.h"
46

47 48 49
#include "gimpinktool.h"
#include "gimpinktool-blob.h"
#include "gimptool.h"
50
#include "paint_options.h"
51
#include "tool_manager.h"
52

53 54
#include "libgimp/gimpintl.h"

55 56
#define WANT_INK_BITS
#include "icons.h"
57

58

59
#define SUBSAMPLE 8
60

61 62
/*  the Ink structures  */

63 64 65 66 67 68 69
typedef Blob * (* BlobFunc) (gdouble,
			     gdouble,
			     gdouble,
			     gdouble,
			     gdouble,
			     gdouble);

70

71
typedef struct _BrushWidget BrushWidget;
Michael Natterer's avatar
Michael Natterer committed
72

73 74
struct _BrushWidget
{
75 76
  GtkWidget *widget;
  gboolean   state;
77 78
};

79
typedef struct _InkOptions InkOptions;
Michael Natterer's avatar
Michael Natterer committed
80

81 82
struct _InkOptions
{
83
  PaintOptions  paint_options;
84

Michael Natterer's avatar
Michael Natterer committed
85 86
  gdouble       size;
  gdouble       size_d;
87
  GtkObject    *size_w;
88

Michael Natterer's avatar
Michael Natterer committed
89 90
  gdouble       sensitivity;
  gdouble       sensitivity_d;
91
  GtkObject    *sensitivity_w;
92

Michael Natterer's avatar
Michael Natterer committed
93 94
  gdouble       vel_sensitivity;
  gdouble       vel_sensitivity_d;
95
  GtkObject    *vel_sensitivity_w;
96

Michael Natterer's avatar
Michael Natterer committed
97 98
  gdouble       tilt_sensitivity;
  gdouble       tilt_sensitivity_d;
99
  GtkObject    *tilt_sensitivity_w;
100

Michael Natterer's avatar
Michael Natterer committed
101 102
  gdouble       tilt_angle;
  gdouble       tilt_angle_d;
103
  GtkObject    *tilt_angle_w;
104

105 106 107
  BlobFunc      function;
  BlobFunc      function_d;
  GtkWidget    *function_w[3];  /* 3 radio buttons */
108

Michael Natterer's avatar
Michael Natterer committed
109 110 111 112
  gdouble       aspect;
  gdouble       aspect_d;
  gdouble       angle;
  gdouble       angle_d;
113
  BrushWidget  *brush_w;
114 115 116
};


117
/*  local function prototypes  */
118

119 120 121 122 123 124
static void        gimp_ink_tool_class_init      (GimpInkToolClass *klass);
static void        gimp_ink_tool_init            (GimpInkTool      *tool);

static void        gimp_ink_tool_destroy         (GtkObject        *object);

static InkOptions * ink_options_new     (void);
125
static void         ink_options_reset   (ToolOptions    *tool_options);
126 127

static void        ink_button_press     (GimpTool       *tool,
128 129
					 GdkEventButton *mevent,
					 GDisplay       *gdisp);
130
static void        ink_button_release   (GimpTool       *tool,
131 132
					 GdkEventButton *bevent,
					 GDisplay       *gdisp);
133
static void        ink_motion           (GimpTool       *tool,
134 135
					 GdkEventMotion *mevent,
					 GDisplay       *gdisp);
136
static void        ink_cursor_update    (GimpTool       *tool,
137 138
					 GdkEventMotion *mevent,
					 GDisplay       *gdisp);
139
static void        ink_control          (GimpTool       *tool,
140 141 142
					 ToolAction      tool_action,
					 GDisplay       *gdisp);

143
static void        time_smoother_add    (GimpInkTool    *ink_tool,
144
					 guint32         value);
145 146
static gdouble     time_smoother_result (GimpInkTool    *ink_tool);
static void        time_smoother_init   (GimpInkTool    *ink_tool,
147
					 guint32         initval);
148
static void        dist_smoother_add    (GimpInkTool    *ink_tool,
149
					 gdouble         value);
150 151
static gdouble     dist_smoother_result (GimpInkTool    *ink_tool);
static void        dist_smoother_init   (GimpInkTool    *ink_tool,
152 153
					 gdouble         initval);

154
static void        ink_init             (GimpInkTool    *ink_tool, 
155 156 157
					 GimpDrawable   *drawable, 
					 gdouble         x, 
					 gdouble         y);
158 159
static void        ink_finish           (GimpInkTool    *ink_tool, 
					 GimpDrawable   *drawable);
160 161 162 163 164 165 166 167 168 169
static void        ink_cleanup          (void);

static void        ink_type_update      (GtkWidget      *radio_button,
					 BlobFunc        function);
static GdkPixmap * blob_pixmap          (GdkColormap    *colormap,
					 GdkVisual      *visual,
					 BlobFunc        function);
static void        paint_blob           (GdkDrawable    *drawable, 
					 GdkGC          *gc,
					 Blob           *blob);
170

Michael Natterer's avatar
Michael Natterer committed
171
/*  Rendering functions  */
172
static void        ink_set_paint_area   (GimpInkTool    *ink_tool, 
173 174
					 GimpDrawable   *drawable, 
					 Blob           *blob);
175
static void        ink_paste            (GimpInkTool    *ink_tool, 
176 177 178
					 GimpDrawable   *drawable,
					 Blob           *blob);

179
static void        ink_to_canvas_tiles  (GimpInkTool    *ink_tool,
180 181 182 183 184 185 186 187 188 189 190 191
					 Blob           *blob,
					 guchar         *color);

static void        ink_set_undo_tiles   (GimpDrawable   *drawable,
					 gint            x, 
					 gint            y,
					 gint            w, 
					 gint            h);
static void        ink_set_canvas_tiles (gint            x, 
					 gint            y,
					 gint            w, 
					 gint            h);
192

Michael Natterer's avatar
Michael Natterer committed
193
/*  Brush pseudo-widget callbacks  */
194 195
static void   brush_widget_active_rect    (BrushWidget    *brush_widget,
					   GtkWidget      *widget,
196
					   GdkRectangle   *rect);
197 198
static void   brush_widget_realize        (GtkWidget      *widget);
static void   brush_widget_expose         (GtkWidget      *widget,
199 200
					   GdkEventExpose *event,
					   BrushWidget    *brush_widget);
201
static void   brush_widget_button_press   (GtkWidget      *widget,
202 203
					   GdkEventButton *event,
					   BrushWidget    *brush_widget);
204
static void   brush_widget_button_release (GtkWidget      *widget,
205 206
					   GdkEventButton *event,
					   BrushWidget    *brush_widget);
207
static void   brush_widget_motion_notify  (GtkWidget      *widget,
208 209 210 211
					   GdkEventMotion *event,
					   BrushWidget    *brush_widget);


212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
/* local variables */

/* the ink tool options  */
static InkOptions *ink_options = NULL;

/*  undo blocks variables  */
static TileManager *undo_tiles = NULL;

/* Tiles used to render the stroke at 1 byte/pp */
static TileManager *canvas_tiles = NULL;

/* Flat buffer that is used to used to render the dirty region
 * for composition onto the destination drawable
 */
static TempBuf *canvas_buf = NULL;

228 229
static GimpToolClass *parent_class      = NULL;

230

231 232
/*  functions  */

233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
void
gimp_ink_tool_register (void)
{
  tool_manager_register_tool (GIMP_TYPE_INK_TOOL,
			      TRUE,
			      "gimp:ink_tool",
			      _("Ink Tool"),
			      _("Draw in ink"),
			      N_("/Tools/Paint Tools/Ink"), "K",
			      NULL, "tools/ink.html",
			      (const gchar **) ink_bits);
}

GtkType
gimp_ink_tool_get_type (void)
{
  static GtkType tool_type = 0;

  if (! tool_type)
    {
      GtkTypeInfo tool_info =
      {
	"GimpInkTool",
	sizeof (GimpInkTool),
	sizeof (GimpInkToolClass),
	(GtkClassInitFunc) gimp_ink_tool_class_init,
	(GtkObjectInitFunc) gimp_ink_tool_init,
	/* reserved_1 */ NULL,
	/* reserved_2 */ NULL,
	(GtkClassInitFunc) NULL,
      };

      tool_type = gtk_type_unique (GIMP_TYPE_TOOL, &tool_info);
    }

  return tool_type;
}

static void
gimp_ink_tool_class_init (GimpInkToolClass *klass)
{
  GtkObjectClass *object_class;
  GimpToolClass  *tool_class;

  object_class = (GtkObjectClass *) klass;
  tool_class   = (GimpToolClass *) klass;

  parent_class = gtk_type_class (GIMP_TYPE_TOOL);

  object_class->destroy = gimp_ink_tool_destroy;

  tool_class->control        = ink_control;
  tool_class->button_press   = ink_button_press;
  tool_class->button_release = ink_button_release;
  tool_class->motion         = ink_motion;
  tool_class->cursor_update  = ink_cursor_update;
}

static void
gimp_ink_tool_init (GimpInkTool *ink_tool)
{
  GimpTool *tool;

  tool = GIMP_TOOL (ink_tool);
 
  /*  The tool options  */
  if (! ink_options)
    {
      ink_options = ink_options_new ();

      tool_manager_register_tool_options (GIMP_TYPE_INK_TOOL,
					  (ToolOptions *) ink_options);

306
      ink_options_reset ((ToolOptions *) ink_options);
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
    }
}

static void
gimp_ink_tool_destroy (GtkObject *object)
{
  GimpInkTool *ink_tool;

  ink_tool = GIMP_INK_TOOL (object);

  if (ink_tool->last_blob)
    g_free (ink_tool->last_blob);

  ink_cleanup ();

  if (GTK_OBJECT_CLASS (parent_class)->destroy)
    GTK_OBJECT_CLASS (parent_class)->destroy (object);
}

326 327 328 329 330 331 332 333 334 335 336 337
static void 
ink_type_update (GtkWidget      *radio_button,
		 BlobFunc        function)
{
  InkOptions *options = ink_options;

  if (GTK_TOGGLE_BUTTON (radio_button)->active)
    options->function = function;

  gtk_widget_queue_draw (options->brush_w->widget);
}

338
static void
339
ink_options_reset (ToolOptions *tool_options)
340
{
341
  InkOptions *options;
342

343 344 345
  options = (InkOptions *) tool_options;

  paint_options_reset (tool_options);
346

347 348 349 350 351 352
  gtk_adjustment_set_value (GTK_ADJUSTMENT (options->size_w),
			    options->size_d);
  gtk_adjustment_set_value (GTK_ADJUSTMENT (options->sensitivity_w),
			    options->sensitivity_d);
  gtk_adjustment_set_value (GTK_ADJUSTMENT (options->tilt_sensitivity_w),
			    options->tilt_sensitivity_d);
353 354
  gtk_adjustment_set_value (GTK_ADJUSTMENT (options->vel_sensitivity_w),
			    options->vel_sensitivity_d);
355 356
  gtk_adjustment_set_value (GTK_ADJUSTMENT (options->tilt_angle_w),
			    options->tilt_angle_d);
357 358 359 360 361 362
  gtk_toggle_button_set_active (((options->function_d == blob_ellipse) ?
				 GTK_TOGGLE_BUTTON (options->function_w[0]) :
				 ((options->function_d == blob_square) ?
				  GTK_TOGGLE_BUTTON (options->function_w[1]) :
				  GTK_TOGGLE_BUTTON (options->function_w[2]))),
				TRUE);
363 364
  options->aspect = options->aspect_d;
  options->angle  = options->angle_d;
365
  gtk_widget_queue_draw (options->brush_w->widget);
366 367
}

368
static InkOptions *
369
ink_options_new (void)
370
{
371
  InkOptions *options;
372 373 374 375 376 377 378 379 380 381
  GtkWidget  *table;
  GtkWidget  *vbox;
  GtkWidget  *hbox;
  GtkWidget  *hbox2;
  GtkWidget  *radio_button;
  GtkWidget  *pixmap_widget;
  GtkWidget  *slider;
  GtkWidget  *frame;
  GtkWidget  *darea;
  GdkPixmap  *pixmap;
382

383
  options = g_new0 (InkOptions, 1);
384
  paint_options_init ((PaintOptions *) options,
385
		      GIMP_TYPE_INK_TOOL,
386
		      ink_options_reset);
387

388
  options->size             = options->size_d             = 4.4;
389
  options->sensitivity      = options->sensitivity_d      = 1.0;
390 391
  options->vel_sensitivity  = options->vel_sensitivity_d  = 0.8;
  options->tilt_sensitivity = options->tilt_sensitivity_d = 0.4;
392 393 394 395 396
  options->tilt_angle       = options->tilt_angle_d       = 0.0;
  options->function         = options->function_d         = blob_ellipse;
  options->aspect           = options->aspect_d           = 1.0;
  options->angle            = options->angle_d            = 0.0;

jtl's avatar
jtl committed
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
  /*  the main vbox  */
  vbox = gtk_vbox_new (FALSE, 2);
  gtk_box_pack_start (GTK_BOX (((ToolOptions *) options)->main_vbox), vbox,
		      TRUE, TRUE, 0);
  gtk_widget_show (vbox);

  /* adjust sliders */
  frame = gtk_frame_new (_("Adjustment"));
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, TRUE, 0);
  gtk_widget_show (frame);

  table = gtk_table_new (2, 2, FALSE);
  gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  gtk_container_set_border_width (GTK_CONTAINER (table), 2);
  gtk_container_add (GTK_CONTAINER (frame), table);
  gtk_widget_show (table);
414 415 416

  /*  size slider  */
  options->size_w =
417
    gtk_adjustment_new (options->size_d, 0.0, 20.0, 1.0, 2.0, 0.0);
418
  slider = gtk_hscale_new (GTK_ADJUSTMENT (options->size_w));
419
  gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP);
jtl's avatar
jtl committed
420 421 422
  gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
			     _("Size:"), 1.0, 1.0,
			     slider, 1, FALSE);
423
  gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED);
424
  gtk_signal_connect (GTK_OBJECT (options->size_w), "value_changed",
Michael Natterer's avatar
Michael Natterer committed
425
		      GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
426
		      &options->size);
jtl's avatar
jtl committed
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453

  /* angle adjust slider */
  options->tilt_angle_w =
    gtk_adjustment_new (options->tilt_angle_d, -90.0, 90.0, 1, 10.0, 0.0);
  slider = gtk_hscale_new (GTK_ADJUSTMENT (options->tilt_angle_w));
  gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP);
  gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
			     _("Angle:"), 1.0, 1.0,
			     slider, 1, FALSE);
  gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED);
  gtk_signal_connect (GTK_OBJECT (options->tilt_angle_w), "value_changed",
		      GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
		      &options->tilt_angle);

  /* sens sliders */
  frame = gtk_frame_new (_("Sensitivity"));
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, TRUE, 0);
  gtk_widget_show (frame);

  table = gtk_table_new (3, 2, FALSE);
  gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  gtk_container_set_border_width (GTK_CONTAINER (table), 2);
  gtk_container_add (GTK_CONTAINER (frame), table);
  gtk_widget_show (table);

  /* size sens slider */
454 455 456
  options->sensitivity_w =
    gtk_adjustment_new (options->sensitivity_d, 0.0, 1.0, 0.01, 0.1, 0.0);
  slider = gtk_hscale_new (GTK_ADJUSTMENT (options->sensitivity_w));
457
  gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP);
jtl's avatar
jtl committed
458 459 460
  gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
			     _("Size:"), 1.0, 1.0,
			     slider, 1, FALSE);
461
  gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED);
462
  gtk_signal_connect (GTK_OBJECT (options->sensitivity_w), "value_changed",
Michael Natterer's avatar
Michael Natterer committed
463
		      GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
464
		      &options->sensitivity);
465
  
466 467 468 469
  /* tilt sens slider */
  options->tilt_sensitivity_w =
    gtk_adjustment_new (options->tilt_sensitivity_d, 0.0, 1.0, 0.01, 0.1, 0.0);
  slider = gtk_hscale_new (GTK_ADJUSTMENT (options->tilt_sensitivity_w));
470
  gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP);
jtl's avatar
jtl committed
471 472 473
  gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
			     _("Tilt:"), 1.0, 1.0,
			     slider, 1, FALSE);
474
  gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED);
475
  gtk_signal_connect (GTK_OBJECT (options->tilt_sensitivity_w), "value_changed",
Michael Natterer's avatar
Michael Natterer committed
476
		      GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
477
		      &options->tilt_sensitivity);
478 479 480 481 482 483

  /* velocity sens slider */
  options->vel_sensitivity_w =
    gtk_adjustment_new (options->vel_sensitivity_d, 0.0, 1.0, 0.01, 0.1, 0.0);
  slider = gtk_hscale_new (GTK_ADJUSTMENT (options->vel_sensitivity_w));
  gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP);
jtl's avatar
jtl committed
484 485 486
  gimp_table_attach_aligned (GTK_TABLE (table), 0, 3,
			     _("Speed:"), 1.0, 1.0,
			     slider, 1, FALSE);
487 488
  gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED);
  gtk_signal_connect (GTK_OBJECT (options->vel_sensitivity_w), "value_changed",
Michael Natterer's avatar
Michael Natterer committed
489
		      GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
490 491
		      &options->vel_sensitivity);

jtl's avatar
jtl committed
492 493 494 495
  /*  bottom hbox */
  hbox = gtk_hbox_new (FALSE, 2);
  gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
  gtk_widget_show (hbox);
Raph Levien's avatar
Raph Levien committed
496

497
  /* Brush type radiobuttons */
498
  frame = gtk_frame_new (_("Type"));
jtl's avatar
jtl committed
499
  gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
500 501
  gtk_widget_show (frame);

jtl's avatar
jtl committed
502 503 504
  hbox2 = gtk_hbox_new (FALSE, 0);
  gtk_container_add (GTK_CONTAINER (frame), hbox2);
  
505 506
  vbox = gtk_vbox_new (FALSE, 2);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 2);
jtl's avatar
jtl committed
507
  gtk_box_pack_start (GTK_BOX (hbox2), vbox, FALSE, FALSE, 0);
508 509 510 511
  gtk_widget_show (vbox);

  pixmap = blob_pixmap (gtk_widget_get_colormap (vbox),
			gtk_widget_get_visual (vbox),
512 513 514 515
			blob_ellipse);

  pixmap_widget = gtk_pixmap_new (pixmap, NULL);
  gdk_pixmap_unref (pixmap);
jtl's avatar
jtl committed
516
  gtk_misc_set_padding (GTK_MISC (pixmap_widget), 6, 0);
517 518 519 520 521 522 523

  radio_button = gtk_radio_button_new (NULL);
  gtk_signal_connect (GTK_OBJECT (radio_button), "toggled",
		      GTK_SIGNAL_FUNC (ink_type_update), 
		      (gpointer)blob_ellipse);

  gtk_container_add (GTK_CONTAINER (radio_button), pixmap_widget);
524
  gtk_box_pack_start (GTK_BOX (vbox), radio_button, FALSE, FALSE, 0);
525

526
  options->function_w[0] = radio_button;
527 528 529

  pixmap = blob_pixmap (gtk_widget_get_colormap (vbox),
			gtk_widget_get_visual (vbox),
530 531 532 533
			blob_square);

  pixmap_widget = gtk_pixmap_new (pixmap, NULL);
  gdk_pixmap_unref (pixmap);
jtl's avatar
jtl committed
534
  gtk_misc_set_padding (GTK_MISC (pixmap_widget), 6, 0);
535

536 537
  radio_button =
    gtk_radio_button_new_from_widget (GTK_RADIO_BUTTON (radio_button));
538 539 540 541 542
  gtk_signal_connect (GTK_OBJECT (radio_button), "toggled",
		      GTK_SIGNAL_FUNC (ink_type_update), 
		      (gpointer)blob_square);
  
  gtk_container_add (GTK_CONTAINER (radio_button), pixmap_widget);
543
  gtk_box_pack_start (GTK_BOX (vbox), radio_button, FALSE, FALSE, 0);
544

545 546
  options->function_w[1] = radio_button;

547 548
  pixmap = blob_pixmap (gtk_widget_get_colormap (vbox),
			gtk_widget_get_visual (vbox),
549 550 551 552
			blob_diamond);

  pixmap_widget = gtk_pixmap_new (pixmap, NULL);
  gdk_pixmap_unref (pixmap);
jtl's avatar
jtl committed
553
  gtk_misc_set_padding (GTK_MISC (pixmap_widget), 6, 0);
554

555 556
  radio_button =
    gtk_radio_button_new_from_widget (GTK_RADIO_BUTTON (radio_button));
557 558 559 560 561
  gtk_signal_connect (GTK_OBJECT (radio_button), "toggled",
		      GTK_SIGNAL_FUNC (ink_type_update), 
		      (gpointer)blob_diamond);

  gtk_container_add (GTK_CONTAINER (radio_button), pixmap_widget);
562
  gtk_box_pack_start (GTK_BOX (vbox), radio_button, FALSE, FALSE, 0);
563

564 565
  options->function_w[2] = radio_button;

566
  /* Brush shape widget */
567
  frame = gtk_frame_new (_("Shape"));
jtl's avatar
jtl committed
568
  gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
569 570 571 572 573 574
  gtk_widget_show (frame);

  vbox = gtk_vbox_new (FALSE, 2);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 2);
  gtk_container_add (GTK_CONTAINER (frame), vbox);
  gtk_widget_show (vbox);
575

jtl's avatar
jtl committed
576
  frame = gtk_aspect_frame_new (NULL, 0.0, 0.5, 1.0, FALSE);
577 578
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
  gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
579

jtl's avatar
jtl committed
580 581 582
  options->brush_w = g_new (BrushWidget, 1);
  options->brush_w->state = FALSE;

583
  darea = gtk_drawing_area_new();
584
  options->brush_w->widget = darea;
585

586 587
  gtk_drawing_area_size (GTK_DRAWING_AREA (darea), 60, 60);
  gtk_container_add (GTK_CONTAINER (frame), darea);
588

589 590 591 592 593
  gtk_widget_set_events (darea, 
			 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
			 | GDK_POINTER_MOTION_MASK | GDK_EXPOSURE_MASK);
  gtk_signal_connect (GTK_OBJECT (darea), "button_press_event",
		      GTK_SIGNAL_FUNC (brush_widget_button_press),
594
		      options->brush_w);
595 596
  gtk_signal_connect (GTK_OBJECT (darea), "button_release_event",
		      GTK_SIGNAL_FUNC (brush_widget_button_release),
597
		      options->brush_w);
598 599
  gtk_signal_connect (GTK_OBJECT (darea), "motion_notify_event",
		      GTK_SIGNAL_FUNC (brush_widget_motion_notify),
600
		      options->brush_w);
601 602
  gtk_signal_connect (GTK_OBJECT (darea), "expose_event",
		      GTK_SIGNAL_FUNC (brush_widget_expose), 
603
		      options->brush_w);
604 605
  gtk_signal_connect (GTK_OBJECT (darea), "realize",
		      GTK_SIGNAL_FUNC (brush_widget_realize),
606
		      options->brush_w);
607

jtl's avatar
jtl committed
608
  gtk_widget_show_all (hbox);
609 610 611 612

  return options;
}

613 614 615

/*  the brush widget functions  */

616
static void
617 618
brush_widget_active_rect (BrushWidget  *brush_widget,
			  GtkWidget    *widget,
619 620 621 622 623
			  GdkRectangle *rect)
{
  int x,y;
  int r;

624
  r = MIN (widget->allocation.width, widget->allocation.height) / 2;
625

626
  x = widget->allocation.width / 2 + 0.85 * r * ink_options->aspect / 10.0 *
627
    cos (ink_options->angle);
628
  y = widget->allocation.height / 2 + 0.85 * r * ink_options->aspect / 10.0 *
629 630
    sin (ink_options->angle);

631 632
  rect->x = x - 5;
  rect->y = y - 5;
633 634 635 636 637
  rect->width = 10;
  rect->height = 10;
}

static void
638
brush_widget_realize (GtkWidget *widget)
639
{
640
  gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
641 642 643
}

static void
644 645 646 647 648
brush_widget_draw_brush (BrushWidget *brush_widget,
			 GtkWidget   *widget,
			 gdouble      xc,
			 gdouble      yc,
			 gdouble      radius)
649
{
650 651 652 653 654 655 656 657 658 659 660 661
  Blob *blob;

  blob = ink_options->function (xc, yc,
				radius * cos (ink_options->angle),
				radius * sin (ink_options->angle),
				(- (radius / ink_options->aspect) *
				 sin (ink_options->angle)),
				((radius / ink_options->aspect) *
				 cos (ink_options->angle)));

  paint_blob (widget->window, widget->style->fg_gc[widget->state], blob);
  g_free (blob);
662 663 664
}

static void
665 666 667
brush_widget_expose (GtkWidget      *widget,
		     GdkEventExpose *event,
		     BrushWidget    *brush_widget)
668 669 670
{
  GdkRectangle rect;
  int r0;
671 672

  r0 = MIN (widget->allocation.width, widget->allocation.height) / 2;
673 674 675 676

  if (r0 < 2)
    return;

677 678 679 680 681 682 683
  gdk_window_clear_area (widget->window, 0, 0,
			 widget->allocation.width,
			 widget->allocation.height);
  brush_widget_draw_brush (brush_widget, widget,
			   widget->allocation.width / 2,
			   widget->allocation.height / 2,
			   0.9 * r0);
684

685 686
  brush_widget_active_rect (brush_widget, widget, &rect);
  gdk_draw_rectangle (widget->window, widget->style->bg_gc[GTK_STATE_NORMAL],
687 688 689
		      TRUE,	/* filled */
		      rect.x, rect.y, 
		      rect.width, rect.height);
690
  gtk_draw_shadow (widget->style, widget->window, widget->state, GTK_SHADOW_OUT,
691 692 693 694 695
		   rect.x, rect.y,
		   rect.width, rect.height);
}

static void
696 697 698
brush_widget_button_press (GtkWidget      *widget,
			   GdkEventButton *event,
			   BrushWidget    *brush_widget)
699 700 701
{
  GdkRectangle rect;

702
  brush_widget_active_rect (brush_widget, widget, &rect);
703 704 705 706 707
  
  if ((event->x >= rect.x) && (event->x-rect.x < rect.width) &&
      (event->y >= rect.y) && (event->y-rect.y < rect.height))
    {
      brush_widget->state = TRUE;
708

709
      gtk_grab_add (brush_widget->widget);
710 711 712 713
    }
}

static void
714 715 716
brush_widget_button_release (GtkWidget      *widget,
			     GdkEventButton *event,
			     BrushWidget    *brush_widget)
717 718
{
  brush_widget->state = FALSE;
719

720
  gtk_grab_remove (brush_widget->widget);
721 722 723
}

static void
724 725 726
brush_widget_motion_notify (GtkWidget      *widget,
			    GdkEventMotion *event,
			    BrushWidget    *brush_widget)
727 728 729 730 731 732 733 734
{
  int x;
  int y;
  int r0;
  int rsquare;

  if (brush_widget->state)
    {
735 736
      x = event->x - widget->allocation.width / 2;
      y = event->y - widget->allocation.height / 2;
737 738 739 740
      rsquare = x*x + y*y;

      if (rsquare != 0)
	{
741
	  ink_options->angle = atan2 (y, x);
742

743
	  r0 = MIN (widget->allocation.width, widget->allocation.height) / 2;
744
	  ink_options->aspect =
745
	    10.0 * sqrt ((double) rsquare / (r0 * r0)) / 0.85;
746 747 748 749 750 751

	  if (ink_options->aspect < 1.0)
	    ink_options->aspect = 1.0;
	  if (ink_options->aspect > 10.0)
	    ink_options->aspect = 10.0;

752
	  gtk_widget_draw (widget, NULL);
753 754 755 756
	}
    }
}

757 758 759 760 761 762
/*
 * Return a black-on white pixmap in the given colormap and
 * visual that represents the BlobFunc 'function'
 */
static GdkPixmap *
blob_pixmap (GdkColormap *colormap,
763 764
	     GdkVisual   *visual,
	     BlobFunc     function)
765 766
{
  GdkPixmap *pixmap;
767 768 769
  GdkGC     *black_gc, *white_gc;
  GdkColor   tmp_color;
  Blob      *blob;
770 771 772 773 774 775 776 777 778 779 780 781 782 783 784

  pixmap = gdk_pixmap_new (NULL, 22, 21, visual->depth);

  black_gc = gdk_gc_new (pixmap);
  gdk_color_black (colormap, &tmp_color);
  gdk_gc_set_foreground (black_gc, &tmp_color);

  white_gc = gdk_gc_new (pixmap);
  gdk_color_white (colormap, &tmp_color);
  gdk_gc_set_foreground (white_gc, &tmp_color);

  gdk_draw_rectangle (pixmap, white_gc, TRUE, 0, 0, 21, 20);
  gdk_draw_rectangle (pixmap, black_gc, FALSE, 0, 0, 21, 20);
  blob = (*function) (10, 10, 8, 0, 0, 8);
  paint_blob (pixmap, black_gc, blob);
Sven Neumann's avatar
Sven Neumann committed
785
  g_free (blob);
786 787 788 789 790 791 792 793 794 795 796

  gdk_gc_unref (white_gc);
  gdk_gc_unref (black_gc);

  return pixmap;
}

/*
 * Draw a blob onto a drawable with the specified graphics context
 */
static void
797 798 799
paint_blob (GdkDrawable *drawable,
	    GdkGC       *gc,
	    Blob        *blob)
800 801 802 803 804 805 806 807 808 809 810 811
{
  int i;

  for (i=0;i<blob->height;i++)
    {
      if (blob->data[i].left <= blob->data[i].right)
	gdk_draw_line (drawable, gc,
		       blob->data[i].left,i+blob->y,
		       blob->data[i].right+1,i+blob->y);
    }
}

812

813
static Blob *
814 815 816 817 818
ink_pen_ellipse (gdouble x_center,
		 gdouble y_center,
		 gdouble pressure,
		 gdouble xtilt,
		 gdouble ytilt,
819
		 gdouble velocity)
820 821 822 823 824
{
  double size;
  double tsin, tcos;
  double aspect, radmin;
  double x,y;
825
  double tscale;
Raph Levien's avatar
Raph Levien committed
826 827
  double tscale_c;
  double tscale_s;
828
  
829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856
  /* Adjust the size depending on pressure. */

  size = ink_options->size * (1.0 + ink_options->sensitivity *
			      (2.0 * pressure - 1.0) );

  /* Adjust the size further depending on pointer velocity
     and velocity-sensitivity.  These 'magic constants' are
     'feels natural' tigert-approved. --ADM */

  if (velocity < 3.0)
    velocity = 3.0;

#ifdef VERBOSE
  g_print("%f (%f) -> ", (float)size, (float)velocity);
#endif  

  size = ink_options->vel_sensitivity *
    ((4.5 * size) / (1.0 + ink_options->vel_sensitivity * (2.0*(velocity))))
    + (1.0 - ink_options->vel_sensitivity) * size;

#ifdef VERBOSE
  g_print("%f\n", (float)size);
#endif

  /* Clamp resulting size to sane limits */

  if (size > ink_options->size * (1.0 + ink_options->sensitivity))
    size = ink_options->size * (1.0 + ink_options->sensitivity);
857

858 859 860
  if (size*SUBSAMPLE < 1.0) size = 1.0/SUBSAMPLE;

  /* Add brush angle/aspect to tilt vectorially */
861

Raph Levien's avatar
Raph Levien committed
862 863 864 865
  /* I'm not happy with the way the brush widget info is combined with
     tilt info from the brush. My personal feeling is that representing
     both as affine transforms would make the most sense. -RLL */

866
  tscale = ink_options->tilt_sensitivity * 10.0;
867 868
  tscale_c = tscale * cos (gimp_deg_to_rad (ink_options->tilt_angle));
  tscale_s = tscale * sin (gimp_deg_to_rad (ink_options->tilt_angle));
Raph Levien's avatar
Raph Levien committed
869 870 871 872 873 874 875 876
  x = ink_options->aspect*cos(ink_options->angle) +
    xtilt * tscale_c - ytilt * tscale_s;
  y = ink_options->aspect*sin(ink_options->angle) +
    ytilt * tscale_c + xtilt * tscale_s;
#ifdef VERBOSE
  g_print ("angle %g aspect %g; %g %g; %g %g\n",
	   ink_options->angle, ink_options->aspect, tscale_c, tscale_s, x, y);
#endif
877 878 879 880 881 882 883 884 885 886 887 888
  aspect = sqrt(x*x+y*y);

  if (aspect != 0)
    {
      tcos = x/aspect;
      tsin = y/aspect;
    }
  else
    {
      tsin = sin(ink_options->angle);
      tcos = cos(ink_options->angle);
    }
889

890 891 892 893 894 895 896 897
  if (aspect < 1.0) 
    aspect = 1.0;
  else if (aspect > 10.0) 
    aspect = 10.0;

  radmin = SUBSAMPLE * size/aspect;
  if (radmin < 1.0) radmin = 1.0;
  
898 899 900
  return ink_options->function (x_center * SUBSAMPLE, y_center * SUBSAMPLE,
				radmin*aspect*tcos, radmin*aspect*tsin,  
				-radmin*tsin, radmin*tcos);
901 902 903
}

static void
904
ink_button_press (GimpTool       *tool,
905
		  GdkEventButton *bevent,
906
		  GDisplay       *gdisp)
907
{
908
  GimpInkTool  *ink_tool;
909 910
  GimpDrawable *drawable;
  Blob         *b;
911
  gdouble       x, y;
912

913
  ink_tool = GIMP_INK_TOOL (tool);
914 915 916 917

  /*  Keep the coordinates of the target  */
  gdisplay_untransform_coords_f (gdisp, bevent->x, bevent->y,
				 &x, &y, TRUE);
918
  drawable = gimp_image_active_drawable (gdisp->gimage);
919 920 921

  ink_init (ink_tool, drawable, x, y);

922 923
  tool->state        = ACTIVE;
  tool->gdisp        = gdisp;
924 925 926
  tool->paused_count = 0;

  /*  pause the current selection and grab the pointer  */
927
  gdisplays_selection_visibility (gdisp->gimage, SelectionPause);
928

929 930
  /* add motion memory if you press mod1 first ^ perfectmouse */
  if (((bevent->state & GDK_MOD1_MASK) != 0) != (perfectmouse != 0))
931 932 933 934 935
    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,
936 937
		      GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON1_MOTION_MASK |
		      GDK_BUTTON_RELEASE_MASK,
938 939
		      NULL, NULL, bevent->time);
  
940
  tool->gdisp = gdisp;
941 942 943
  tool->state = ACTIVE;

  b = ink_pen_ellipse (x, y, 
944
		       bevent->pressure, bevent->xtilt, bevent->ytilt, 10.0);
945 946 947 948

  ink_paste (ink_tool, drawable, b);
  ink_tool->last_blob = b;

949 950 951 952 953 954 955
  time_smoother_init (ink_tool, bevent->time);
  ink_tool->last_time = bevent->time;
  dist_smoother_init (ink_tool, 0.0);
  ink_tool->init_velocity = TRUE;
  ink_tool->lastx = x;
  ink_tool->lasty = y;

956
  gdisplay_flush_now (gdisp);
957 958 959
}

static void
960
ink_button_release (GimpTool       *tool,
961
		    GdkEventButton *bevent,
962
		    GDisplay       *gdisp)
963
{
964 965
  GimpInkTool *ink_tool;
  GimpImage   *gimage;
966

967 968 969
  ink_tool = GIMP_INK_TOOL (tool);

  gimage = gdisp->gimage;
970 971

  /*  resume the current selection and ungrab the pointer  */
972
  gdisplays_selection_visibility (gdisp->gimage, SelectionResume);
973 974 975 976 977 978 979

  gdk_pointer_ungrab (bevent->time);
  gdk_flush ();

  /*  Set tool state to inactive -- no longer painting */
  tool->state = INACTIVE;

980 981 982 983
  /*  free the last blob  */
  g_free (ink_tool->last_blob);
  ink_tool->last_blob = NULL;

984
  ink_finish (ink_tool, gimp_image_active_drawable (gdisp->gimage));
985 986 987
  gdisplays_flush ();
}

988 989

static void
990 991
dist_smoother_init (GimpInkTool *ink_tool,
		    gdouble      initval)
992 993 994 995 996 997 998 999 1000 1001 1002 1003
{
  gint i;

  ink_tool->dt_index = 0;

  for (i=0; i<DIST_SMOOTHER_BUFFER; i++)
    {
      ink_tool->dt_buffer[i] = initval;
    }
}

static gdouble
1004
dist_smoother_result (GimpInkTool *ink_tool)
1005
{
1006
  gint    i;
1007 1008
  gdouble result = 0.0;

1009
  for (i = 0; i < DIST_SMOOTHER_BUFFER; i++)
1010 1011 1012 1013
    {
      result += ink_tool->dt_buffer[i];
    }

1014
  return (result / (gdouble) DIST_SMOOTHER_BUFFER);
1015 1016 1017
}

static void
1018 1019
dist_smoother_add (GimpInkTool *ink_tool,
		   gdouble      value)
1020 1021 1022 1023 1024 1025 1026 1027 1028
{
  ink_tool->dt_buffer[ink_tool->dt_index] = value;

  if ((++ink_tool->dt_index) == DIST_SMOOTHER_BUFFER)
    ink_tool->dt_index = 0;
}


static void
1029 1030
time_smoother_init (GimpInkTool *ink_tool,
		    guint32      initval)
1031 1032 1033 1034 1035
{
  gint i;

  ink_tool->ts_index = 0;

1036
  for (i = 0; i < TIME_SMOOTHER_BUFFER; i++)
1037 1038 1039 1040 1041 1042
    {
      ink_tool->ts_buffer[i] = initval;
    }
}

static gdouble
1043
time_smoother_result (GimpInkTool *ink_tool)
BST 1999 Adam D. Moss's avatar