gimpink.c 46 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 "libgimpwidgets/gimpwidgets.h"
27

28
#include "tools-types.h"
Sven Neumann's avatar
Sven Neumann committed
29

Michael Natterer's avatar
Michael Natterer committed
30 31 32 33 34
#include "base/pixel-region.h"
#include "base/temp-buf.h"
#include "base/tile.h"
#include "base/tile-manager.h"

35 36
#include "paint-funcs/paint-funcs.h"

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

44 45
#include "paint/gimppaintoptions.h"

46 47
#include "display/gimpdisplay.h"

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

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

56 57
#include "libgimp/gimpintl.h"

58

59
#define SUBSAMPLE 8
60

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

68

69
typedef struct _BrushWidget BrushWidget;
70
typedef struct _InkOptions  InkOptions;
Michael Natterer's avatar
Michael Natterer committed
71

72 73
struct _BrushWidget
{
74 75
  GtkWidget  *widget;
  gboolean    state;
76

77 78 79
  /* EEK */
  InkOptions *ink_options;
};
Michael Natterer's avatar
Michael Natterer committed
80

81 82
struct _InkOptions
{
83
  GimpPaintOptions  paint_options;
84

85 86 87
  gdouble           size;
  gdouble           size_d;
  GtkObject        *size_w;
88

89 90 91
  gdouble           sensitivity;
  gdouble           sensitivity_d;
  GtkObject        *sensitivity_w;
92

93 94 95
  gdouble           vel_sensitivity;
  gdouble           vel_sensitivity_d;
  GtkObject        *vel_sensitivity_w;
96

97 98 99
  gdouble           tilt_sensitivity;
  gdouble           tilt_sensitivity_d;
  GtkObject        *tilt_sensitivity_w;
100

101 102 103
  gdouble           tilt_angle;
  gdouble           tilt_angle_d;
  GtkObject        *tilt_angle_w;
104

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

109 110 111 112 113
  gdouble           aspect;
  gdouble           aspect_d;
  gdouble           angle;
  gdouble           angle_d;
  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 void        gimp_ink_tool_control         (GimpTool         *tool,
125
                                                  GimpToolAction    action,
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
                                                  GimpDisplay      *gdisp);
static void        gimp_ink_tool_button_press    (GimpTool         *tool,
                                                  GimpCoords       *coords,
                                                  guint32           time,
                                                  GdkModifierType   state,
                                                  GimpDisplay      *gdisp);
static void        gimp_ink_tool_button_release  (GimpTool         *tool,
                                                  GimpCoords       *coords,
                                                  guint32           time,
                                                  GdkModifierType   state,
                                                  GimpDisplay      *gdisp);
static void        gimp_ink_tool_motion          (GimpTool         *tool,
                                                  GimpCoords       *coords,
                                                  guint32           time,
                                                  GdkModifierType   state,
                                                  GimpDisplay      *gdisp);
static void        gimp_ink_tool_cursor_update   (GimpTool         *tool,
                                                  GimpCoords       *coords,
                                                  GdkModifierType   state,
                                                  GimpDisplay      *gdisp);

147 148
static Blob *      ink_pen_ellipse      (InkOptions      *options,
                                         gdouble          x_center,
149 150 151 152 153
                                         gdouble          y_center,
                                         gdouble          pressure,
                                         gdouble          xtilt,
                                         gdouble          ytilt,
                                         gdouble          velocity);
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171

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);
172 173
static void        ink_cleanup          (void);

174 175
static void        ink_type_update      (GtkWidget       *radio_button,
					 BlobFunc         function);
176 177 178
static gboolean    blob_button_expose   (GtkWidget       *widget,
                                         GdkEventExpose  *event,
                                         BlobFunc         function);
179 180 181
static void        paint_blob           (GdkDrawable     *drawable, 
					 GdkGC           *gc,
					 Blob            *blob);
182

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

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

223 224 225 226
static GimpToolOptions * ink_options_new   (GimpToolInfo    *tool_info);
static void              ink_options_reset (GimpToolOptions *tool_options);
static void              ink_type_update   (GtkWidget       *radio_button,
                                            BlobFunc         function);
227

228

229 230 231 232 233 234 235 236 237 238 239 240 241
/* local variables */

/*  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;

242
static GimpToolClass *parent_class = NULL;
243

244

245
/*  public functions  */
246

247
void
Nate Summers's avatar
Nate Summers committed
248
gimp_ink_tool_register (GimpToolRegisterCallback  callback,
249
                        gpointer                  data)
250
{
Nate Summers's avatar
Nate Summers committed
251
  (* callback) (GIMP_TYPE_INK_TOOL,
252 253
                ink_options_new,
                TRUE,
254
                "gimp-ink-tool",
255
                _("Ink"),
256 257 258
                _("Draw in ink"),
                N_("/Tools/Paint Tools/Ink"), "K",
                NULL, "tools/ink.html",
Nate Summers's avatar
Nate Summers committed
259
                GIMP_STOCK_TOOL_INK,
260
                data);
261 262
}

263
GType
264 265
gimp_ink_tool_get_type (void)
{
266
  static GType tool_type = 0;
267 268 269

  if (! tool_type)
    {
270
      static const GTypeInfo tool_info =
271
      {
272 273 274 275 276 277
        sizeof (GimpInkToolClass),
	(GBaseInitFunc) NULL,
	(GBaseFinalizeFunc) NULL,
	(GClassInitFunc) gimp_ink_tool_class_init,
	NULL,           /* class_finalize */
	NULL,           /* class_data     */
278
	sizeof (GimpInkTool),
279 280
	0,              /* n_preallocs    */
	(GInstanceInitFunc) gimp_ink_tool_init,
281 282
      };

283 284 285
      tool_type = g_type_register_static (GIMP_TYPE_TOOL,
					  "GimpInkTool", 
                                          &tool_info, 0);
286 287 288 289 290
    }

  return tool_type;
}

291 292 293

/*  private functions  */

294 295 296
static void
gimp_ink_tool_class_init (GimpInkToolClass *klass)
{
297
  GObjectClass   *object_class;
298 299
  GimpToolClass  *tool_class;

300 301
  object_class = G_OBJECT_CLASS (klass);
  tool_class   = GIMP_TOOL_CLASS (klass);
302

303
  parent_class = g_type_class_peek_parent (klass);
304

305
  object_class->finalize     = gimp_ink_tool_finalize;
306

307 308 309 310 311
  tool_class->control        = gimp_ink_tool_control;
  tool_class->button_press   = gimp_ink_tool_button_press;
  tool_class->button_release = gimp_ink_tool_button_release;
  tool_class->motion         = gimp_ink_tool_motion;
  tool_class->cursor_update  = gimp_ink_tool_cursor_update;
312 313 314 315 316 317 318 319
}

static void
gimp_ink_tool_init (GimpInkTool *ink_tool)
{
  GimpTool *tool;

  tool = GIMP_TOOL (ink_tool);
320

321 322
  gimp_tool_control_set_motion_mode (tool->control, GIMP_MOTION_MODE_EXACT);
  gimp_tool_control_set_tool_cursor (tool->control, GIMP_INK_TOOL_CURSOR);
323 324 325
}

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

  ink_tool = GIMP_INK_TOOL (object);

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

  ink_cleanup ();

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

343
static void
344 345 346
gimp_ink_tool_control (GimpTool       *tool,
                       GimpToolAction  action,
                       GimpDisplay    *gdisp)
347
{
348
  GimpInkTool *ink_tool;
349

350
  ink_tool = GIMP_INK_TOOL (tool);
351

352 353 354 355
  switch (action)
    {
    case PAUSE:
      break;
356

357 358
    case RESUME:
      break;
359

360 361 362
    case HALT:
      ink_cleanup ();
      break;
363

364 365 366
    default:
      break;
    }
367 368

  GIMP_TOOL_CLASS (parent_class)->control (tool, action, gdisp);
369 370
}

371 372 373 374 375 376
static void
gimp_ink_tool_button_press (GimpTool        *tool,
                            GimpCoords      *coords,
                            guint32          time,
                            GdkModifierType  state,
                            GimpDisplay     *gdisp)
377
{
378 379 380 381
  GimpInkTool  *ink_tool;
  InkOptions   *options;
  GimpDrawable *drawable;
  Blob         *b;
382

383
  ink_tool = GIMP_INK_TOOL (tool);
384

385 386
  options = (InkOptions *) tool->tool_info->tool_options;

387
  drawable = gimp_image_active_drawable (gdisp->gimage);
jtl's avatar
jtl committed
388

389
  ink_init (ink_tool, drawable, coords->x, coords->y);
jtl's avatar
jtl committed
390

391
  gimp_tool_control_activate (tool->control);
392
  tool->gdisp = gdisp; 
393

394
  /*  pause the current selection  */
395
  gimp_image_selection_control (gdisp->gimage, GIMP_SELECTION_PAUSE);
396

397 398
  b = ink_pen_ellipse (options,
                       coords->x,
399 400 401 402 403
                       coords->y,
		       coords->pressure,
                       coords->xtilt,
                       coords->ytilt,
		       10.0);
jtl's avatar
jtl committed
404

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

408 409 410 411 412 413
  time_smoother_init (ink_tool, time);
  ink_tool->last_time = time;
  dist_smoother_init (ink_tool, 0.0);
  ink_tool->init_velocity = TRUE;
  ink_tool->lastx = coords->x;
  ink_tool->lasty = coords->y;
jtl's avatar
jtl committed
414

415 416
  gimp_display_flush_now (gdisp);
}
jtl's avatar
jtl committed
417

418 419 420 421 422 423 424 425 426
static void
gimp_ink_tool_button_release (GimpTool        *tool,
                              GimpCoords      *coords,
                              guint32          time,
                              GdkModifierType  state,
                              GimpDisplay     *gdisp)
{
  GimpInkTool *ink_tool;
  GimpImage   *gimage;
jtl's avatar
jtl committed
427

428
  ink_tool = GIMP_INK_TOOL (tool);
429

430
  gimage = gdisp->gimage;
431

432
  /*  resume the current selection  */
433
  gimp_image_selection_control (gdisp->gimage, GIMP_SELECTION_RESUME);
434

435
  /*  Set tool state to inactive -- no longer painting */
436
  gimp_tool_control_halt (tool->control);    /* sets paused_count to 0 -- is this ok? */
437

438 439 440
  /*  free the last blob  */
  g_free (ink_tool->last_blob);
  ink_tool->last_blob = NULL;
Raph Levien's avatar
Raph Levien committed
441

442
  ink_finish (ink_tool, gimp_image_active_drawable (gdisp->gimage));
443
  gimp_image_flush (gdisp->gimage);
444
}
445

446 447 448 449 450 451 452 453
static void
gimp_ink_tool_motion (GimpTool        *tool,
                      GimpCoords      *coords,
                      guint32          time,
                      GdkModifierType  state,
                      GimpDisplay     *gdisp)
{
  GimpInkTool  *ink_tool;
454
  InkOptions   *options;
455 456 457 458 459 460
  GimpDrawable *drawable;
  Blob         *b, *blob_union;

  gdouble velocity;
  gdouble dist;
  gdouble lasttime, thistime;
461

462
  ink_tool = GIMP_INK_TOOL (tool);
463

464 465
  options = (InkOptions *) tool->tool_info->tool_options;

466
  drawable = gimp_image_active_drawable (gdisp->gimage);
467

468
  lasttime = ink_tool->last_time;
469

470 471
  time_smoother_add (ink_tool, time);
  thistime = ink_tool->last_time = time_smoother_result (ink_tool);
472

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

480 481
  if (thistime == lasttime)
    thistime = lasttime + 1;
482

483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498
  if (ink_tool->init_velocity)
    {
      dist_smoother_init (ink_tool,
			  dist = sqrt ((ink_tool->lastx - coords->x) *
                                       (ink_tool->lastx - coords->x) +
				       (ink_tool->lasty - coords->y) *
                                       (ink_tool->lasty - coords->y)));
      ink_tool->init_velocity = FALSE;
    }
  else
    {
      dist_smoother_add (ink_tool,
			 sqrt ((ink_tool->lastx - coords->x) *
                               (ink_tool->lastx - coords->x) +
			       (ink_tool->lasty - coords->y) *
                               (ink_tool->lasty - coords->y)));
499

500 501
      dist = dist_smoother_result (ink_tool);
    }
502

503 504
  ink_tool->lastx = coords->x;
  ink_tool->lasty = coords->y;
505

506
  velocity = 10.0 * sqrt ((dist) / (gdouble) (thistime - lasttime));
507

508 509
  b = ink_pen_ellipse (options,
                       coords->x,
510 511 512 513 514
                       coords->y,
                       coords->pressure,
                       coords->xtilt,
		       coords->ytilt,
                       velocity);
515

516 517 518
  blob_union = blob_convex_union (ink_tool->last_blob, b);
  g_free (ink_tool->last_blob);
  ink_tool->last_blob = b;
519

520 521
  ink_paste (ink_tool, drawable, blob_union);  
  g_free (blob_union);
522

523 524
  gimp_display_flush_now (gdisp);
}
jtl's avatar
jtl committed
525

526 527 528 529 530 531
static void
gimp_ink_tool_cursor_update (GimpTool        *tool,
                             GimpCoords      *coords,
                             GdkModifierType  state,
                             GimpDisplay     *gdisp)
{
532 533
  GimpLayer     *layer;
  GdkCursorType  ctype = GDK_TOP_LEFT_ARROW;
534

535 536 537
  if ((layer = gimp_image_get_active_layer (gdisp->gimage))) 
    {
      gint off_x, off_y;
538

539
      gimp_drawable_offsets (GIMP_DRAWABLE (layer), &off_x, &off_y);
540

541 542 543 544 545 546 547 548
      if (coords->x >= off_x &&
          coords->y >= off_y &&
	  coords->x < (off_x + gimp_drawable_width (GIMP_DRAWABLE (layer))) &&
	  coords->y < (off_y + gimp_drawable_height (GIMP_DRAWABLE (layer))))
	{
	  /*  One more test--is there a selected region?
	   *  if so, is cursor inside?
	   */
549
	  if (gimp_image_mask_is_empty (gdisp->gimage))
550
	    ctype = GIMP_MOUSE_CURSOR;
551
	  else if (gimp_image_mask_value (gdisp->gimage, coords->x, coords->y))
552 553 554
	    ctype = GIMP_MOUSE_CURSOR;
	}
    }
555

556
  gimp_tool_control_set_cursor (tool->control, ctype);
557 558

  GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, gdisp);
559 560
}

561 562 563

/*  the brush widget functions  */

564
static void
565 566
brush_widget_active_rect (BrushWidget  *brush_widget,
			  GtkWidget    *widget,
567 568
			  GdkRectangle *rect)
{
569 570
  gint x,y;
  gint r;
571

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

574 575 576 577
  x = widget->allocation.width / 2 + 0.85 * r * brush_widget->ink_options->aspect / 10.0 *
    cos (brush_widget->ink_options->angle);
  y = widget->allocation.height / 2 + 0.85 * r * brush_widget->ink_options->aspect / 10.0 *
    sin (brush_widget->ink_options->angle);
578

579 580
  rect->x = x - 5;
  rect->y = y - 5;
581 582 583 584 585
  rect->width = 10;
  rect->height = 10;
}

static void
586
brush_widget_realize (GtkWidget *widget)
587
{
588
  gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
589 590 591
}

static void
592 593 594 595 596
brush_widget_draw_brush (BrushWidget *brush_widget,
			 GtkWidget   *widget,
			 gdouble      xc,
			 gdouble      yc,
			 gdouble      radius)
597
{
598 599
  Blob *blob;

600 601 602 603 604 605 606
  blob = brush_widget->ink_options->function (xc, yc,
                                              radius * cos (brush_widget->ink_options->angle),
                                              radius * sin (brush_widget->ink_options->angle),
                                              (- (radius / brush_widget->ink_options->aspect) *
                                               sin (brush_widget->ink_options->angle)),
                                              ((radius / brush_widget->ink_options->aspect) *
                                               cos (brush_widget->ink_options->angle)));
607 608 609

  paint_blob (widget->window, widget->style->fg_gc[widget->state], blob);
  g_free (blob);
610 611
}

612
static gboolean
613 614 615
brush_widget_expose (GtkWidget      *widget,
		     GdkEventExpose *event,
		     BrushWidget    *brush_widget)
616 617
{
  GdkRectangle rect;
618
  gint         r0;
619 620

  r0 = MIN (widget->allocation.width, widget->allocation.height) / 2;
621 622

  if (r0 < 2)
623
    return TRUE;
624

625
  brush_widget_draw_brush (brush_widget, widget,
626
			   widget->allocation.width  / 2,
627 628
			   widget->allocation.height / 2,
			   0.9 * r0);
629

630 631
  brush_widget_active_rect (brush_widget, widget, &rect);
  gdk_draw_rectangle (widget->window, widget->style->bg_gc[GTK_STATE_NORMAL],
632 633 634
		      TRUE,	/* filled */
		      rect.x, rect.y, 
		      rect.width, rect.height);
635 636 637 638 639
  gtk_paint_shadow (widget->style, widget->window, widget->state,
                    GTK_SHADOW_OUT,
                    NULL, widget, NULL,
                    rect.x, rect.y,
                    rect.width, rect.height);
640 641

  return TRUE;
642 643
}

644
static gboolean
645 646 647
brush_widget_button_press (GtkWidget      *widget,
			   GdkEventButton *event,
			   BrushWidget    *brush_widget)
648 649 650
{
  GdkRectangle rect;

651
  brush_widget_active_rect (brush_widget, widget, &rect);
652

653 654 655 656
  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;
657

658
      gtk_grab_add (brush_widget->widget);
659
    }
660 661

  return TRUE;
662 663
}

664
static gboolean
665 666 667
brush_widget_button_release (GtkWidget      *widget,
			     GdkEventButton *event,
			     BrushWidget    *brush_widget)
668 669
{
  brush_widget->state = FALSE;
670

671
  gtk_grab_remove (brush_widget->widget);
672 673

  return TRUE;
674 675
}

676
static gboolean
677 678 679
brush_widget_motion_notify (GtkWidget      *widget,
			    GdkEventMotion *event,
			    BrushWidget    *brush_widget)
680 681 682 683 684 685 686 687
{
  int x;
  int y;
  int r0;
  int rsquare;

  if (brush_widget->state)
    {
688 689
      x = event->x - widget->allocation.width / 2;
      y = event->y - widget->allocation.height / 2;
690 691 692 693
      rsquare = x*x + y*y;

      if (rsquare != 0)
	{
694
	  brush_widget->ink_options->angle = atan2 (y, x);
695

696
	  r0 = MIN (widget->allocation.width, widget->allocation.height) / 2;
697 698
	  brush_widget->ink_options->aspect =
	    10.0 * sqrt ((gdouble) rsquare / (r0 * r0)) / 0.85;
699

700 701 702 703
	  if (brush_widget->ink_options->aspect < 1.0)
	    brush_widget->ink_options->aspect = 1.0;
	  if (brush_widget->ink_options->aspect > 10.0)
	    brush_widget->ink_options->aspect = 10.0;
704

705
	  gtk_widget_queue_draw (widget);
706 707
	}
    }
708 709

  return TRUE;
710 711
}

712 713 714 715
static gboolean
blob_button_expose (GtkWidget      *widget,
                    GdkEventExpose *event,
                    BlobFunc        function)
716
{
717 718 719
  Blob *blob = (*function) (widget->allocation.width  / 2,
                            widget->allocation.height / 2, 8, 0, 0, 8);
  paint_blob (widget->window, widget->style->fg_gc[widget->state], blob);
Sven Neumann's avatar
Sven Neumann committed
720
  g_free (blob);
721

722
  return TRUE;
723 724 725 726 727 728
}

/*
 * Draw a blob onto a drawable with the specified graphics context
 */
static void
729 730 731
paint_blob (GdkDrawable *drawable,
	    GdkGC       *gc,
	    Blob        *blob)
732
{
733
  gint i;
734

735 736 737 738 739
  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);
740 741
}

742

743
static Blob *
744 745 746 747 748 749 750
ink_pen_ellipse (InkOptions *options,
                 gdouble     x_center,
		 gdouble     y_center,
		 gdouble     pressure,
		 gdouble     xtilt,
		 gdouble     ytilt,
		 gdouble     velocity)
751
{
752 753 754 755 756 757 758 759
  gdouble size;
  gdouble tsin, tcos;
  gdouble aspect, radmin;
  gdouble x,y;
  gdouble tscale;
  gdouble tscale_c;
  gdouble tscale_s;

760 761
  /* Adjust the size depending on pressure. */

762 763
  size = options->size * (1.0 + options->sensitivity *
                          (2.0 * pressure - 1.0) );
764 765 766 767 768 769 770 771 772

  /* 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
773
  g_print ("%f (%f) -> ", (float)size, (float)velocity);
774 775
#endif  

776 777 778
  size = options->vel_sensitivity *
    ((4.5 * size) / (1.0 + options->vel_sensitivity * (2.0*(velocity))))
    + (1.0 - options->vel_sensitivity) * size;
779 780

#ifdef VERBOSE
781
  g_print ("%f\n", (float)size);
782 783 784 785
#endif

  /* Clamp resulting size to sane limits */

786 787
  if (size > options->size * (1.0 + options->sensitivity))
    size = options->size * (1.0 + options->sensitivity);
788

789 790 791
  if (size*SUBSAMPLE < 1.0) size = 1.0/SUBSAMPLE;

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

Raph Levien's avatar
Raph Levien committed
793 794 795 796
  /* 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 */

797 798 799 800 801 802 803 804 805
  tscale = options->tilt_sensitivity * 10.0;
  tscale_c = tscale * cos (gimp_deg_to_rad (options->tilt_angle));
  tscale_s = tscale * sin (gimp_deg_to_rad (options->tilt_angle));

  x = (options->aspect * cos (options->angle) +
       xtilt * tscale_c - ytilt * tscale_s);
  y = (options->aspect * sin (options->angle) +
       ytilt * tscale_c + xtilt * tscale_s);

Raph Levien's avatar
Raph Levien committed
806 807
#ifdef VERBOSE
  g_print ("angle %g aspect %g; %g %g; %g %g\n",
808
	   options->angle, options->aspect, tscale_c, tscale_s, x, y);
Raph Levien's avatar
Raph Levien committed
809
#endif
810 811 812 813 814 815 816 817 818
  aspect = sqrt(x*x+y*y);

  if (aspect != 0)
    {
      tcos = x/aspect;
      tsin = y/aspect;
    }
  else
    {
819 820
      tsin = sin (options->angle);
      tcos = cos (options->angle);
821
    }
822

823 824 825 826 827 828 829 830
  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;
  
831 832 833 834 835 836
  return options->function (x_center * SUBSAMPLE,
                            y_center * SUBSAMPLE,
                            radmin * aspect * tcos,
                            radmin * aspect * tsin,  
                            -radmin * tsin,
                            radmin * tcos);
837 838 839
}

static void
840 841
dist_smoother_init (GimpInkTool *ink_tool,
		    gdouble      initval)
842
{
843
  gint i;
844

845
  ink_tool->dt_index = 0;
846

847
  for (i=0; i<DIST_SMOOTHER_BUFFER; i++)
848
    {
849 850 851
      ink_tool->dt_buffer[i] = initval;
    }
}
852

853 854 855 856 857
static gdouble
dist_smoother_result (GimpInkTool *ink_tool)
{
  gint    i;
  gdouble result = 0.0;
858

859 860 861
  for (i = 0; i < DIST_SMOOTHER_BUFFER; i++)
    {
      result += ink_tool->dt_buffer[i];
862
    }
863 864

  return (result / (gdouble) DIST_SMOOTHER_BUFFER);
865 866 867
}

static void
868 869
dist_smoother_add (GimpInkTool *ink_tool,
		   gdouble      value)
870
{
871
  ink_tool->dt_buffer[ink_tool->dt_index] = value;
Michael Natterer's avatar
Michael Natterer committed
872

873 874 875
  if ((++ink_tool->dt_index) == DIST_SMOOTHER_BUFFER)
    ink_tool->dt_index = 0;
}
876 877


878 879 880 881 882
static void
time_smoother_init (GimpInkTool *ink_tool,
		    guint32      initval)
{
  gint i;
883

884
  ink_tool->ts_index = 0;
885

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