gimpink.c 44.6 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
#include "display/gimpdisplay.h"
45
#include "display/gimpdisplay-foreach.h"
Michael Natterer's avatar
Michael Natterer committed
46
#include "display/gimpdisplayshell.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

Michael Natterer's avatar
Michael Natterer committed
54 55 56
#include "gimprc.h"
#include "undo.h"

57 58
#include "libgimp/gimpintl.h"

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
static void        gimp_ink_tool_class_init      (GimpInkToolClass *klass);
static void        gimp_ink_tool_init            (GimpInkTool      *tool);

123
static void        gimp_ink_tool_finalize        (GObject          *object);
124 125

static InkOptions * ink_options_new     (void);
126 127 128 129
static void         ink_options_reset   (GimpToolOptions *tool_options);

static void        ink_button_press     (GimpTool        *tool,
					 GdkEventButton  *mevent,
Michael Natterer's avatar
Michael Natterer committed
130
					 GimpDisplay     *gdisp);
131 132
static void        ink_button_release   (GimpTool        *tool,
					 GdkEventButton  *bevent,
Michael Natterer's avatar
Michael Natterer committed
133
					 GimpDisplay     *gdisp);
134 135
static void        ink_motion           (GimpTool        *tool,
					 GdkEventMotion  *mevent,
Michael Natterer's avatar
Michael Natterer committed
136
					 GimpDisplay     *gdisp);
137 138
static void        ink_cursor_update    (GimpTool        *tool,
					 GdkEventMotion  *mevent,
Michael Natterer's avatar
Michael Natterer committed
139
					 GimpDisplay     *gdisp);
140 141
static void        ink_control          (GimpTool        *tool,
					 ToolAction       tool_action,
Michael Natterer's avatar
Michael Natterer committed
142
					 GimpDisplay     *gdisp);
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160

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
			      TRUE,
			      "gimp:ink_tool",
			      _("Ink Tool"),
			      _("Draw in ink"),
			      N_("/Tools/Paint Tools/Ink"), "K",
			      NULL, "tools/ink.html",
Michael Natterer's avatar
Michael Natterer committed
245
			      GIMP_STOCK_TOOL_INK);
246 247
}

248
GType
249 250
gimp_ink_tool_get_type (void)
{
251
  static GType tool_type = 0;
252 253 254

  if (! tool_type)
    {
255
      static const GTypeInfo tool_info =
256
      {
257 258 259 260 261 262
        sizeof (GimpInkToolClass),
	(GBaseInitFunc) NULL,
	(GBaseFinalizeFunc) NULL,
	(GClassInitFunc) gimp_ink_tool_class_init,
	NULL,           /* class_finalize */
	NULL,           /* class_data     */
263
	sizeof (GimpInkTool),
264 265
	0,              /* n_preallocs    */
	(GInstanceInitFunc) gimp_ink_tool_init,
266 267
      };

268 269 270
      tool_type = g_type_register_static (GIMP_TYPE_TOOL,
					  "GimpInkTool", 
                                          &tool_info, 0);
271 272 273 274 275 276 277 278
    }

  return tool_type;
}

static void
gimp_ink_tool_class_init (GimpInkToolClass *klass)
{
279
  GObjectClass   *object_class;
280 281
  GimpToolClass  *tool_class;

282 283
  object_class = G_OBJECT_CLASS (klass);
  tool_class   = GIMP_TOOL_CLASS (klass);
284

285
  parent_class = g_type_class_peek_parent (klass);
286

287
  object_class->finalize     = gimp_ink_tool_finalize;
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308

  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,
309
					  (GimpToolOptions *) ink_options);
310

311
      ink_options_reset ((GimpToolOptions *) ink_options);
312 313 314 315
    }
}

static void
316
gimp_ink_tool_finalize (GObject *object)
317 318 319 320 321 322
{
  GimpInkTool *ink_tool;

  ink_tool = GIMP_INK_TOOL (object);

  if (ink_tool->last_blob)
323 324 325 326
    {
      g_free (ink_tool->last_blob);
      ink_tool->last_blob = NULL;
    }
327 328 329

  ink_cleanup ();

330
  G_OBJECT_CLASS (parent_class)->finalize (object);
331 332
}

333 334 335 336 337 338 339 340 341 342 343 344
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);
}

345
static void
346
ink_options_reset (GimpToolOptions *tool_options)
347
{
348
  InkOptions *options;
349

350 351 352
  options = (InkOptions *) tool_options;

  paint_options_reset (tool_options);
353

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

375
static InkOptions *
376
ink_options_new (void)
377
{
378
  InkOptions *options;
379 380 381 382 383 384 385 386 387 388
  GtkWidget  *table;
  GtkWidget  *vbox;
  GtkWidget  *hbox;
  GtkWidget  *hbox2;
  GtkWidget  *radio_button;
  GtkWidget  *pixmap_widget;
  GtkWidget  *slider;
  GtkWidget  *frame;
  GtkWidget  *darea;
  GdkPixmap  *pixmap;
389

390
  options = g_new0 (InkOptions, 1);
391
  paint_options_init ((PaintOptions *) options,
392
		      GIMP_TYPE_INK_TOOL,
393
		      ink_options_reset);
394

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

  /*  size slider  */
  options->size_w =
424
    gtk_adjustment_new (options->size_d, 0.0, 20.0, 1.0, 2.0, 0.0);
425
  slider = gtk_hscale_new (GTK_ADJUSTMENT (options->size_w));
426
  gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP);
jtl's avatar
jtl committed
427 428 429
  gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
			     _("Size:"), 1.0, 1.0,
			     slider, 1, FALSE);
430
  gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED);
431 432 433 434

  g_signal_connect (G_OBJECT (options->size_w), "value_changed",
		    G_CALLBACK (gimp_double_adjustment_update),
		    &options->size);
jtl's avatar
jtl committed
435 436 437 438 439 440 441 442 443 444

  /* 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);
445 446 447 448

  g_signal_connect (G_OBJECT (options->tilt_angle_w), "value_changed",
		    G_CALLBACK (gimp_double_adjustment_update),
		    &options->tilt_angle);
jtl's avatar
jtl committed
449 450 451 452 453 454 455 456 457 458 459 460 461 462

  /* 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 */
463 464 465
  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));
466
  gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP);
jtl's avatar
jtl committed
467 468 469
  gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
			     _("Size:"), 1.0, 1.0,
			     slider, 1, FALSE);
470
  gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED);
471 472 473 474

  g_signal_connect (G_OBJECT (options->sensitivity_w), "value_changed",
		    G_CALLBACK (gimp_double_adjustment_update),
		    &options->sensitivity);
475
  
476 477 478 479
  /* 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));
480
  gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP);
jtl's avatar
jtl committed
481 482 483
  gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
			     _("Tilt:"), 1.0, 1.0,
			     slider, 1, FALSE);
484
  gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED);
485 486 487 488

  g_signal_connect (G_OBJECT (options->tilt_sensitivity_w), "value_changed",
		    G_CALLBACK (gimp_double_adjustment_update),
		    &options->tilt_sensitivity);
489 490 491 492 493 494

  /* 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
495 496 497
  gimp_table_attach_aligned (GTK_TABLE (table), 0, 3,
			     _("Speed:"), 1.0, 1.0,
			     slider, 1, FALSE);
498
  gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED);
499 500 501 502

  g_signal_connect (G_OBJECT (options->vel_sensitivity_w), "value_changed",
		    G_CALLBACK (gimp_double_adjustment_update),
		    &options->vel_sensitivity);
503

jtl's avatar
jtl committed
504 505 506 507
  /*  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
508

509
  /* Brush type radiobuttons */
510
  frame = gtk_frame_new (_("Type"));
jtl's avatar
jtl committed
511
  gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
512 513
  gtk_widget_show (frame);

jtl's avatar
jtl committed
514 515 516
  hbox2 = gtk_hbox_new (FALSE, 0);
  gtk_container_add (GTK_CONTAINER (frame), hbox2);
  
517 518
  vbox = gtk_vbox_new (FALSE, 2);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 2);
jtl's avatar
jtl committed
519
  gtk_box_pack_start (GTK_BOX (hbox2), vbox, FALSE, FALSE, 0);
520 521 522 523
  gtk_widget_show (vbox);

  pixmap = blob_pixmap (gtk_widget_get_colormap (vbox),
			gtk_widget_get_visual (vbox),
524 525 526
			blob_ellipse);

  pixmap_widget = gtk_pixmap_new (pixmap, NULL);
Sven Neumann's avatar
Sven Neumann committed
527
  gdk_drawable_unref (pixmap);
jtl's avatar
jtl committed
528
  gtk_misc_set_padding (GTK_MISC (pixmap_widget), 6, 0);
529 530 531

  radio_button = gtk_radio_button_new (NULL);
  gtk_container_add (GTK_CONTAINER (radio_button), pixmap_widget);
532
  gtk_box_pack_start (GTK_BOX (vbox), radio_button, FALSE, FALSE, 0);
533

534 535 536 537 538
  g_signal_connect (G_OBJECT (radio_button), "toggled",
		    G_CALLBACK (ink_type_update),
		    (gpointer) blob_ellipse);


539
  options->function_w[0] = radio_button;
540 541 542

  pixmap = blob_pixmap (gtk_widget_get_colormap (vbox),
			gtk_widget_get_visual (vbox),
543 544 545
			blob_square);

  pixmap_widget = gtk_pixmap_new (pixmap, NULL);
Sven Neumann's avatar
Sven Neumann committed
546
  gdk_drawable_unref (pixmap);
jtl's avatar
jtl committed
547
  gtk_misc_set_padding (GTK_MISC (pixmap_widget), 6, 0);
548

549 550
  radio_button =
    gtk_radio_button_new_from_widget (GTK_RADIO_BUTTON (radio_button));
551
  gtk_container_add (GTK_CONTAINER (radio_button), pixmap_widget);
552
  gtk_box_pack_start (GTK_BOX (vbox), radio_button, FALSE, FALSE, 0);
553

554 555 556 557 558
  g_signal_connect (G_OBJECT (radio_button), "toggled",
		    G_CALLBACK (ink_type_update), 
		    (gpointer) blob_square);
  

559 560
  options->function_w[1] = radio_button;

561 562
  pixmap = blob_pixmap (gtk_widget_get_colormap (vbox),
			gtk_widget_get_visual (vbox),
563 564 565
			blob_diamond);

  pixmap_widget = gtk_pixmap_new (pixmap, NULL);
Sven Neumann's avatar
Sven Neumann committed
566
  gdk_drawable_unref (pixmap);
jtl's avatar
jtl committed
567
  gtk_misc_set_padding (GTK_MISC (pixmap_widget), 6, 0);
568

569 570
  radio_button =
    gtk_radio_button_new_from_widget (GTK_RADIO_BUTTON (radio_button));
571
  gtk_container_add (GTK_CONTAINER (radio_button), pixmap_widget);
572
  gtk_box_pack_start (GTK_BOX (vbox), radio_button, FALSE, FALSE, 0);
573

574 575 576 577 578
  g_signal_connect (G_OBJECT (radio_button), "toggled",
		    G_CALLBACK (ink_type_update), 
		    (gpointer) blob_diamond);


579 580
  options->function_w[2] = radio_button;

581
  /* Brush shape widget */
582
  frame = gtk_frame_new (_("Shape"));
jtl's avatar
jtl committed
583
  gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
584 585 586 587 588 589
  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);
590

jtl's avatar
jtl committed
591
  frame = gtk_aspect_frame_new (NULL, 0.0, 0.5, 1.0, FALSE);
592 593
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
  gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
594

jtl's avatar
jtl committed
595 596 597
  options->brush_w = g_new (BrushWidget, 1);
  options->brush_w->state = FALSE;

598
  darea = gtk_drawing_area_new();
599
  options->brush_w->widget = darea;
600

601 602
  gtk_drawing_area_size (GTK_DRAWING_AREA (darea), 60, 60);
  gtk_container_add (GTK_CONTAINER (frame), darea);
603

604 605 606
  gtk_widget_set_events (darea, 
			 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
			 | GDK_POINTER_MOTION_MASK | GDK_EXPOSURE_MASK);
607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622

  g_signal_connect (G_OBJECT (darea), "button_press_event",
		    G_CALLBACK (brush_widget_button_press),
		    options->brush_w);
  g_signal_connect (G_OBJECT (darea), "button_release_event",
		    G_CALLBACK (brush_widget_button_release),
		    options->brush_w);
  g_signal_connect (G_OBJECT (darea), "motion_notify_event",
		    G_CALLBACK (brush_widget_motion_notify),
		    options->brush_w);
  g_signal_connect (G_OBJECT (darea), "expose_event",
		    G_CALLBACK (brush_widget_expose), 
		    options->brush_w);
  g_signal_connect (G_OBJECT (darea), "realize",
		    G_CALLBACK (brush_widget_realize),
		    options->brush_w);
623

jtl's avatar
jtl committed
624
  gtk_widget_show_all (hbox);
625 626 627 628

  return options;
}

629 630 631

/*  the brush widget functions  */

632
static void
633 634
brush_widget_active_rect (BrushWidget  *brush_widget,
			  GtkWidget    *widget,
635 636 637 638 639
			  GdkRectangle *rect)
{
  int x,y;
  int r;

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

642
  x = widget->allocation.width / 2 + 0.85 * r * ink_options->aspect / 10.0 *
643
    cos (ink_options->angle);
644
  y = widget->allocation.height / 2 + 0.85 * r * ink_options->aspect / 10.0 *
645 646
    sin (ink_options->angle);

647 648
  rect->x = x - 5;
  rect->y = y - 5;
649 650 651 652 653
  rect->width = 10;
  rect->height = 10;
}

static void
654
brush_widget_realize (GtkWidget *widget)
655
{
656
  gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
657 658 659
}

static void
660 661 662 663 664
brush_widget_draw_brush (BrushWidget *brush_widget,
			 GtkWidget   *widget,
			 gdouble      xc,
			 gdouble      yc,
			 gdouble      radius)
665
{
666 667 668 669 670 671 672 673 674 675 676 677
  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);
678 679 680
}

static void
681 682 683
brush_widget_expose (GtkWidget      *widget,
		     GdkEventExpose *event,
		     BrushWidget    *brush_widget)
684 685 686
{
  GdkRectangle rect;
  int r0;
687 688

  r0 = MIN (widget->allocation.width, widget->allocation.height) / 2;
689 690 691 692

  if (r0 < 2)
    return;

693 694 695 696 697 698 699
  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);
700

701 702
  brush_widget_active_rect (brush_widget, widget, &rect);
  gdk_draw_rectangle (widget->window, widget->style->bg_gc[GTK_STATE_NORMAL],
703 704 705
		      TRUE,	/* filled */
		      rect.x, rect.y, 
		      rect.width, rect.height);
706
  gtk_draw_shadow (widget->style, widget->window, widget->state, GTK_SHADOW_OUT,
707 708 709 710 711
		   rect.x, rect.y,
		   rect.width, rect.height);
}

static void
712 713 714
brush_widget_button_press (GtkWidget      *widget,
			   GdkEventButton *event,
			   BrushWidget    *brush_widget)
715 716 717
{
  GdkRectangle rect;

718
  brush_widget_active_rect (brush_widget, widget, &rect);
719 720 721 722 723
  
  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;
724

725
      gtk_grab_add (brush_widget->widget);
726 727 728 729
    }
}

static void
730 731 732
brush_widget_button_release (GtkWidget      *widget,
			     GdkEventButton *event,
			     BrushWidget    *brush_widget)
733 734
{
  brush_widget->state = FALSE;
735

736
  gtk_grab_remove (brush_widget->widget);
737 738 739
}

static void
740 741 742
brush_widget_motion_notify (GtkWidget      *widget,
			    GdkEventMotion *event,
			    BrushWidget    *brush_widget)
743 744 745 746 747 748 749 750
{
  int x;
  int y;
  int r0;
  int rsquare;

  if (brush_widget->state)
    {
751 752
      x = event->x - widget->allocation.width / 2;
      y = event->y - widget->allocation.height / 2;
753 754 755 756
      rsquare = x*x + y*y;

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

759
	  r0 = MIN (widget->allocation.width, widget->allocation.height) / 2;
760
	  ink_options->aspect =
761
	    10.0 * sqrt ((double) rsquare / (r0 * r0)) / 0.85;
762 763 764 765 766 767

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

768
	  gtk_widget_draw (widget, NULL);
769 770 771 772
	}
    }
}

773 774 775 776 777 778
/*
 * Return a black-on white pixmap in the given colormap and
 * visual that represents the BlobFunc 'function'
 */
static GdkPixmap *
blob_pixmap (GdkColormap *colormap,
779 780
	     GdkVisual   *visual,
	     BlobFunc     function)
781 782
{
  GdkPixmap *pixmap;
783 784
  GdkGC     *black_gc, *white_gc;
  GdkColor   tmp_color;
785
  gboolean   success;
786
  Blob      *blob;
787 788 789

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

790 791 792 793 794
  tmp_color.red   = 0;
  tmp_color.green = 0;
  tmp_color.blue  = 0;
  gdk_colormap_alloc_colors (colormap, &tmp_color, 1, FALSE, TRUE, &success);

795 796 797
  black_gc = gdk_gc_new (pixmap);
  gdk_gc_set_foreground (black_gc, &tmp_color);

798 799 800 801 802
  tmp_color.red   = 255;
  tmp_color.green = 255;
  tmp_color.blue  = 255;
  gdk_colormap_alloc_colors (colormap, &tmp_color, 1, FALSE, TRUE, &success);

803 804 805 806 807 808 809
  white_gc = gdk_gc_new (pixmap);
  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
810
  g_free (blob);
811 812 813 814 815 816 817 818 819 820 821

  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
822 823 824
paint_blob (GdkDrawable *drawable,
	    GdkGC       *gc,
	    Blob        *blob)
825 826 827 828 829 830 831 832 833 834 835 836
{
  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);
    }
}

837

838
static Blob *
839 840 841 842 843
ink_pen_ellipse (gdouble x_center,
		 gdouble y_center,
		 gdouble pressure,
		 gdouble xtilt,
		 gdouble ytilt,
844
		 gdouble velocity)
845 846 847 848 849
{
  double size;
  double tsin, tcos;
  double aspect, radmin;
  double x,y;
850
  double tscale;
Raph Levien's avatar
Raph Levien committed
851 852
  double tscale_c;
  double tscale_s;
853
  
854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881
  /* 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);
882

883 884 885
  if (size*SUBSAMPLE < 1.0) size = 1.0/SUBSAMPLE;

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

Raph Levien's avatar
Raph Levien committed
887 888 889 890
  /* 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 */

891
  tscale = ink_options->tilt_sensitivity * 10.0;
892 893
  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
894 895 896 897 898 899 900 901
  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
902 903 904 905 906 907 908 909 910 911 912 913
  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);
    }
914

915 916 917 918 919 920 921 922
  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;
  
923 924 925
  return ink_options->function (x_center * SUBSAMPLE, y_center * SUBSAMPLE,
				radmin*aspect*tcos, radmin*aspect*tsin,  
				-radmin*tsin, radmin*tcos);
926 927 928
}

static void
929
ink_button_press (GimpTool       *tool,
930
		  GdkEventButton *bevent,
Michael Natterer's avatar
Michael Natterer committed
931
		  GimpDisplay    *gdisp)
932
{
Michael Natterer's avatar
Michael Natterer committed
933 934 935 936 937
  GimpInkTool      *ink_tool;
  GimpDisplayShell *shell;
  GimpDrawable     *drawable;
  Blob             *b;
  gdouble           x, y;
938

939
  ink_tool = GIMP_INK_TOOL (tool);
940

Michael Natterer's avatar
Michael Natterer committed
941 942
  shell = GIMP_DISPLAY_SHELL (gdisp->shell);

943 944 945
  /*  Keep the coordinates of the target  */
  gdisplay_untransform_coords_f (gdisp, bevent->x, bevent->y,
				 &x, &y, TRUE);
946
  drawable = gimp_image_active_drawable (gdisp->gimage);
947 948 949

  ink_init (ink_tool, drawable, x, y);

950 951
  tool->state        = ACTIVE;
  tool->gdisp        = gdisp;
952 953 954
  tool->paused_count = 0;

  /*  pause the current selection and grab the pointer  */
955
  gimp_image_selection_control (gdisp->gimage, GIMP_SELECTION_PAUSE);
956

957
  /* add motion memory if you press mod1 first ^ perfectmouse */
958
  if (((bevent->state & GDK_MOD1_MASK) != 0) != (gimprc.perfectmouse != 0))
Michael Natterer's avatar
Michael Natterer committed
959 960 961
    gdk_pointer_grab (shell->canvas->window, FALSE,
		      GDK_BUTTON1_MOTION_MASK |
                      GDK_BUTTON_RELEASE_MASK,
962 963
		      NULL, NULL, bevent->time);
  else
Michael Natterer's avatar
Michael Natterer committed
964 965 966
    gdk_pointer_grab (shell->canvas->window, FALSE,
		      GDK_POINTER_MOTION_HINT_MASK |
                      GDK_BUTTON1_MOTION_MASK |
967
		      GDK_BUTTON_RELEASE_MASK,
968 969
		      NULL, NULL, bevent->time);
  
970
  tool->gdisp = gdisp;
971
  tool->state = ACTIVE;
Hans Breuer's avatar
Hans Breuer committed
972
#ifdef __GNUC__
973
#warning FIXME: presure, tilt
Hans Breuer's avatar
Hans Breuer committed
974
#endif
975
  b = ink_pen_ellipse (x, y,
976
		       1.0, 0.5, 0.5,
977 978
		       /* bevent->pressure, bevent->xtilt, bevent->ytilt, */
		       10.0);
979 980