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

Michael Natterer's avatar
Michael Natterer committed
31 32 33 34 35
#include "base/pixel-region.h"
#include "base/temp-buf.h"
#include "base/tile.h"
#include "base/tile-manager.h"

36 37
#include "paint-funcs/paint-funcs.h"

38 39 40 41 42
#include "core/gimpcontext.h"
#include "core/gimpdrawable.h"
#include "core/gimpimage.h"
#include "core/gimpimage-mask.h"

43
#include "drawable.h"
44
#include "gimprc.h"
45
#include "undo.h"
46
#include "gdisplay.h"
47

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

54 55
#include "libgimp/gimpintl.h"

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

59

60
#define SUBSAMPLE 8
61

62 63
/*  the Ink structures  */

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

71

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

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

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

82 83
struct _InkOptions
{
84
  PaintOptions  paint_options;
85

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

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

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

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

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

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

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


118
/*  local function prototypes  */
119

120 121 122 123 124 125
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);
126
static void         ink_options_reset   (ToolOptions    *tool_options);
127 128

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

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

155
static void        ink_init             (GimpInkTool    *ink_tool, 
156 157 158
					 GimpDrawable   *drawable, 
					 gdouble         x, 
					 gdouble         y);
159 160
static void        ink_finish           (GimpInkTool    *ink_tool, 
					 GimpDrawable   *drawable);
161 162 163 164 165 166 167 168 169 170
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);
171

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

180
static void        ink_to_canvas_tiles  (GimpInkTool    *ink_tool,
181 182 183 184 185 186 187 188 189 190 191 192
					 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);
193

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


213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
/* 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;

229 230
static GimpToolClass *parent_class      = NULL;

231

232 233
/*  functions  */

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 306
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);

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

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

327 328 329 330 331 332 333 334 335 336 337 338
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);
}

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

344 345 346
  options = (InkOptions *) tool_options;

  paint_options_reset (tool_options);
347

348 349 350 351 352 353
  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);
354 355
  gtk_adjustment_set_value (GTK_ADJUSTMENT (options->vel_sensitivity_w),
			    options->vel_sensitivity_d);
356 357
  gtk_adjustment_set_value (GTK_ADJUSTMENT (options->tilt_angle_w),
			    options->tilt_angle_d);
358 359 360 361 362 363
  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);
364 365
  options->aspect = options->aspect_d;
  options->angle  = options->angle_d;
366
  gtk_widget_queue_draw (options->brush_w->widget);
367 368
}

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

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

389
  options->size             = options->size_d             = 4.4;
390
  options->sensitivity      = options->sensitivity_d      = 1.0;
391 392
  options->vel_sensitivity  = options->vel_sensitivity_d  = 0.8;
  options->tilt_sensitivity = options->tilt_sensitivity_d = 0.4;
393 394 395 396 397
  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
398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414
  /*  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);
415 416 417

  /*  size slider  */
  options->size_w =
418
    gtk_adjustment_new (options->size_d, 0.0, 20.0, 1.0, 2.0, 0.0);
419
  slider = gtk_hscale_new (GTK_ADJUSTMENT (options->size_w));
420
  gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP);
jtl's avatar
jtl committed
421 422 423
  gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
			     _("Size:"), 1.0, 1.0,
			     slider, 1, FALSE);
424
  gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED);
425
  gtk_signal_connect (GTK_OBJECT (options->size_w), "value_changed",
Michael Natterer's avatar
Michael Natterer committed
426
		      GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
427
		      &options->size);
jtl's avatar
jtl committed
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 454

  /* 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 */
455 456 457
  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));
458
  gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP);
jtl's avatar
jtl committed
459 460 461
  gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
			     _("Size:"), 1.0, 1.0,
			     slider, 1, FALSE);
462
  gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED);
463
  gtk_signal_connect (GTK_OBJECT (options->sensitivity_w), "value_changed",
Michael Natterer's avatar
Michael Natterer committed
464
		      GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
465
		      &options->sensitivity);
466
  
467 468 469 470
  /* 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));
471
  gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP);
jtl's avatar
jtl committed
472 473 474
  gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
			     _("Tilt:"), 1.0, 1.0,
			     slider, 1, FALSE);
475
  gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED);
476
  gtk_signal_connect (GTK_OBJECT (options->tilt_sensitivity_w), "value_changed",
Michael Natterer's avatar
Michael Natterer committed
477
		      GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
478
		      &options->tilt_sensitivity);
479 480 481 482 483 484

  /* 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
485 486 487
  gimp_table_attach_aligned (GTK_TABLE (table), 0, 3,
			     _("Speed:"), 1.0, 1.0,
			     slider, 1, FALSE);
488 489
  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
490
		      GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
491 492
		      &options->vel_sensitivity);

jtl's avatar
jtl committed
493 494 495 496
  /*  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
497

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

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

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

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

  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);
525
  gtk_box_pack_start (GTK_BOX (vbox), radio_button, FALSE, FALSE, 0);
526

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

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

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

537 538
  radio_button =
    gtk_radio_button_new_from_widget (GTK_RADIO_BUTTON (radio_button));
539 540 541 542 543
  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);
544
  gtk_box_pack_start (GTK_BOX (vbox), radio_button, FALSE, FALSE, 0);
545

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

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

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

556 557
  radio_button =
    gtk_radio_button_new_from_widget (GTK_RADIO_BUTTON (radio_button));
558 559 560 561 562
  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);
563
  gtk_box_pack_start (GTK_BOX (vbox), radio_button, FALSE, FALSE, 0);
564

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

567
  /* Brush shape widget */
568
  frame = gtk_frame_new (_("Shape"));
jtl's avatar
jtl committed
569
  gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
570 571 572 573 574 575
  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);
576

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

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

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

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

590 591 592 593 594
  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),
595
		      options->brush_w);
596 597
  gtk_signal_connect (GTK_OBJECT (darea), "button_release_event",
		      GTK_SIGNAL_FUNC (brush_widget_button_release),
598
		      options->brush_w);
599 600
  gtk_signal_connect (GTK_OBJECT (darea), "motion_notify_event",
		      GTK_SIGNAL_FUNC (brush_widget_motion_notify),
601
		      options->brush_w);
602 603
  gtk_signal_connect (GTK_OBJECT (darea), "expose_event",
		      GTK_SIGNAL_FUNC (brush_widget_expose), 
604
		      options->brush_w);
605 606
  gtk_signal_connect (GTK_OBJECT (darea), "realize",
		      GTK_SIGNAL_FUNC (brush_widget_realize),
607
		      options->brush_w);
608

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

  return options;
}

614 615 616

/*  the brush widget functions  */

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

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

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

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

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

static void
645 646 647 648 649
brush_widget_draw_brush (BrushWidget *brush_widget,
			 GtkWidget   *widget,
			 gdouble      xc,
			 gdouble      yc,
			 gdouble      radius)
650
{
651 652 653 654 655 656 657 658 659 660 661 662
  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);
663 664 665
}

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

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

  if (r0 < 2)
    return;

678 679 680 681 682 683 684
  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);
685

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

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

703
  brush_widget_active_rect (brush_widget, widget, &rect);
704 705 706 707 708
  
  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;
709

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

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

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

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

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

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

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

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

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

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

  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
786
  g_free (blob);
787 788 789 790 791 792 793 794 795 796 797

  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
798 799 800
paint_blob (GdkDrawable *drawable,
	    GdkGC       *gc,
	    Blob        *blob)
801 802 803 804 805 806 807 808 809 810 811 812
{
  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);
    }
}

813

814
static Blob *
815 816 817 818 819
ink_pen_ellipse (gdouble x_center,
		 gdouble y_center,
		 gdouble pressure,
		 gdouble xtilt,
		 gdouble ytilt,
820
		 gdouble velocity)
821 822 823 824 825
{
  double size;
  double tsin, tcos;
  double aspect, radmin;
  double x,y;
826
  double tscale;
Raph Levien's avatar
Raph Levien committed
827 828
  double tscale_c;
  double tscale_s;
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 857
  /* 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);
858

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

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

Raph Levien's avatar
Raph Levien committed
863 864 865 866
  /* 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 */

867
  tscale = ink_options->tilt_sensitivity * 10.0;
868 869
  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
870 871 872 873 874 875 876 877
  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
878 879 880 881 882 883 884 885 886 887 888 889
  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);
    }
890

891 892 893 894 895 896 897 898
  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;
  
899 900 901
  return ink_options->function (x_center * SUBSAMPLE, y_center * SUBSAMPLE,
				radmin*aspect*tcos, radmin*aspect*tsin,  
				-radmin*tsin, radmin*tcos);
902 903 904
}

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

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

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

  ink_init (ink_tool, drawable, x, y);

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

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

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

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

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

950 951 952 953 954 955 956
  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;

957
  gdisplay_flush_now (gdisp);
958 959 960
}

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

968 969 970
  ink_tool = GIMP_INK_TOOL (tool);

  gimage = gdisp->gimage;
971 972

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

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

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

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

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

989 990

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

  ink_tool->dt_index = 0;

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

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

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

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

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

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


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

  ink_tool->ts_index = 0;

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

static gdouble
Michael Natterer's avatar