gimpink.c 43.7 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"

Michael Natterer's avatar
Michael Natterer committed
38
#include "core/gimp.h"
39 40 41 42 43
#include "core/gimpcontext.h"
#include "core/gimpdrawable.h"
#include "core/gimpimage.h"
#include "core/gimpimage-mask.h"

44 45 46
#include "gimpinktool.h"
#include "gimpinktool-blob.h"
#include "gimptool.h"
47
#include "paint_options.h"
48
#include "tool_manager.h"
49

Michael Natterer's avatar
Michael Natterer committed
50 51 52 53
#include "gimprc.h"
#include "undo.h"
#include "gdisplay.h"

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 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
static void         ink_options_reset   (GimpToolOptions *tool_options);

static void        ink_button_press     (GimpTool        *tool,
					 GdkEventButton  *mevent,
					 GDisplay        *gdisp);
static void        ink_button_release   (GimpTool        *tool,
					 GdkEventButton  *bevent,
					 GDisplay        *gdisp);
static void        ink_motion           (GimpTool        *tool,
					 GdkEventMotion  *mevent,
					 GDisplay        *gdisp);
static void        ink_cursor_update    (GimpTool        *tool,
					 GdkEventMotion  *mevent,
					 GDisplay        *gdisp);
static void        ink_control          (GimpTool        *tool,
					 ToolAction       tool_action,
					 GDisplay        *gdisp);

static void        time_smoother_add    (GimpInkTool     *ink_tool,
					 guint32          value);
static gdouble     time_smoother_result (GimpInkTool     *ink_tool);
static void        time_smoother_init   (GimpInkTool     *ink_tool,
					 guint32          initval);
static void        dist_smoother_add    (GimpInkTool     *ink_tool,
					 gdouble          value);
static gdouble     dist_smoother_result (GimpInkTool     *ink_tool);
static void        dist_smoother_init   (GimpInkTool     *ink_tool,
					 gdouble          initval);

static void        ink_init             (GimpInkTool     *ink_tool, 
					 GimpDrawable    *drawable, 
					 gdouble          x, 
					 gdouble          y);
static void        ink_finish           (GimpInkTool     *ink_tool, 
					 GimpDrawable    *drawable);
161 162
static void        ink_cleanup          (void);

163 164 165 166 167 168 169 170
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 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
static void        ink_set_paint_area   (GimpInkTool     *ink_tool, 
					 GimpDrawable    *drawable, 
					 Blob            *blob);
static void        ink_paste            (GimpInkTool     *ink_tool, 
					 GimpDrawable    *drawable,
					 Blob            *blob);

static void        ink_to_canvas_tiles  (GimpInkTool     *ink_tool,
					 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
void
Michael Natterer's avatar
Michael Natterer committed
235
gimp_ink_tool_register (Gimp *gimp)
236
{
Michael Natterer's avatar
Michael Natterer committed
237 238
  tool_manager_register_tool (gimp,
			      GIMP_TYPE_INK_TOOL,
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
			      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,
306
					  (GimpToolOptions *) ink_options);
307

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

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

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

340
static void
341
ink_options_reset (GimpToolOptions *tool_options)
342
{
343
  InkOptions *options;
344

345 346 347
  options = (InkOptions *) tool_options;

  paint_options_reset (tool_options);
348

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

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

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

390
  options->size             = options->size_d             = 4.4;
391
  options->sensitivity      = options->sensitivity_d      = 1.0;
392 393
  options->vel_sensitivity  = options->vel_sensitivity_d  = 0.8;
  options->tilt_sensitivity = options->tilt_sensitivity_d = 0.4;
394 395 396 397 398
  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
399 400
  /*  the main vbox  */
  vbox = gtk_vbox_new (FALSE, 2);
401
  gtk_box_pack_start (GTK_BOX (((GimpToolOptions *) options)->main_vbox), vbox,
jtl's avatar
jtl committed
402 403 404 405 406 407 408 409 410 411 412 413 414 415
		      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);
416 417 418

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  return options;
}

615 616 617

/*  the brush widget functions  */

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

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

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

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

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

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

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

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

  if (r0 < 2)
    return;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

814

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

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

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

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

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

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

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

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

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

  ink_init (ink_tool, drawable, x, y);

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

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

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

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

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

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

958
  gdisplay_flush_now (gdisp);
959 960 961
}

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

969 970 971
  ink_tool = GIMP_INK_TOOL (tool);

  gimage = gdisp->gimage;
972 973

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

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

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

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

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

990 991

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

  ink_tool->dt_index = 0;

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

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

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

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

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

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


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

  ink_tool->ts_index = 0;

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

static gdouble
1045
time_smoother_result (GimpInkTool *ink_tool)
1046
{
1047
  gint    i;
1048 1049
  guint64 result = 0;

1050
  for (i = 0; i < TIME_SMOOTHER_BUFFER; i++)
1051 1052 1053 1054
    {
      result += ink_tool->ts_buffer[i];
    }

Tor Lillqvist's avatar
Tor Lillqvist committed
1055 1056 1057
#ifdef _MSC_VER
  return (gdouble) (gint64) (result / TIME_SMOOTHER_BUFFER);
#else
1058
  return (result / TIME_SMOOTHER_BUFFER);
Tor Lillqvist's avatar
Tor Lillqvist committed
1059
#endif
1060 1061 1062
}

static void
1063 1064
time_smoother_add (GimpInkTool *ink_tool,
		   guint32      value)
1065 1066 1067 1068 1069 1070 1071 1072
{
  ink_tool->ts_buffer[ink_tool->ts_index] = value;

  if ((++ink_tool->ts_index) == TIME_SMOOTHER_BUFFER)
    ink_tool->ts_index = 0;
}


1073
static void