gimpink.c 42.9 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 27
#include "libgimpmath/gimpmath.h"

Sven Neumann's avatar
Sven Neumann committed
28 29
#include "apptypes.h"

30 31 32 33
#include "appenv.h"
#include "drawable.h"
#include "draw_core.h"
#include "gimage_mask.h"
34 35
#include "gimpcontext.h"
#include "gimpimage.h"
36
#include "gimprc.h"
Michael Natterer's avatar
Michael Natterer committed
37
#include "gimpui.h"
38
#include "ink.h"
39
#include "paint_options.h"
40 41 42
#include "tools.h"
#include "undo.h"
#include "blob.h"
43
#include "gdisplay.h"
44 45 46 47 48
#include "paint_funcs.h"
#include "pixel_region.h"
#include "temp_buf.h"
#include "tile.h"
#include "tile_manager.h"
49

50 51
#include "libgimp/gimpintl.h"

52

53 54
#define SUBSAMPLE 8

55 56 57
#define DIST_SMOOTHER_BUFFER 10
#define TIME_SMOOTHER_BUFFER 10

58 59
/*  the Ink structures  */

60 61 62 63 64 65 66
typedef Blob * (* BlobFunc) (gdouble,
			     gdouble,
			     gdouble,
			     gdouble,
			     gdouble,
			     gdouble);

67

68
typedef struct _InkTool InkTool;
Michael Natterer's avatar
Michael Natterer committed
69

70 71
struct _InkTool
{
Michael Natterer's avatar
Michael Natterer committed
72
  DrawCore *core;         /*  Core select object             */
73
  
Michael Natterer's avatar
Michael Natterer committed
74
  Blob     *last_blob;	   /*  blob for last cursor position  */
75

Michael Natterer's avatar
Michael Natterer committed
76 77
  gint      x1, y1;       /*  image space coordinate         */
  gint      x2, y2;       /*  image space coords             */
78

Michael Natterer's avatar
Michael Natterer committed
79
  /* circular distance history buffer */
Michael Natterer's avatar
Michael Natterer committed
80 81
  gdouble   dt_buffer[DIST_SMOOTHER_BUFFER];
  gint      dt_index;
82

Michael Natterer's avatar
Michael Natterer committed
83
  /* circular timing history buffer */
Michael Natterer's avatar
Michael Natterer committed
84 85
  guint32   ts_buffer[TIME_SMOOTHER_BUFFER];
  gint      ts_index;
86

Michael Natterer's avatar
Michael Natterer committed
87 88
  gdouble   last_time;    /*  previous time of a motion event      */
  gdouble   lastx, lasty; /*  previous position of a motion event  */
89

Michael Natterer's avatar
Michael Natterer committed
90
  gboolean  init_velocity;
91 92 93
};

typedef struct _BrushWidget BrushWidget;
Michael Natterer's avatar
Michael Natterer committed
94

95 96
struct _BrushWidget
{
97 98
  GtkWidget *widget;
  gboolean   state;
99 100
};

101
typedef struct _InkOptions InkOptions;
Michael Natterer's avatar
Michael Natterer committed
102

103 104
struct _InkOptions
{
105
  PaintOptions  paint_options;
106

Michael Natterer's avatar
Michael Natterer committed
107 108
  gdouble       size;
  gdouble       size_d;
109
  GtkObject    *size_w;
110

Michael Natterer's avatar
Michael Natterer committed
111 112
  gdouble       sensitivity;
  gdouble       sensitivity_d;
113
  GtkObject    *sensitivity_w;
114

Michael Natterer's avatar
Michael Natterer committed
115 116
  gdouble       vel_sensitivity;
  gdouble       vel_sensitivity_d;
117
  GtkObject    *vel_sensitivity_w;
118

Michael Natterer's avatar
Michael Natterer committed
119 120
  gdouble       tilt_sensitivity;
  gdouble       tilt_sensitivity_d;
121
  GtkObject    *tilt_sensitivity_w;
122

Michael Natterer's avatar
Michael Natterer committed
123 124
  gdouble       tilt_angle;
  gdouble       tilt_angle_d;
125
  GtkObject    *tilt_angle_w;
126

127 128 129
  BlobFunc      function;
  BlobFunc      function_d;
  GtkWidget    *function_w[3];  /* 3 radio buttons */
130

Michael Natterer's avatar
Michael Natterer committed
131 132 133 134
  gdouble       aspect;
  gdouble       aspect_d;
  gdouble       angle;
  gdouble       angle_d;
135
  BrushWidget  *brush_w;
136 137 138
};


139
/*  local function prototypes  */
140

141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
static void        ink_button_press     (Tool           *tool,
					 GdkEventButton *mevent,
					 GDisplay       *gdisp);
static void        ink_button_release   (Tool           *tool,
					 GdkEventButton *bevent,
					 GDisplay       *gdisp);
static void        ink_motion           (Tool           *tool,
					 GdkEventMotion *mevent,
					 GDisplay       *gdisp);
static void        ink_cursor_update    (Tool           *tool,
					 GdkEventMotion *mevent,
					 GDisplay       *gdisp);
static void        ink_control          (Tool           *tool,
					 ToolAction      tool_action,
					 GDisplay       *gdisp);

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

static void        ink_init             (InkTool        *ink_tool, 
					 GimpDrawable   *drawable, 
					 gdouble         x, 
					 gdouble         y);
static void        ink_finish           (InkTool        *ink_tool, 
					 GimpDrawable   *drawable, 
					 gint            tool_id);
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);
185

Michael Natterer's avatar
Michael Natterer committed
186
/*  Rendering functions  */
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
static void        ink_set_paint_area   (InkTool        *ink_tool, 
					 GimpDrawable   *drawable, 
					 Blob           *blob);
static void        ink_paste            (InkTool        *ink_tool, 
					 GimpDrawable   *drawable,
					 Blob           *blob);

static void        ink_to_canvas_tiles  (InkTool        *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);
207

Michael Natterer's avatar
Michael Natterer committed
208
/*  Brush pseudo-widget callbacks  */
209 210
static void   brush_widget_active_rect    (BrushWidget    *brush_widget,
					   GtkWidget      *widget,
211
					   GdkRectangle   *rect);
212 213
static void   brush_widget_realize        (GtkWidget      *widget);
static void   brush_widget_expose         (GtkWidget      *widget,
214 215
					   GdkEventExpose *event,
					   BrushWidget    *brush_widget);
216
static void   brush_widget_button_press   (GtkWidget      *widget,
217 218
					   GdkEventButton *event,
					   BrushWidget    *brush_widget);
219
static void   brush_widget_button_release (GtkWidget      *widget,
220 221
					   GdkEventButton *event,
					   BrushWidget    *brush_widget);
222
static void   brush_widget_motion_notify  (GtkWidget      *widget,
223 224 225 226
					   GdkEventMotion *event,
					   BrushWidget    *brush_widget);


227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
/* 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;


244 245
/*  functions  */

246 247 248 249 250 251 252 253 254 255 256 257
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);
}

258
static void
259
ink_options_reset (void)
260 261 262
{
  InkOptions *options = ink_options;

263 264
  paint_options_reset ((PaintOptions *) options);

265 266 267 268 269 270
  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);
271 272
  gtk_adjustment_set_value (GTK_ADJUSTMENT (options->vel_sensitivity_w),
			    options->vel_sensitivity_d);
273 274
  gtk_adjustment_set_value (GTK_ADJUSTMENT (options->tilt_angle_w),
			    options->tilt_angle_d);
275 276 277 278 279 280
  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);
281 282
  options->aspect = options->aspect_d;
  options->angle  = options->angle_d;
283
  gtk_widget_queue_draw (options->brush_w->widget);
284 285
}

286
static InkOptions *
287
ink_options_new (void)
288
{
289
  InkOptions *options;
290

291
  GtkWidget *table;
292
  GtkWidget *vbox;
jtl's avatar
jtl committed
293 294
  GtkWidget *hbox;
  GtkWidget *hbox2;
295 296
  GtkWidget *radio_button;
  GtkWidget *pixmap_widget;
297
  GtkWidget *slider;
298
  GtkWidget *frame;
299
  GtkWidget *darea;
300 301
  GdkPixmap *pixmap;

302
  /*  the new ink tool options structure  */
Michael Natterer's avatar
Michael Natterer committed
303
  options = g_new (InkOptions, 1);
304 305 306
  paint_options_init ((PaintOptions *) options,
		      INK,
		      ink_options_reset);
307
  options->size             = options->size_d             = 4.4;
308
  options->sensitivity      = options->sensitivity_d      = 1.0;
309 310
  options->vel_sensitivity  = options->vel_sensitivity_d  = 0.8;
  options->tilt_sensitivity = options->tilt_sensitivity_d = 0.4;
311 312 313 314 315
  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
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
  /*  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);
333 334 335

  /*  size slider  */
  options->size_w =
336
    gtk_adjustment_new (options->size_d, 0.0, 20.0, 1.0, 2.0, 0.0);
337
  slider = gtk_hscale_new (GTK_ADJUSTMENT (options->size_w));
338
  gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP);
jtl's avatar
jtl committed
339 340 341
  gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
			     _("Size:"), 1.0, 1.0,
			     slider, 1, FALSE);
342
  gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED);
343
  gtk_signal_connect (GTK_OBJECT (options->size_w), "value_changed",
Michael Natterer's avatar
Michael Natterer committed
344
		      GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
345
		      &options->size);
jtl's avatar
jtl committed
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372

  /* 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 */
373 374 375
  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));
376
  gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP);
jtl's avatar
jtl committed
377 378 379
  gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
			     _("Size:"), 1.0, 1.0,
			     slider, 1, FALSE);
380
  gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED);
381
  gtk_signal_connect (GTK_OBJECT (options->sensitivity_w), "value_changed",
Michael Natterer's avatar
Michael Natterer committed
382
		      GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
383
		      &options->sensitivity);
384
  
385 386 387 388
  /* 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));
389
  gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP);
jtl's avatar
jtl committed
390 391 392
  gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
			     _("Tilt:"), 1.0, 1.0,
			     slider, 1, FALSE);
393
  gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED);
394
  gtk_signal_connect (GTK_OBJECT (options->tilt_sensitivity_w), "value_changed",
Michael Natterer's avatar
Michael Natterer committed
395
		      GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
396
		      &options->tilt_sensitivity);
397 398 399 400 401 402

  /* 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
403 404 405
  gimp_table_attach_aligned (GTK_TABLE (table), 0, 3,
			     _("Speed:"), 1.0, 1.0,
			     slider, 1, FALSE);
406 407
  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
408
		      GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
409 410
		      &options->vel_sensitivity);

jtl's avatar
jtl committed
411 412 413 414
  /*  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
415

416
  /* Brush type radiobuttons */
417
  frame = gtk_frame_new (_("Type"));
jtl's avatar
jtl committed
418
  gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
419 420
  gtk_widget_show (frame);

jtl's avatar
jtl committed
421 422 423
  hbox2 = gtk_hbox_new (FALSE, 0);
  gtk_container_add (GTK_CONTAINER (frame), hbox2);
  
424 425
  vbox = gtk_vbox_new (FALSE, 2);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 2);
jtl's avatar
jtl committed
426
  gtk_box_pack_start (GTK_BOX (hbox2), vbox, FALSE, FALSE, 0);
427 428 429 430
  gtk_widget_show (vbox);

  pixmap = blob_pixmap (gtk_widget_get_colormap (vbox),
			gtk_widget_get_visual (vbox),
431 432 433 434
			blob_ellipse);

  pixmap_widget = gtk_pixmap_new (pixmap, NULL);
  gdk_pixmap_unref (pixmap);
jtl's avatar
jtl committed
435
  gtk_misc_set_padding (GTK_MISC (pixmap_widget), 6, 0);
436 437 438 439 440 441 442

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

445
  options->function_w[0] = radio_button;
446 447 448

  pixmap = blob_pixmap (gtk_widget_get_colormap (vbox),
			gtk_widget_get_visual (vbox),
449 450 451 452
			blob_square);

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

455 456
  radio_button =
    gtk_radio_button_new_from_widget (GTK_RADIO_BUTTON (radio_button));
457 458 459 460 461
  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);
462
  gtk_box_pack_start (GTK_BOX (vbox), radio_button, FALSE, FALSE, 0);
463

464 465
  options->function_w[1] = radio_button;

466 467
  pixmap = blob_pixmap (gtk_widget_get_colormap (vbox),
			gtk_widget_get_visual (vbox),
468 469 470 471
			blob_diamond);

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

474 475
  radio_button =
    gtk_radio_button_new_from_widget (GTK_RADIO_BUTTON (radio_button));
476 477 478 479 480
  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);
481
  gtk_box_pack_start (GTK_BOX (vbox), radio_button, FALSE, FALSE, 0);
482

483 484
  options->function_w[2] = radio_button;

485
  /* Brush shape widget */
486
  frame = gtk_frame_new (_("Shape"));
jtl's avatar
jtl committed
487
  gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
488 489 490 491 492 493
  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);
494

jtl's avatar
jtl committed
495
  frame = gtk_aspect_frame_new (NULL, 0.0, 0.5, 1.0, FALSE);
496 497
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
  gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
498

jtl's avatar
jtl committed
499 500 501
  options->brush_w = g_new (BrushWidget, 1);
  options->brush_w->state = FALSE;

502
  darea = gtk_drawing_area_new();
503
  options->brush_w->widget = darea;
504

505 506
  gtk_drawing_area_size (GTK_DRAWING_AREA (darea), 60, 60);
  gtk_container_add (GTK_CONTAINER (frame), darea);
507

508 509 510 511 512
  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),
513
		      options->brush_w);
514 515
  gtk_signal_connect (GTK_OBJECT (darea), "button_release_event",
		      GTK_SIGNAL_FUNC (brush_widget_button_release),
516
		      options->brush_w);
517 518
  gtk_signal_connect (GTK_OBJECT (darea), "motion_notify_event",
		      GTK_SIGNAL_FUNC (brush_widget_motion_notify),
519
		      options->brush_w);
520 521
  gtk_signal_connect (GTK_OBJECT (darea), "expose_event",
		      GTK_SIGNAL_FUNC (brush_widget_expose), 
522
		      options->brush_w);
523 524
  gtk_signal_connect (GTK_OBJECT (darea), "realize",
		      GTK_SIGNAL_FUNC (brush_widget_realize),
525
		      options->brush_w);
526

jtl's avatar
jtl committed
527
  gtk_widget_show_all (hbox);
528 529 530 531

  return options;
}

532 533 534

/*  the brush widget functions  */

535
static void
536 537
brush_widget_active_rect (BrushWidget  *brush_widget,
			  GtkWidget    *widget,
538 539 540 541 542
			  GdkRectangle *rect)
{
  int x,y;
  int r;

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

545
  x = widget->allocation.width / 2 + 0.85 * r * ink_options->aspect / 10.0 *
546
    cos (ink_options->angle);
547
  y = widget->allocation.height / 2 + 0.85 * r * ink_options->aspect / 10.0 *
548 549
    sin (ink_options->angle);

550 551
  rect->x = x - 5;
  rect->y = y - 5;
552 553 554 555 556
  rect->width = 10;
  rect->height = 10;
}

static void
557
brush_widget_realize (GtkWidget *widget)
558
{
559
  gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
560 561 562
}

static void
563 564 565 566 567
brush_widget_draw_brush (BrushWidget *brush_widget,
			 GtkWidget   *widget,
			 gdouble      xc,
			 gdouble      yc,
			 gdouble      radius)
568
{
569 570 571 572 573 574 575 576 577 578 579 580
  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);
581 582 583
}

static void
584 585 586
brush_widget_expose (GtkWidget      *widget,
		     GdkEventExpose *event,
		     BrushWidget    *brush_widget)
587 588 589
{
  GdkRectangle rect;
  int r0;
590 591

  r0 = MIN (widget->allocation.width, widget->allocation.height) / 2;
592 593 594 595

  if (r0 < 2)
    return;

596 597 598 599 600 601 602
  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);
603

604 605
  brush_widget_active_rect (brush_widget, widget, &rect);
  gdk_draw_rectangle (widget->window, widget->style->bg_gc[GTK_STATE_NORMAL],
606 607 608
		      TRUE,	/* filled */
		      rect.x, rect.y, 
		      rect.width, rect.height);
609
  gtk_draw_shadow (widget->style, widget->window, widget->state, GTK_SHADOW_OUT,
610 611 612 613 614
		   rect.x, rect.y,
		   rect.width, rect.height);
}

static void
615 616 617
brush_widget_button_press (GtkWidget      *widget,
			   GdkEventButton *event,
			   BrushWidget    *brush_widget)
618 619 620
{
  GdkRectangle rect;

621
  brush_widget_active_rect (brush_widget, widget, &rect);
622 623 624 625 626
  
  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;
627

628
      gtk_grab_add (brush_widget->widget);
629 630 631 632
    }
}

static void
633 634 635
brush_widget_button_release (GtkWidget      *widget,
			     GdkEventButton *event,
			     BrushWidget    *brush_widget)
636 637
{
  brush_widget->state = FALSE;
638

639
  gtk_grab_remove (brush_widget->widget);
640 641 642
}

static void
643 644 645
brush_widget_motion_notify (GtkWidget      *widget,
			    GdkEventMotion *event,
			    BrushWidget    *brush_widget)
646 647 648 649 650 651 652 653
{
  int x;
  int y;
  int r0;
  int rsquare;

  if (brush_widget->state)
    {
654 655
      x = event->x - widget->allocation.width / 2;
      y = event->y - widget->allocation.height / 2;
656 657 658 659
      rsquare = x*x + y*y;

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

662
	  r0 = MIN (widget->allocation.width, widget->allocation.height) / 2;
663
	  ink_options->aspect =
664
	    10.0 * sqrt ((double) rsquare / (r0 * r0)) / 0.85;
665 666 667 668 669 670

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

671
	  gtk_widget_draw (widget, NULL);
672 673 674 675
	}
    }
}

676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703
/*
 * Return a black-on white pixmap in the given colormap and
 * visual that represents the BlobFunc 'function'
 */
static GdkPixmap *
blob_pixmap (GdkColormap *colormap,
	     GdkVisual *visual,
	     BlobFunc function)
{
  GdkPixmap *pixmap;
  GdkGC *black_gc, *white_gc;
  GdkColor tmp_color;
  Blob *blob;

  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
704
  g_free (blob);
705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729

  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
paint_blob (GdkDrawable *drawable, GdkGC *gc,
	    Blob *blob)
{
  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);
    }
}

730

731 732
static Blob *
ink_pen_ellipse (gdouble x_center, gdouble y_center,
733 734
		 gdouble pressure, gdouble xtilt, gdouble ytilt,
		 gdouble velocity)
735 736 737 738 739
{
  double size;
  double tsin, tcos;
  double aspect, radmin;
  double x,y;
740
  double tscale;
Raph Levien's avatar
Raph Levien committed
741 742
  double tscale_c;
  double tscale_s;
743
  
744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771
  /* 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);
772

773 774 775
  if (size*SUBSAMPLE < 1.0) size = 1.0/SUBSAMPLE;

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

Raph Levien's avatar
Raph Levien committed
777 778 779 780
  /* 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 */

781
  tscale = ink_options->tilt_sensitivity * 10.0;
782 783
  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
784 785 786 787 788 789 790 791
  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
792 793 794 795 796 797 798 799 800 801 802 803
  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);
    }
804

805 806 807 808 809 810 811 812
  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;
  
813 814 815
  return ink_options->function (x_center * SUBSAMPLE, y_center * SUBSAMPLE,
				radmin*aspect*tcos, radmin*aspect*tsin,  
				-radmin*tsin, radmin*tcos);
816 817 818 819 820
}

static void
ink_button_press (Tool           *tool,
		  GdkEventButton *bevent,
821
		  GDisplay       *gdisp)
822 823 824 825 826 827 828 829 830 831 832
{
  gdouble       x, y;
  InkTool      *ink_tool;
  GimpDrawable *drawable;
  Blob         *b;

  ink_tool = (InkTool *) tool->private;

  /*  Keep the coordinates of the target  */
  gdisplay_untransform_coords_f (gdisp, bevent->x, bevent->y,
				 &x, &y, TRUE);
833
  drawable = gimp_image_active_drawable (gdisp->gimage);
834 835 836

  ink_init (ink_tool, drawable, x, y);

837 838
  tool->state        = ACTIVE;
  tool->gdisp        = gdisp;
839 840 841
  tool->paused_count = 0;

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

844 845
  /* add motion memory if you press mod1 first ^ perfectmouse */
  if (((bevent->state & GDK_MOD1_MASK) != 0) != (perfectmouse != 0))
846 847 848 849 850
    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,
851 852
		      GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON1_MOTION_MASK |
		      GDK_BUTTON_RELEASE_MASK,
853 854
		      NULL, NULL, bevent->time);
  
855
  tool->gdisp = gdisp;
856 857 858
  tool->state = ACTIVE;

  b = ink_pen_ellipse (x, y, 
859
		       bevent->pressure, bevent->xtilt, bevent->ytilt, 10.0);
860 861 862 863

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

864 865 866 867 868 869 870
  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;

871
  gdisplay_flush_now (gdisp);
872 873 874 875 876
}

static void
ink_button_release (Tool           *tool,
		    GdkEventButton *bevent,
877
		    GDisplay       *gdisp)
878
{
879 880
  GImage  *gimage;
  InkTool *ink_tool;
881

882
  gimage   = gdisp->gimage;
883 884 885
  ink_tool = (InkTool *) tool->private;

  /*  resume the current selection and ungrab the pointer  */
886
  gdisplays_selection_visibility (gdisp->gimage, SelectionResume);
887 888 889 890 891 892 893

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

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

894 895 896 897
  /*  free the last blob  */
  g_free (ink_tool->last_blob);
  ink_tool->last_blob = NULL;

898
  ink_finish (ink_tool, gimp_image_active_drawable (gdisp->gimage), tool->ID);
899 900 901
  gdisplays_flush ();
}

902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963

static void
dist_smoother_init (InkTool* ink_tool, gdouble initval)
{
  gint i;

  ink_tool->dt_index = 0;

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

static gdouble
dist_smoother_result (InkTool* ink_tool)
{
  gint i;
  gdouble result = 0.0;

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

  return (result / (gdouble)DIST_SMOOTHER_BUFFER);
}

static void
dist_smoother_add (InkTool* ink_tool, gdouble value)
{
  ink_tool->dt_buffer[ink_tool->dt_index] = value;

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


static void
time_smoother_init (InkTool* ink_tool, guint32 initval)
{
  gint i;

  ink_tool->ts_index = 0;

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

static gdouble
time_smoother_result (InkTool* ink_tool)
{
  gint i;
  guint64 result = 0;

  for (i=0; i<TIME_SMOOTHER_BUFFER; i++)
    {
      result += ink_tool->ts_buffer[i];
    }

Tor Lillqvist's avatar
Tor Lillqvist committed
964 965 966
#ifdef _MSC_VER
  return (gdouble) (gint64) (result / TIME_SMOOTHER_BUFFER);
#else
967
  return (result / TIME_SMOOTHER_BUFFER);
Tor Lillqvist's avatar
Tor Lillqvist committed
968
#endif
969 970 971 972 973 974 975 976 977 978 979 980
}

static void
time_smoother_add (InkTool* ink_tool, guint32 value)
{
  ink_tool->ts_buffer[ink_tool->ts_index] = value;

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


981 982 983
static void
ink_motion (Tool           *tool,
	    GdkEventMotion *mevent,
984
	    GDisplay       *gdisp)
985
{
986
  InkTool      *ink_tool;
987
  GimpDrawable *drawable;
988
  Blob         *b, *blob_union;
989

990 991 992 993
  gdouble x, y;
  gdouble pressure;
  gdouble velocity;
  gdouble dist;
994 995
  gdouble lasttime, thistime;
  
996 997 998
  ink_tool = (InkTool *) tool->private;

  gdisplay_untransform_coords_f (gdisp, mevent->x, mevent->y, &x, &y, TRUE);
999
  drawable = gimp_image_active_drawable (gdisp->gimage);
1000

1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036
  lasttime = ink_tool->last_time;

  time_smoother_add (ink_tool, mevent->time);
  thistime = ink_tool->last_time =
    time_smoother_result(ink_tool);

  /* The time resolution on X-based GDK motion events is
     bloody awful, hence the use of the smoothing function.
     Sadly this also means that there is always the chance of
     having an indeterminite velocity since this event and
     the previous several may still appear to issue at the same
     instant. -ADM */

  if (thistime == lasttime)
    thistime = lasttime + 1;

  if (ink_tool->init_velocity)
    {
      dist_smoother_init (ink_tool, dist = sqrt((ink_tool->lastx-x)*(ink_tool->lastx-x) +
						(ink_tool->lasty-y)*(ink_tool->lasty-y)));
      ink_tool->init_velocity = FALSE;
    }
  else
    {
      dist_smoother_add (ink_tool, sqrt((ink_tool->lastx-x)*(ink_tool->lastx-x) +
					(ink_tool->lasty-y)*(ink_tool->lasty-y)));
      dist = dist_smoother_result(ink_tool);
    }

  ink_tool->lastx = x;
  ink_tool->lasty = y;

  pressure = mevent->pressure;
  velocity = 10.0 * sqrt((dist) / (double)(thistime - lasttime));
  
  b = ink_pen_ellipse (x, y, pressure, mevent->xtilt, mevent->ytilt, velocity);
1037 1038 1039 1040 1041 1042 1043
  blob_union = blob_convex_union (ink_tool->last_blob, b);
  g_free (ink_tool->last_blob);
  ink_tool->last_blob = b;

  ink_paste (ink_tool, drawable, blob_union);  
  g_free (blob_union);
  
1044
  gdisplay_flush_now (gdisp);
1045 1046 1047 1048 1049
}

static void
ink_cursor_update (Tool           *tool,
		   GdkEventMotion *mevent,
1050
		   GDisplay       *gdisp)
1051
{
1052 1053 1054
  Layer         *layer;
  GdkCursorType  ctype = GDK_TOP_LEFT_ARROW;
  gint           x, y;
1055

1056 1057
  gdisplay_untransform_coords (gdisp, mevent->x, mevent->y,
			       &x, &y, FALSE, FALSE);
1058
  if ((layer = gimp_image_get_active_layer (gdisp->gimage))) 
1059 1060
    {
      int off_x, off_y;
1061

1062
      gimp_drawable_offsets (GIMP_DRAWABLE (layer), &off_x, &off_y);
1063 1064

      if (x >= off_x && y >= off_y &&
1065 1066
	  x < (off_x + gimp_drawable_width (GIMP_DRAWABLE (layer))) &&
	  y < (off_y + gimp_drawable_height (GIMP_DRAWABLE (layer))))
1067 1068 1069 1070 1071 1072 1073 1074 1075
	{
	  /*  One more test--is there a selected region?
	   *  if so, is cursor inside?
	   */
	  if (gimage_mask_is_empty (gdisp->gimage))
	    ctype = GIMP_MOUSE_CURSOR;
	  else if (gimage_mask_value (gdisp->gimage, x, y))
	    ctype = GIMP_MOUSE_CURSOR;
	}
1076
    }
1077 1078 1079 1080
  gdisplay_install_tool_cursor (gdisp, ctype,
				INK,
				CURSOR_MODIFIER_NONE,
				FALSE);
1081 1082 1083
}

static void
Michael Natterer's avatar
Michael Natterer committed
1084 1085
ink_control (Tool       *tool,
	     ToolAction  action,
1086
	     GDisplay   *gdisp)
1087 1088 1089 1090 1091