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

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

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

56 57
#include "libgimp/gimpintl.h"

58

59
#define SUBSAMPLE 8
60

61 62
/*  the Ink structures  */

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

70

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

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

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

81 82
struct _InkOptions
{
83
  PaintOptions  paint_options;
84

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

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

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

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

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

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

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


117
/*  local function prototypes  */
118

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

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

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

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

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);
160 161
static void        ink_cleanup          (void);

162 163 164 165 166 167 168 169
static void        ink_type_update      (GtkWidget       *radio_button,
					 BlobFunc         function);
static GdkPixmap * blob_pixmap          (GdkColormap     *colormap,
					 GdkVisual       *visual,
					 BlobFunc         function);
static void        paint_blob           (GdkDrawable     *drawable, 
					 GdkGC           *gc,
					 Blob            *blob);
170

Michael Natterer's avatar
Michael Natterer committed
171
/*  Rendering functions  */
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
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);
192

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


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

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

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

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

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

228 229
static GimpToolClass *parent_class      = NULL;

230

231 232
/*  functions  */

233
void
Michael Natterer's avatar
Michael Natterer committed
234
gimp_ink_tool_register (Gimp *gimp)
235
{
Michael Natterer's avatar
Michael Natterer committed
236 237
  tool_manager_register_tool (gimp,
			      GIMP_TYPE_INK_TOOL,
238 239 240 241 242 243
			      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
244
			      GIMP_STOCK_TOOL_INK);
245 246
}

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

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

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

  return tool_type;
}

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

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

284
  parent_class = g_type_class_peek_parent (klass);
285

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

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

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

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

  ink_tool = GIMP_INK_TOOL (object);

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

  ink_cleanup ();

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

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

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

349 350 351
  options = (InkOptions *) tool_options;

  paint_options_reset (tool_options);
352

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

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

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

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

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

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

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

  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
448 449 450 451 452 453 454 455 456 457 458 459 460 461

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

  return options;
}

628 629 630

/*  the brush widget functions  */

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

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

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

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

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

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

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

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

  if (r0 < 2)
    return;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

836

837
static Blob *
838 839 840 841 842
ink_pen_ellipse (gdouble x_center,
		 gdouble y_center,
		 gdouble pressure,
		 gdouble xtilt,
		 gdouble ytilt,
843
		 gdouble velocity)
844 845 846 847 848
{
  double size;
  double tsin, tcos;
  double aspect, radmin;
  double x,y;
849
  double tscale;
Raph Levien's avatar
Raph Levien committed
850 851
  double tscale_c;
  double tscale_s;
852
  
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
  /* 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);
881

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

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

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

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

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

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

937
  ink_tool = GIMP_INK_TOOL (tool);
938 939 940 941

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

  ink_init (ink_tool, drawable, x, y);

946 947
  tool->state        = ACTIVE;
  tool->gdisp        = gdisp;
948 949 950
  tool->paused_count = 0;

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

953
  /* add motion memory if you press mod1 first ^ perfectmouse */
954
  if (((bevent->state & GDK_MOD1_MASK) != 0) != (gimprc.perfectmouse != 0))
955 956 957 958 959
    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,
960 961
		      GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON1_MOTION_MASK |
		      GDK_BUTTON_RELEASE_MASK,
962 963
		      NULL, NULL, bevent->time);
  
964
  tool->gdisp = gdisp;
965
  tool->state = ACTIVE;
Hans Breuer's avatar
Hans Breuer committed
966
#ifdef __GNUC__
967
#warning FIXME: presure, tilt
Hans Breuer's avatar
Hans Breuer committed
968
#endif
969
  b = ink_pen_ellipse (x, y,
970
		       1.0, 0.5, 0.5,
971 972
		       /* bevent->pressure, bevent->xtilt, bevent->ytilt, */
		       10.0);
973 974 975 976

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

977 978 979 980 981 982 983
  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;

984
  gdisplay_flush_now (gdisp);
985 986 987
}

static void
988
ink_button_release (GimpTool       *tool,
989
		    GdkEventButton *bevent,
Michael Natterer's avatar
Michael Natterer committed
990
		    GimpDisplay    *gdisp)
991
{
992 993
  GimpInkTool *ink_tool;
  GimpImage   *gimage;
994

995 996 997
  ink_tool = GIMP_INK_TOOL (tool);

  gimage = gdisp->gimage;
998 999

  /*  resume the current selection and ungrab the pointer  */
1000
  gimp_image_selection_control (gdisp->gimage, GIMP_SELECTION_RESUME);
1001 1002 1003 1004 1005 1006 1007

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

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

1008 1009 1010 1011
  /*  free the last blob  */
  g_free (ink_tool->last_blob);
  ink_tool->last_blob = NULL;

1012
  ink_finish (ink_tool, gimp_image_active_drawable (gdisp->gimage));
1013 1014