gimpink.c 45.3 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
static void         ink_options_reset   (GimpToolOptions *tool_options);

128 129 130
static void        ink_control          (GimpTool        *tool,
					 ToolAction       tool_action,
					 GimpDisplay     *gdisp);
131
static void        ink_button_press     (GimpTool        *tool,
132 133 134
                                         GimpCoords      *coords,
                                         guint32          time,
					 GdkModifierType  state,
Michael Natterer's avatar
Michael Natterer committed
135
					 GimpDisplay     *gdisp);
136
static void        ink_button_release   (GimpTool        *tool,
137 138 139
                                         GimpCoords      *coords,
                                         guint32          time,
					 GdkModifierType  state,
Michael Natterer's avatar
Michael Natterer committed
140
					 GimpDisplay     *gdisp);
141
static void        ink_motion           (GimpTool        *tool,
142 143 144
                                         GimpCoords      *coords,
                                         guint32          time,
					 GdkModifierType  state,
Michael Natterer's avatar
Michael Natterer committed
145
					 GimpDisplay     *gdisp);
146
static void        ink_cursor_update    (GimpTool        *tool,
147 148
                                         GimpCoords      *coords,
					 GdkModifierType  state,
Michael Natterer's avatar
Michael Natterer committed
149
					 GimpDisplay     *gdisp);
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167

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);
168 169
static void        ink_cleanup          (void);

170 171 172 173 174 175 176 177
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);
178

Michael Natterer's avatar
Michael Natterer committed
179
/*  Rendering functions  */
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
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);
200

Michael Natterer's avatar
Michael Natterer committed
201
/*  Brush pseudo-widget callbacks  */
202 203
static void   brush_widget_active_rect    (BrushWidget    *brush_widget,
					   GtkWidget      *widget,
204
					   GdkRectangle   *rect);
205 206
static void   brush_widget_realize        (GtkWidget      *widget);
static void   brush_widget_expose         (GtkWidget      *widget,
207 208
					   GdkEventExpose *event,
					   BrushWidget    *brush_widget);
209
static void   brush_widget_button_press   (GtkWidget      *widget,
210 211
					   GdkEventButton *event,
					   BrushWidget    *brush_widget);
212
static void   brush_widget_button_release (GtkWidget      *widget,
213 214
					   GdkEventButton *event,
					   BrushWidget    *brush_widget);
215
static void   brush_widget_motion_notify  (GtkWidget      *widget,
216 217 218 219
					   GdkEventMotion *event,
					   BrushWidget    *brush_widget);


220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
/* 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;

236 237
static GimpToolClass *parent_class      = NULL;

238

239 240
/*  functions  */

241
void
Michael Natterer's avatar
Michael Natterer committed
242
gimp_ink_tool_register (Gimp *gimp)
243
{
Michael Natterer's avatar
Michael Natterer committed
244 245
  tool_manager_register_tool (gimp,
			      GIMP_TYPE_INK_TOOL,
246 247 248 249 250 251
			      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
252
			      GIMP_STOCK_TOOL_INK);
253 254
}

255
GType
256 257
gimp_ink_tool_get_type (void)
{
258
  static GType tool_type = 0;
259 260 261

  if (! tool_type)
    {
262
      static const GTypeInfo tool_info =
263
      {
264 265 266 267 268 269
        sizeof (GimpInkToolClass),
	(GBaseInitFunc) NULL,
	(GBaseFinalizeFunc) NULL,
	(GClassInitFunc) gimp_ink_tool_class_init,
	NULL,           /* class_finalize */
	NULL,           /* class_data     */
270
	sizeof (GimpInkTool),
271 272
	0,              /* n_preallocs    */
	(GInstanceInitFunc) gimp_ink_tool_init,
273 274
      };

275 276 277
      tool_type = g_type_register_static (GIMP_TYPE_TOOL,
					  "GimpInkTool", 
                                          &tool_info, 0);
278 279 280 281 282 283 284 285
    }

  return tool_type;
}

static void
gimp_ink_tool_class_init (GimpInkToolClass *klass)
{
286
  GObjectClass   *object_class;
287 288
  GimpToolClass  *tool_class;

289 290
  object_class = G_OBJECT_CLASS (klass);
  tool_class   = GIMP_TOOL_CLASS (klass);
291

292
  parent_class = g_type_class_peek_parent (klass);
293

294
  object_class->finalize     = gimp_ink_tool_finalize;
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315

  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,
316
					  (GimpToolOptions *) ink_options);
317

318
      ink_options_reset ((GimpToolOptions *) ink_options);
319
    }
320 321

  tool->tool_cursor = GIMP_INK_TOOL_CURSOR;
322 323 324
}

static void
325
gimp_ink_tool_finalize (GObject *object)
326 327 328 329 330 331
{
  GimpInkTool *ink_tool;

  ink_tool = GIMP_INK_TOOL (object);

  if (ink_tool->last_blob)
332 333 334 335
    {
      g_free (ink_tool->last_blob);
      ink_tool->last_blob = NULL;
    }
336 337 338

  ink_cleanup ();

339
  G_OBJECT_CLASS (parent_class)->finalize (object);
340 341
}

342 343 344 345 346 347 348 349 350 351 352 353
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);
}

354
static void
355
ink_options_reset (GimpToolOptions *tool_options)
356
{
357
  InkOptions *options;
358

359 360 361
  options = (InkOptions *) tool_options;

  paint_options_reset (tool_options);
362

363 364 365 366 367 368
  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);
369 370
  gtk_adjustment_set_value (GTK_ADJUSTMENT (options->vel_sensitivity_w),
			    options->vel_sensitivity_d);
371 372
  gtk_adjustment_set_value (GTK_ADJUSTMENT (options->tilt_angle_w),
			    options->tilt_angle_d);
373 374 375 376 377 378
  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);
379 380
  options->aspect = options->aspect_d;
  options->angle  = options->angle_d;
381
  gtk_widget_queue_draw (options->brush_w->widget);
382 383
}

384
static InkOptions *
385
ink_options_new (void)
386
{
387
  InkOptions *options;
388 389 390 391 392 393 394 395 396 397
  GtkWidget  *table;
  GtkWidget  *vbox;
  GtkWidget  *hbox;
  GtkWidget  *hbox2;
  GtkWidget  *radio_button;
  GtkWidget  *pixmap_widget;
  GtkWidget  *slider;
  GtkWidget  *frame;
  GtkWidget  *darea;
  GdkPixmap  *pixmap;
398

399
  options = g_new0 (InkOptions, 1);
400
  paint_options_init ((PaintOptions *) options,
401
		      GIMP_TYPE_INK_TOOL,
402
		      ink_options_reset);
403

404
  options->size             = options->size_d             = 4.4;
405
  options->sensitivity      = options->sensitivity_d      = 1.0;
406 407
  options->vel_sensitivity  = options->vel_sensitivity_d  = 0.8;
  options->tilt_sensitivity = options->tilt_sensitivity_d = 0.4;
408 409 410 411 412
  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
413 414
  /*  the main vbox  */
  vbox = gtk_vbox_new (FALSE, 2);
415
  gtk_box_pack_start (GTK_BOX (((GimpToolOptions *) options)->main_vbox), vbox,
jtl's avatar
jtl committed
416 417 418 419 420 421 422 423 424 425 426 427 428 429
		      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);
430 431 432

  /*  size slider  */
  options->size_w =
433
    gtk_adjustment_new (options->size_d, 0.0, 20.0, 1.0, 2.0, 0.0);
434
  slider = gtk_hscale_new (GTK_ADJUSTMENT (options->size_w));
435
  gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP);
jtl's avatar
jtl committed
436 437 438
  gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
			     _("Size:"), 1.0, 1.0,
			     slider, 1, FALSE);
439
  gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED);
440 441 442 443

  g_signal_connect (G_OBJECT (options->size_w), "value_changed",
		    G_CALLBACK (gimp_double_adjustment_update),
		    &options->size);
jtl's avatar
jtl committed
444 445 446 447 448 449 450 451 452 453

  /* 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);
454 455 456 457

  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
458 459 460 461 462 463 464 465 466 467 468 469 470 471

  /* 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 */
472 473 474
  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));
475
  gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP);
jtl's avatar
jtl committed
476 477 478
  gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
			     _("Size:"), 1.0, 1.0,
			     slider, 1, FALSE);
479
  gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED);
480 481 482 483

  g_signal_connect (G_OBJECT (options->sensitivity_w), "value_changed",
		    G_CALLBACK (gimp_double_adjustment_update),
		    &options->sensitivity);
484
  
485 486 487 488
  /* 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));
489
  gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP);
jtl's avatar
jtl committed
490 491 492
  gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
			     _("Tilt:"), 1.0, 1.0,
			     slider, 1, FALSE);
493
  gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED);
494 495 496 497

  g_signal_connect (G_OBJECT (options->tilt_sensitivity_w), "value_changed",
		    G_CALLBACK (gimp_double_adjustment_update),
		    &options->tilt_sensitivity);
498 499 500 501 502 503

  /* 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
504 505 506
  gimp_table_attach_aligned (GTK_TABLE (table), 0, 3,
			     _("Speed:"), 1.0, 1.0,
			     slider, 1, FALSE);
507
  gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED);
508 509 510 511

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

jtl's avatar
jtl committed
513 514 515 516
  /*  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
517

518
  /* Brush type radiobuttons */
519
  frame = gtk_frame_new (_("Type"));
jtl's avatar
jtl committed
520
  gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
521 522
  gtk_widget_show (frame);

jtl's avatar
jtl committed
523 524 525
  hbox2 = gtk_hbox_new (FALSE, 0);
  gtk_container_add (GTK_CONTAINER (frame), hbox2);
  
526 527
  vbox = gtk_vbox_new (FALSE, 2);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 2);
jtl's avatar
jtl committed
528
  gtk_box_pack_start (GTK_BOX (hbox2), vbox, FALSE, FALSE, 0);
529 530 531 532
  gtk_widget_show (vbox);

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

  pixmap_widget = gtk_pixmap_new (pixmap, NULL);
Sven Neumann's avatar
Sven Neumann committed
536
  gdk_drawable_unref (pixmap);
jtl's avatar
jtl committed
537
  gtk_misc_set_padding (GTK_MISC (pixmap_widget), 6, 0);
538 539 540

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

543 544 545 546 547
  g_signal_connect (G_OBJECT (radio_button), "toggled",
		    G_CALLBACK (ink_type_update),
		    (gpointer) blob_ellipse);


548
  options->function_w[0] = radio_button;
549 550 551

  pixmap = blob_pixmap (gtk_widget_get_colormap (vbox),
			gtk_widget_get_visual (vbox),
552 553 554
			blob_square);

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

558 559
  radio_button =
    gtk_radio_button_new_from_widget (GTK_RADIO_BUTTON (radio_button));
560
  gtk_container_add (GTK_CONTAINER (radio_button), pixmap_widget);
561
  gtk_box_pack_start (GTK_BOX (vbox), radio_button, FALSE, FALSE, 0);
562

563 564 565 566 567
  g_signal_connect (G_OBJECT (radio_button), "toggled",
		    G_CALLBACK (ink_type_update), 
		    (gpointer) blob_square);
  

568 569
  options->function_w[1] = radio_button;

570 571
  pixmap = blob_pixmap (gtk_widget_get_colormap (vbox),
			gtk_widget_get_visual (vbox),
572 573 574
			blob_diamond);

  pixmap_widget = gtk_pixmap_new (pixmap, NULL);
Sven Neumann's avatar
Sven Neumann committed
575
  gdk_drawable_unref (pixmap);
jtl's avatar
jtl committed
576
  gtk_misc_set_padding (GTK_MISC (pixmap_widget), 6, 0);
577

578 579
  radio_button =
    gtk_radio_button_new_from_widget (GTK_RADIO_BUTTON (radio_button));
580
  gtk_container_add (GTK_CONTAINER (radio_button), pixmap_widget);
581
  gtk_box_pack_start (GTK_BOX (vbox), radio_button, FALSE, FALSE, 0);
582

583 584 585 586 587
  g_signal_connect (G_OBJECT (radio_button), "toggled",
		    G_CALLBACK (ink_type_update), 
		    (gpointer) blob_diamond);


588 589
  options->function_w[2] = radio_button;

590
  /* Brush shape widget */
591
  frame = gtk_frame_new (_("Shape"));
jtl's avatar
jtl committed
592
  gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
593 594 595 596 597 598
  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);
599

jtl's avatar
jtl committed
600
  frame = gtk_aspect_frame_new (NULL, 0.0, 0.5, 1.0, FALSE);
601 602
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
  gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
603

jtl's avatar
jtl committed
604 605 606
  options->brush_w = g_new (BrushWidget, 1);
  options->brush_w->state = FALSE;

607
  darea = gtk_drawing_area_new();
608
  options->brush_w->widget = darea;
609

610 611
  gtk_drawing_area_size (GTK_DRAWING_AREA (darea), 60, 60);
  gtk_container_add (GTK_CONTAINER (frame), darea);
612

613 614 615
  gtk_widget_set_events (darea, 
			 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
			 | GDK_POINTER_MOTION_MASK | GDK_EXPOSURE_MASK);
616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631

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

jtl's avatar
jtl committed
633
  gtk_widget_show_all (hbox);
634 635 636 637

  return options;
}

638 639 640

/*  the brush widget functions  */

641
static void
642 643
brush_widget_active_rect (BrushWidget  *brush_widget,
			  GtkWidget    *widget,
644 645 646 647 648
			  GdkRectangle *rect)
{
  int x,y;
  int r;

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

651
  x = widget->allocation.width / 2 + 0.85 * r * ink_options->aspect / 10.0 *
652
    cos (ink_options->angle);
653
  y = widget->allocation.height / 2 + 0.85 * r * ink_options->aspect / 10.0 *
654 655
    sin (ink_options->angle);

656 657
  rect->x = x - 5;
  rect->y = y - 5;
658 659 660 661 662
  rect->width = 10;
  rect->height = 10;
}

static void
663
brush_widget_realize (GtkWidget *widget)
664
{
665
  gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
666 667 668
}

static void
669 670 671 672 673
brush_widget_draw_brush (BrushWidget *brush_widget,
			 GtkWidget   *widget,
			 gdouble      xc,
			 gdouble      yc,
			 gdouble      radius)
674
{
675 676 677 678 679 680 681 682 683 684 685 686
  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);
687 688 689
}

static void
690 691 692
brush_widget_expose (GtkWidget      *widget,
		     GdkEventExpose *event,
		     BrushWidget    *brush_widget)
693 694 695
{
  GdkRectangle rect;
  int r0;
696 697

  r0 = MIN (widget->allocation.width, widget->allocation.height) / 2;
698 699 700 701

  if (r0 < 2)
    return;

702 703 704 705 706 707 708
  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);
709

710 711
  brush_widget_active_rect (brush_widget, widget, &rect);
  gdk_draw_rectangle (widget->window, widget->style->bg_gc[GTK_STATE_NORMAL],
712 713 714
		      TRUE,	/* filled */
		      rect.x, rect.y, 
		      rect.width, rect.height);
715
  gtk_draw_shadow (widget->style, widget->window, widget->state, GTK_SHADOW_OUT,
716 717 718 719 720
		   rect.x, rect.y,
		   rect.width, rect.height);
}

static void
721 722 723
brush_widget_button_press (GtkWidget      *widget,
			   GdkEventButton *event,
			   BrushWidget    *brush_widget)
724 725 726
{
  GdkRectangle rect;

727
  brush_widget_active_rect (brush_widget, widget, &rect);
728 729 730 731 732
  
  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;
733

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

static void
739 740 741
brush_widget_button_release (GtkWidget      *widget,
			     GdkEventButton *event,
			     BrushWidget    *brush_widget)
742 743
{
  brush_widget->state = FALSE;
744

745
  gtk_grab_remove (brush_widget->widget);
746 747 748
}

static void
749 750 751
brush_widget_motion_notify (GtkWidget      *widget,
			    GdkEventMotion *event,
			    BrushWidget    *brush_widget)
752 753 754 755 756 757 758 759
{
  int x;
  int y;
  int r0;
  int rsquare;

  if (brush_widget->state)
    {
760 761
      x = event->x - widget->allocation.width / 2;
      y = event->y - widget->allocation.height / 2;
762 763 764 765
      rsquare = x*x + y*y;

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

768
	  r0 = MIN (widget->allocation.width, widget->allocation.height) / 2;
769
	  ink_options->aspect =
770
	    10.0 * sqrt ((double) rsquare / (r0 * r0)) / 0.85;
771 772 773 774 775 776

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

777
	  gtk_widget_draw (widget, NULL);
778 779 780 781
	}
    }
}

782 783 784 785 786 787
/*
 * Return a black-on white pixmap in the given colormap and
 * visual that represents the BlobFunc 'function'
 */
static GdkPixmap *
blob_pixmap (GdkColormap *colormap,
788 789
	     GdkVisual   *visual,
	     BlobFunc     function)
790 791
{
  GdkPixmap *pixmap;
792 793
  GdkGC     *black_gc, *white_gc;
  GdkColor   tmp_color;
794
  gboolean   success;
795
  Blob      *blob;
796 797 798

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

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

804 805 806
  black_gc = gdk_gc_new (pixmap);
  gdk_gc_set_foreground (black_gc, &tmp_color);

807 808 809 810 811
  tmp_color.red   = 255;
  tmp_color.green = 255;
  tmp_color.blue  = 255;
  gdk_colormap_alloc_colors (colormap, &tmp_color, 1, FALSE, TRUE, &success);

812 813 814 815 816 817 818
  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
819
  g_free (blob);
820 821 822 823 824 825 826 827 828 829 830

  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
831 832 833
paint_blob (GdkDrawable *drawable,
	    GdkGC       *gc,
	    Blob        *blob)
834 835 836 837 838 839 840 841 842 843 844 845
{
  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);
    }
}

846

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

892 893 894
  if (size*SUBSAMPLE < 1.0) size = 1.0/SUBSAMPLE;

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

Raph Levien's avatar
Raph Levien committed
896 897 898 899
  /* 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 */

900
  tscale = ink_options->tilt_sensitivity * 10.0;
901 902
  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
903 904 905 906 907 908 909 910
  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
911 912 913 914 915 916 917 918 919 920 921 922
  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);
    }
923

924 925 926 927 928 929 930 931
  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;
  
932 933 934
  return ink_options->function (x_center * SUBSAMPLE, y_center * SUBSAMPLE,
				radmin*aspect*tcos, radmin*aspect*tsin,  
				-radmin*tsin, radmin*tcos);
935 936 937
}

static void
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 964 965 966 967 968
ink_control (GimpTool    *tool,
	     ToolAction   action,
	     GimpDisplay *gdisp)
{
  GimpInkTool *ink_tool;

  ink_tool = GIMP_INK_TOOL (tool);

  switch (action)
    {
    case PAUSE:
      break;

    case RESUME:
      break;

    case HALT:
      ink_cleanup ();
      break;

    default:
      break;
    }
}

static void
ink_button_press (GimpTool        *tool,
                  GimpCoords      *coords,
                  guint32          time,
		  GdkModifierType  state,
		  GimpDisplay     *gdisp)
969
{
Michael Natterer's avatar
Michael Natterer committed
970 971 972 973
  GimpInkTool      *ink_tool;
  GimpDisplayShell *shell;
  GimpDrawable     *drawable;
  Blob             *b;
974

975
  ink_tool = GIMP_INK_TOOL (tool);
976

Michael Natterer's avatar
Michael Natterer committed
977 978
  shell = GIMP_DISPLAY_SHELL (gdisp->shell);

979
  drawable = gimp_image_active_drawable (gdisp->gimage);
980

981
  ink_init (ink_tool, drawable, coords->x, coords->y);
982

983 984
  tool->state        = ACTIVE;
  tool->gdisp        = gdisp;
985 986 987
  tool->paused_count = 0;

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

990
  /* add motion memory if you press mod1 first ^ perfectmouse */
991 992 993 994 995 996 997
  if (((state & GDK_MOD1_MASK) != 0) != (gimprc.perfectmouse != 0))
    {
      gdk_pointer_grab (shell->canvas->window, FALSE,
                        GDK_BUTTON1_MOTION_MASK |
                        GDK_BUTTON_RELEASE_MASK,
                        NULL, NULL, time);
    }
998
  else
999 1000 1001 1002 1003 1004 1005
    {
      gdk_pointer_grab (shell->canvas->window, FALSE,
                        GDK_POINTER_MOTION_HINT_MASK |
                        GDK_BUTTON1_MOTION_MASK |
                        GDK_BUTTON_RELEASE_MASK,
                        NULL, NULL, time);
    }
1006
  
1007 1008 1009 1010 1011
  b = ink_pen_ellipse (coords->x,
                       coords->y,
		       coords->pressure,
                       coords->xtilt,
                       coords->ytilt,
1012
		       10.0);
1013 1014 1015 1016

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

1017 1018
  time_smoother_init (ink_tool, time);
  ink_tool->last_time = time;
1019 1020
  dist_smoother_init (ink_tool, 0.0);
  ink_tool->init_velocity = TRUE;
1021 1022
  ink_tool->lastx = coords->x;
  ink_tool->lasty = coords->y;
1023

1024
  gimp_display_flush_now (gdisp);
1025 1026 1027
}

static void
1028 1029 1030 1031 1032
ink_button_release (GimpTool        *tool,
                    GimpCoords      *coords,
                    guint32          time,
		    GdkModifierType  state,
		    GimpDisplay     *gdisp)
1033
{
1034 1035
  GimpInkTool *ink_tool;
  GimpImage   *gimage;
1036

1037 1038 1039
  ink_tool = GIMP_INK_TOOL (tool);