gimpink.c 47 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
#include "core/gimpcontext.h"
#include "core/gimpdrawable.h"
#include "core/gimpimage.h"
#include "core/gimpimage-mask.h"
43
#include "core/gimptoolinfo.h"
44

45
#include "display/gimpdisplay.h"
46
#include "display/gimpdisplay-foreach.h"
47

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 56
#include "gimprc.h"
#include "undo.h"

57 58
#include "libgimp/gimpintl.h"

59

60
#define SUBSAMPLE 8
61

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

69

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

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

78 79 80
  /* EEK */
  InkOptions *ink_options;
};
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 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
static void        gimp_ink_tool_control         (GimpTool         *tool,
                                                  ToolAction        tool_action,
                                                  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);

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

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

175 176 177 178 179 180 181 182
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);
183

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

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

224 225 226 227
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);
228

229

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

243
static GimpToolClass *parent_class = NULL;
244

245

246
/*  public functions  */
247

248
void
249 250
gimp_ink_tool_register (Gimp                     *gimp,
                        GimpToolRegisterCallback  callback)
251
{
252 253 254 255 256 257 258 259 260 261
  (* callback) (gimp,
                GIMP_TYPE_INK_TOOL,
                ink_options_new,
                TRUE,
                "gimp:ink_tool",
                _("Ink Tool"),
                _("Draw in ink"),
                N_("/Tools/Paint Tools/Ink"), "K",
                NULL, "tools/ink.html",
                GIMP_STOCK_TOOL_INK);
262 263
}

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

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

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

  return tool_type;
}

292 293 294

/*  private functions  */

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

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

304
  parent_class = g_type_class_peek_parent (klass);
305

306
  object_class->finalize     = gimp_ink_tool_finalize;
307

308 309 310 311 312
  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;
313 314 315 316 317 318 319 320
}

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

  tool = GIMP_TOOL (ink_tool);
321

322 323
  tool->perfectmouse = TRUE;
  tool->tool_cursor  = GIMP_INK_TOOL_CURSOR;
324 325 326
}

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

  ink_tool = GIMP_INK_TOOL (object);

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

  ink_cleanup ();

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

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

351
  ink_tool = GIMP_INK_TOOL (tool);
352

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

358 359
    case RESUME:
      break;
360

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

365 366 367
    default:
      break;
    }
368 369
}

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

382
  ink_tool = GIMP_INK_TOOL (tool);
383

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

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

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

390 391 392
  tool->state        = ACTIVE;
  tool->gdisp        = gdisp;
  tool->paused_count = 0;
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 436
  /*  Set tool state to inactive -- no longer painting */
  tool->state = INACTIVE;
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 443 444
  ink_finish (ink_tool, gimp_image_active_drawable (gdisp->gimage));
  gdisplays_flush ();
}
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 557 558
  tool->cursor = ctype;

  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 void
613 614 615
brush_widget_expose (GtkWidget      *widget,
		     GdkEventExpose *event,
		     BrushWidget    *brush_widget)
616 617 618
{
  GdkRectangle rect;
  int r0;
619 620

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

  if (r0 < 2)
    return;

625 626 627 628 629 630 631
  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);
632

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

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

652
  brush_widget_active_rect (brush_widget, widget, &rect);
653 654 655 656 657
  
  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;
658

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

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

670
  gtk_grab_remove (brush_widget->widget);
671 672 673
}

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

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

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

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

697 698 699 700
	  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;
701

702
	  gtk_widget_queue_draw (widget);
703 704 705 706
	}
    }
}

707 708 709 710 711 712
/*
 * Return a black-on white pixmap in the given colormap and
 * visual that represents the BlobFunc 'function'
 */
static GdkPixmap *
blob_pixmap (GdkColormap *colormap,
713 714
	     GdkVisual   *visual,
	     BlobFunc     function)
715 716
{
  GdkPixmap *pixmap;
717 718
  GdkGC     *black_gc, *white_gc;
  GdkColor   tmp_color;
719
  gboolean   success;
720
  Blob      *blob;
721 722 723

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

724 725 726 727 728
  tmp_color.red   = 0;
  tmp_color.green = 0;
  tmp_color.blue  = 0;
  gdk_colormap_alloc_colors (colormap, &tmp_color, 1, FALSE, TRUE, &success);

729 730 731
  black_gc = gdk_gc_new (pixmap);
  gdk_gc_set_foreground (black_gc, &tmp_color);

732 733 734 735 736
  tmp_color.red   = 255;
  tmp_color.green = 255;
  tmp_color.blue  = 255;
  gdk_colormap_alloc_colors (colormap, &tmp_color, 1, FALSE, TRUE, &success);

737 738 739 740 741 742 743
  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
744
  g_free (blob);
745

746 747
  g_object_unref (white_gc);
  g_object_unref (black_gc);
748 749 750 751 752 753 754 755

  return pixmap;
}

/*
 * Draw a blob onto a drawable with the specified graphics context
 */
static void
756 757 758
paint_blob (GdkDrawable *drawable,
	    GdkGC       *gc,
	    Blob        *blob)
759 760 761 762 763 764 765 766 767 768 769 770
{
  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);
    }
}

771

772
static Blob *
773 774 775 776 777 778 779
ink_pen_ellipse (InkOptions *options,
                 gdouble     x_center,
		 gdouble     y_center,
		 gdouble     pressure,
		 gdouble     xtilt,
		 gdouble     ytilt,
		 gdouble     velocity)
780
{
781 782 783 784 785 786 787 788
  gdouble size;
  gdouble tsin, tcos;
  gdouble aspect, radmin;
  gdouble x,y;
  gdouble tscale;
  gdouble tscale_c;
  gdouble tscale_s;

789 790
  /* Adjust the size depending on pressure. */

791 792
  size = options->size * (1.0 + options->sensitivity *
                          (2.0 * pressure - 1.0) );
793 794 795 796 797 798 799 800 801 802 803 804

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

805 806 807
  size = options->vel_sensitivity *
    ((4.5 * size) / (1.0 + options->vel_sensitivity * (2.0*(velocity))))
    + (1.0 - options->vel_sensitivity) * size;
808 809 810 811 812 813 814

#ifdef VERBOSE
  g_print("%f\n", (float)size);
#endif

  /* Clamp resulting size to sane limits */

815 816
  if (size > options->size * (1.0 + options->sensitivity))
    size = options->size * (1.0 + options->sensitivity);
817

818 819 820
  if (size*SUBSAMPLE < 1.0) size = 1.0/SUBSAMPLE;

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

Raph Levien's avatar
Raph Levien committed
822 823 824 825
  /* 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 */

826 827 828 829 830 831 832 833 834
  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
835 836
#ifdef VERBOSE
  g_print ("angle %g aspect %g; %g %g; %g %g\n",
837
	   options->angle, options->aspect, tscale_c, tscale_s, x, y);
Raph Levien's avatar
Raph Levien committed
838
#endif
839 840 841 842 843 844 845 846 847
  aspect = sqrt(x*x+y*y);

  if (aspect != 0)
    {
      tcos = x/aspect;
      tsin = y/aspect;
    }
  else
    {
848 849
      tsin = sin (options->angle);
      tcos = cos (options->angle);
850
    }
851

852 853 854 855 856 857 858 859
  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;
  
860 861 862 863 864 865
  return options->function (x_center * SUBSAMPLE,
                            y_center * SUBSAMPLE,
                            radmin * aspect * tcos,
                            radmin * aspect * tsin,  
                            -radmin * tsin,
                            radmin * tcos);
866 867 868
}

static void
869 870
dist_smoother_init (GimpInkTool *ink_tool,
		    gdouble      initval)
871
{
872
  gint i;
873

874
  ink_tool->dt_index = 0;
875

876
  for (i=0; i<DIST_SMOOTHER_BUFFER; i++)
877
    {
878 879 880
      ink_tool->dt_buffer[i] = initval;
    }
}
881

882 883 884 885 886
static gdouble
dist_smoother_result (GimpInkTool *ink_tool)
{
  gint    i;
  gdouble result = 0.0;
887

888 889 890
  for (i = 0; i < DIST_SMOOTHER_BUFFER; i++)
    {
      result += ink_tool->dt_buffer[i];
891
    }
892 893

  return (result / (gdouble) DIST_SMOOTHER_BUFFER);
894 895 896
}

static void
897 898
dist_smoother_add (GimpInkTool *ink_tool,
		   gdouble      value)
899
{
900
  ink_tool->dt_buffer[ink_tool->dt_index] = value;
Michael Natterer's avatar
Michael Natterer committed
901

902 903 904
  if ((++ink_tool->dt_index) == DIST_SMOOTHER_BUFFER)
    ink_tool->dt_index = 0;
}
905 906


907 908 909 910 911
static void
time_smoother_init (GimpInkTool *ink_tool,
		    guint32      initval)
{
  gint i;
912

913
  ink_tool->ts_index = 0;
914

915
  for (i = 0; i < TIME_SMOOTHER_BUFFER; i++)
916
    {
917
      ink_tool->ts_buffer[i] = initval;
918
    }
919 920 921 922 923 924 925 926 927
}

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

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

932 933 934 935 936 937
#ifdef _MSC_VER
  return (gdouble) (gint64) (result / TIME_SMOOTHER_BUFFER);
#else
  return (result / TIME_SMOOTHER_BUFFER);
#endif
}
938 939

static void
940 941
time_smoother_add (GimpInkTool *ink_tool,
		   guint32      value)
942 943 944 945 946 947 948
{
  ink_tool->ts_buffer[ink_tool->ts_index] = value;

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

949
static void
950
ink_init (GimpInkTool  *ink_tool,
951 952 953
	  GimpDrawable *drawable, 
	  gdouble       x,
	  gdouble       y)
954 955 956 957 958 959 960 961
{
  /*  free the block structures  */
  if (undo_tiles)
    tile_manager_destroy (undo_tiles);
  if (canvas_tiles)
    tile_manager_destroy (canvas_tiles);

  /*  Allocate the undo structure  */
962 963 964
  undo_tiles = tile_manager_new (gimp_drawable_width (drawable),
				 gimp_drawable_height (drawable),
				 gimp_drawable_bytes (drawable));
965 966

  /*  Allocate the canvas blocks structure  */
967 968
  canvas_tiles = tile_manager_new (gimp_drawable_width (drawable),
				   gimp_drawable_height (drawable), 1);
969 970 971 972 973 974 975

  /*  Get the initial undo extents  */
  ink_tool->x1 = ink_tool->x2 = x;
  ink_tool->y1 = ink_tool->y2 = y;
}

static void
976 977
ink_finish (GimpInkTool  *ink_tool,
	    GimpDrawable *drawable)
978 979
{
  /*  push an undo  */
Michael Natterer's avatar
Michael Natterer committed
980 981
  gimp_drawable_apply_image (drawable, ink_tool->x1, ink_tool->y1,
			     ink_tool->x2, ink_tool->y2, undo_tiles, TRUE);
982 983 984 985 986
  undo_tiles = NULL;

  /*  invalidate the drawable--have to do it here, because
   *  it is not done during the actual painting.
   */
987
  gimp_viewable_invalidate_preview (GIMP_VIEWABLE (drawable));
988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008
}

static void
ink_cleanup (void)
{
  /*  If the undo tiles exist, nuke them  */
  if (undo_tiles)
    {
      tile_manager_destroy (undo_tiles);
      undo_tiles = NULL;
    }

  /*  If the canvas blocks exist, nuke them  */
  if (canvas_tiles)
    {
      tile_manager_destroy (canvas_tiles);
      canvas_tiles = NULL;
    }

  /*  Free the temporary buffer if it exist  */
  if (canvas_buf)
1009 1010 1011 1012
    {
      temp_buf_free (canvas_buf);
      canvas_buf = NULL;
    }
1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028
}

/*********************************
 *  Rendering functions          *
 *********************************/

/* Some of this stuff should probably be combined with the 
 * code it was copied from in paint_core.c; but I wanted
 * to learn this stuff, so I've kept it simple.
 *
 * The following only supports CONSTANT mode. Incremental
 * would, I think, interact strangely with the way we
 * do things. But it wouldn't be hard to implement at all.
 */

static void
1029
ink_set_paint_area (GimpInkTool  *ink_tool, 
1030 1031 1032
		    GimpDrawable *drawable, 
		    Blob         *blob)
{