gimpink.c 46.8 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"
Michael Natterer's avatar
Michael Natterer committed
47
#include "display/gimpdisplayshell.h"
48

49 50 51
#include "gimpinktool.h"
#include "gimpinktool-blob.h"
#include "gimptool.h"
52
#include "paint_options.h"
53
#include "tool_manager.h"
54

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

58 59
#include "libgimp/gimpintl.h"

60

61
#define SUBSAMPLE 8
62

63 64
/*  the Ink structures  */

65 66 67 68 69 70 71
typedef Blob * (* BlobFunc) (gdouble,
			     gdouble,
			     gdouble,
			     gdouble,
			     gdouble,
			     gdouble);

72

73
typedef struct _BrushWidget BrushWidget;
Michael Natterer's avatar
Michael Natterer committed
74

75 76
struct _BrushWidget
{
77 78
  GtkWidget *widget;
  gboolean   state;
79 80
};

81
typedef struct _InkOptions InkOptions;
Michael Natterer's avatar
Michael Natterer committed
82

83 84
struct _InkOptions
{
85
  PaintOptions  paint_options;
86

Michael Natterer's avatar
Michael Natterer committed
87 88
  gdouble       size;
  gdouble       size_d;
89
  GtkObject    *size_w;
90

Michael Natterer's avatar
Michael Natterer committed
91 92
  gdouble       sensitivity;
  gdouble       sensitivity_d;
93
  GtkObject    *sensitivity_w;
94

Michael Natterer's avatar
Michael Natterer committed
95 96
  gdouble       vel_sensitivity;
  gdouble       vel_sensitivity_d;
97
  GtkObject    *vel_sensitivity_w;
98

Michael Natterer's avatar
Michael Natterer committed
99 100
  gdouble       tilt_sensitivity;
  gdouble       tilt_sensitivity_d;
101
  GtkObject    *tilt_sensitivity_w;
102

Michael Natterer's avatar
Michael Natterer committed
103 104
  gdouble       tilt_angle;
  gdouble       tilt_angle_d;
105
  GtkObject    *tilt_angle_w;
106

107 108 109
  BlobFunc      function;
  BlobFunc      function_d;
  GtkWidget    *function_w[3];  /* 3 radio buttons */
110

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


119
/*  local function prototypes  */
120

121 122 123
static void        gimp_ink_tool_class_init      (GimpInkToolClass *klass);
static void        gimp_ink_tool_init            (GimpInkTool      *tool);

124
static void        gimp_ink_tool_finalize        (GObject          *object);
125

126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
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);

static Blob *      ink_pen_ellipse      (gdouble          x_center,
                                         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 228
static InkOptions * ink_options_new     (void);
static void         ink_options_reset   (GimpToolOptions *tool_options);
static void         ink_type_update     (GtkWidget       *radio_button,
                                         BlobFunc         function);

229

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

246 247
static GimpToolClass *parent_class      = NULL;

248

249
/*  public functions  */
250

251
void
Michael Natterer's avatar
Michael Natterer committed
252
gimp_ink_tool_register (Gimp *gimp)
253
{
Michael Natterer's avatar
Michael Natterer committed
254 255
  tool_manager_register_tool (gimp,
			      GIMP_TYPE_INK_TOOL,
256 257 258 259 260 261
			      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
262
			      GIMP_STOCK_TOOL_INK);
263 264
}

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

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

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

  return tool_type;
}

293 294 295

/*  private functions  */

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

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

305
  parent_class = g_type_class_peek_parent (klass);
306

307
  object_class->finalize     = gimp_ink_tool_finalize;
308

309 310 311 312 313
  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;
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
}

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,
329
					  (GimpToolOptions *) ink_options);
330

331
      ink_options_reset ((GimpToolOptions *) ink_options);
332
    }
333 334

  tool->tool_cursor = GIMP_INK_TOOL_CURSOR;
335 336 337
}

static void
338
gimp_ink_tool_finalize (GObject *object)
339 340 341 342 343 344
{
  GimpInkTool *ink_tool;

  ink_tool = GIMP_INK_TOOL (object);

  if (ink_tool->last_blob)
345 346 347 348
    {
      g_free (ink_tool->last_blob);
      ink_tool->last_blob = NULL;
    }
349 350 351

  ink_cleanup ();

352
  G_OBJECT_CLASS (parent_class)->finalize (object);
353 354
}

355 356 357 358
static void
gimp_ink_tool_control (GimpTool    *tool,
                       ToolAction   action,
                       GimpDisplay *gdisp)
359
{
360
  GimpInkTool *ink_tool;
361

362
  ink_tool = GIMP_INK_TOOL (tool);
363

364 365 366 367
  switch (action)
    {
    case PAUSE:
      break;
368

369 370
    case RESUME:
      break;
371

372 373 374
    case HALT:
      ink_cleanup ();
      break;
375

376 377 378
    default:
      break;
    }
379 380
}

381 382 383 384 385 386
static void
gimp_ink_tool_button_press (GimpTool        *tool,
                            GimpCoords      *coords,
                            guint32          time,
                            GdkModifierType  state,
                            GimpDisplay     *gdisp)
387
{
388 389 390 391
  GimpInkTool      *ink_tool;
  GimpDisplayShell *shell;
  GimpDrawable     *drawable;
  Blob             *b;
392

393
  ink_tool = GIMP_INK_TOOL (tool);
394

395
  shell = GIMP_DISPLAY_SHELL (gdisp->shell);
396

397
  drawable = gimp_image_active_drawable (gdisp->gimage);
jtl's avatar
jtl committed
398

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

401 402 403
  tool->state        = ACTIVE;
  tool->gdisp        = gdisp;
  tool->paused_count = 0;
404

405 406
  /*  pause the current selection and grab the pointer  */
  gimp_image_selection_control (gdisp->gimage, GIMP_SELECTION_PAUSE);
407

408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
  /* add motion memory if you press mod1 first ^ perfectmouse */
  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);
    }
  else
    {
      gdk_pointer_grab (shell->canvas->window, FALSE,
                        GDK_POINTER_MOTION_HINT_MASK |
                        GDK_BUTTON1_MOTION_MASK |
                        GDK_BUTTON_RELEASE_MASK,
                        NULL, NULL, time);
    }
  
  b = ink_pen_ellipse (coords->x,
                       coords->y,
		       coords->pressure,
                       coords->xtilt,
                       coords->ytilt,
		       10.0);
jtl's avatar
jtl committed
431

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

435 436 437 438 439 440
  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
441

442 443
  gimp_display_flush_now (gdisp);
}
jtl's avatar
jtl committed
444

445 446 447 448 449 450 451 452 453
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
454

455
  ink_tool = GIMP_INK_TOOL (tool);
456

457
  gimage = gdisp->gimage;
458

459 460
  /*  resume the current selection and ungrab the pointer  */
  gimp_image_selection_control (gdisp->gimage, GIMP_SELECTION_RESUME);
461

462 463
  gdk_pointer_ungrab (time);
  gdk_flush ();
464

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

468 469 470
  /*  free the last blob  */
  g_free (ink_tool->last_blob);
  ink_tool->last_blob = NULL;
Raph Levien's avatar
Raph Levien committed
471

472 473 474
  ink_finish (ink_tool, gimp_image_active_drawable (gdisp->gimage));
  gdisplays_flush ();
}
475

476 477 478 479 480 481 482 483 484 485 486 487 488 489
static void
gimp_ink_tool_motion (GimpTool        *tool,
                      GimpCoords      *coords,
                      guint32          time,
                      GdkModifierType  state,
                      GimpDisplay     *gdisp)
{
  GimpInkTool  *ink_tool;
  GimpDrawable *drawable;
  Blob         *b, *blob_union;

  gdouble velocity;
  gdouble dist;
  gdouble lasttime, thistime;
jtl's avatar
jtl committed
490
  
491
  ink_tool = GIMP_INK_TOOL (tool);
492

493
  drawable = gimp_image_active_drawable (gdisp->gimage);
494

495
  lasttime = ink_tool->last_time;
496

497 498
  time_smoother_add (ink_tool, time);
  thistime = ink_tool->last_time = time_smoother_result (ink_tool);
499

500 501 502 503 504 505
  /* 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 */
506

507 508
  if (thistime == lasttime)
    thistime = lasttime + 1;
509

510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525
  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)));
526

527 528
      dist = dist_smoother_result (ink_tool);
    }
529

530 531
  ink_tool->lastx = coords->x;
  ink_tool->lasty = coords->y;
532

533
  velocity = 10.0 * sqrt ((dist) / (gdouble) (thistime - lasttime));
534

535 536 537 538 539 540
  b = ink_pen_ellipse (coords->x,
                       coords->y,
                       coords->pressure,
                       coords->xtilt,
		       coords->ytilt,
                       velocity);
541

542 543 544
  blob_union = blob_convex_union (ink_tool->last_blob, b);
  g_free (ink_tool->last_blob);
  ink_tool->last_blob = b;
545

546 547
  ink_paste (ink_tool, drawable, blob_union);  
  g_free (blob_union);
548

549 550
  gimp_display_flush_now (gdisp);
}
jtl's avatar
jtl committed
551

552 553 554 555 556 557 558 559 560
static void
gimp_ink_tool_cursor_update (GimpTool        *tool,
                             GimpCoords      *coords,
                             GdkModifierType  state,
                             GimpDisplay     *gdisp)
{
  GimpDisplayShell *shell;
  GimpLayer        *layer;
  GdkCursorType     ctype = GDK_TOP_LEFT_ARROW;
561

562
  shell = GIMP_DISPLAY_SHELL (gdisp->shell);
563

564 565 566
  if ((layer = gimp_image_get_active_layer (gdisp->gimage))) 
    {
      gint off_x, off_y;
567

568
      gimp_drawable_offsets (GIMP_DRAWABLE (layer), &off_x, &off_y);
569

570 571 572 573 574 575 576 577 578 579 580 581 582 583
      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?
	   */
	  if (gimage_mask_is_empty (gdisp->gimage))
	    ctype = GIMP_MOUSE_CURSOR;
	  else if (gimage_mask_value (gdisp->gimage, coords->x, coords->y))
	    ctype = GIMP_MOUSE_CURSOR;
	}
    }
584

585 586 587 588
  gimp_display_shell_install_tool_cursor (shell,
                                          ctype,
                                          GIMP_INK_TOOL_CURSOR,
                                          GIMP_CURSOR_MODIFIER_NONE);
589 590
}

591 592 593

/*  the brush widget functions  */

594
static void
595 596
brush_widget_active_rect (BrushWidget  *brush_widget,
			  GtkWidget    *widget,
597 598 599 600 601
			  GdkRectangle *rect)
{
  int x,y;
  int r;

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

604
  x = widget->allocation.width / 2 + 0.85 * r * ink_options->aspect / 10.0 *
605
    cos (ink_options->angle);
606
  y = widget->allocation.height / 2 + 0.85 * r * ink_options->aspect / 10.0 *
607 608
    sin (ink_options->angle);

609 610
  rect->x = x - 5;
  rect->y = y - 5;
611 612 613 614 615
  rect->width = 10;
  rect->height = 10;
}

static void
616
brush_widget_realize (GtkWidget *widget)
617
{
618
  gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
619 620 621
}

static void
622 623 624 625 626
brush_widget_draw_brush (BrushWidget *brush_widget,
			 GtkWidget   *widget,
			 gdouble      xc,
			 gdouble      yc,
			 gdouble      radius)
627
{
628 629 630 631 632 633 634 635 636 637 638 639
  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);
640 641 642
}

static void
643 644 645
brush_widget_expose (GtkWidget      *widget,
		     GdkEventExpose *event,
		     BrushWidget    *brush_widget)
646 647 648
{
  GdkRectangle rect;
  int r0;
649 650

  r0 = MIN (widget->allocation.width, widget->allocation.height) / 2;
651 652 653 654

  if (r0 < 2)
    return;

655 656 657 658 659 660 661
  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);
662

663 664
  brush_widget_active_rect (brush_widget, widget, &rect);
  gdk_draw_rectangle (widget->window, widget->style->bg_gc[GTK_STATE_NORMAL],
665 666 667
		      TRUE,	/* filled */
		      rect.x, rect.y, 
		      rect.width, rect.height);
668
  gtk_draw_shadow (widget->style, widget->window, widget->state, GTK_SHADOW_OUT,
669 670 671 672 673
		   rect.x, rect.y,
		   rect.width, rect.height);
}

static void
674 675 676
brush_widget_button_press (GtkWidget      *widget,
			   GdkEventButton *event,
			   BrushWidget    *brush_widget)
677 678 679
{
  GdkRectangle rect;

680
  brush_widget_active_rect (brush_widget, widget, &rect);
681 682 683 684 685
  
  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;
686

687
      gtk_grab_add (brush_widget->widget);
688 689 690 691
    }
}

static void
692 693 694
brush_widget_button_release (GtkWidget      *widget,
			     GdkEventButton *event,
			     BrushWidget    *brush_widget)
695 696
{
  brush_widget->state = FALSE;
697

698
  gtk_grab_remove (brush_widget->widget);
699 700 701
}

static void
702 703 704
brush_widget_motion_notify (GtkWidget      *widget,
			    GdkEventMotion *event,
			    BrushWidget    *brush_widget)
705 706 707 708 709 710 711 712
{
  int x;
  int y;
  int r0;
  int rsquare;

  if (brush_widget->state)
    {
713 714
      x = event->x - widget->allocation.width / 2;
      y = event->y - widget->allocation.height / 2;
715 716 717 718
      rsquare = x*x + y*y;

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

721
	  r0 = MIN (widget->allocation.width, widget->allocation.height) / 2;
722
	  ink_options->aspect =
723
	    10.0 * sqrt ((double) rsquare / (r0 * r0)) / 0.85;
724 725 726 727 728 729

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

730
	  gtk_widget_draw (widget, NULL);
731 732 733 734
	}
    }
}

735 736 737 738 739 740
/*
 * Return a black-on white pixmap in the given colormap and
 * visual that represents the BlobFunc 'function'
 */
static GdkPixmap *
blob_pixmap (GdkColormap *colormap,
741 742
	     GdkVisual   *visual,
	     BlobFunc     function)
743 744
{
  GdkPixmap *pixmap;
745 746
  GdkGC     *black_gc, *white_gc;
  GdkColor   tmp_color;
747
  gboolean   success;
748
  Blob      *blob;
749 750 751

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

752 753 754 755 756
  tmp_color.red   = 0;
  tmp_color.green = 0;
  tmp_color.blue  = 0;
  gdk_colormap_alloc_colors (colormap, &tmp_color, 1, FALSE, TRUE, &success);

757 758 759
  black_gc = gdk_gc_new (pixmap);
  gdk_gc_set_foreground (black_gc, &tmp_color);

760 761 762 763 764
  tmp_color.red   = 255;
  tmp_color.green = 255;
  tmp_color.blue  = 255;
  gdk_colormap_alloc_colors (colormap, &tmp_color, 1, FALSE, TRUE, &success);

765 766 767 768 769 770 771
  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
772
  g_free (blob);
773 774 775 776 777 778 779 780 781 782 783

  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
784 785 786
paint_blob (GdkDrawable *drawable,
	    GdkGC       *gc,
	    Blob        *blob)
787 788 789 790 791 792 793 794 795 796 797 798
{
  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);
    }
}

799

800
static Blob *
801 802 803 804 805
ink_pen_ellipse (gdouble x_center,
		 gdouble y_center,
		 gdouble pressure,
		 gdouble xtilt,
		 gdouble ytilt,
806
		 gdouble velocity)
807 808 809 810 811
{
  double size;
  double tsin, tcos;
  double aspect, radmin;
  double x,y;
812
  double tscale;
Raph Levien's avatar
Raph Levien committed
813 814
  double tscale_c;
  double tscale_s;
815
  
816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843
  /* 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);
844

845 846 847
  if (size*SUBSAMPLE < 1.0) size = 1.0/SUBSAMPLE;

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

Raph Levien's avatar
Raph Levien committed
849 850 851 852
  /* 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 */

853
  tscale = ink_options->tilt_sensitivity * 10.0;
854 855
  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
856 857 858 859 860 861 862 863
  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
864 865 866 867 868 869 870 871 872 873 874 875
  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);
    }
876

877 878 879 880 881 882 883 884
  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;
  
885 886 887
  return ink_options->function (x_center * SUBSAMPLE, y_center * SUBSAMPLE,
				radmin*aspect*tcos, radmin*aspect*tsin,  
				-radmin*tsin, radmin*tcos);
888 889 890
}

static void
891 892
dist_smoother_init (GimpInkTool *ink_tool,
		    gdouble      initval)
893
{
894
  gint i;
895

896
  ink_tool->dt_index = 0;
897

898
  for (i=0; i<DIST_SMOOTHER_BUFFER; i++)
899
    {
900 901 902
      ink_tool->dt_buffer[i] = initval;
    }
}
903

904 905 906 907 908
static gdouble
dist_smoother_result (GimpInkTool *ink_tool)
{
  gint    i;
  gdouble result = 0.0;
909

910 911 912
  for (i = 0; i < DIST_SMOOTHER_BUFFER; i++)
    {
      result += ink_tool->dt_buffer[i];
913
    }
914 915

  return (result / (gdouble) DIST_SMOOTHER_BUFFER);
916 917 918
}

static void
919 920
dist_smoother_add (GimpInkTool *ink_tool,
		   gdouble      value)
921
{
922
  ink_tool->dt_buffer[ink_tool->dt_index] = value;
Michael Natterer's avatar
Michael Natterer committed
923

924 925 926
  if ((++ink_tool->dt_index) == DIST_SMOOTHER_BUFFER)
    ink_tool->dt_index = 0;
}
927 928


929 930 931 932 933
static void
time_smoother_init (GimpInkTool *ink_tool,
		    guint32      initval)
{
  gint i;
934

935
  ink_tool->ts_index = 0;
936

937
  for (i = 0; i < TIME_SMOOTHER_BUFFER; i++)
938
    {
939
      ink_tool->ts_buffer[i] = initval;
940
    }
941 942 943 944 945 946 947 948 949
}

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

  for (i = 0; i < TIME_SMOOTHER_BUFFER; i++)
950
    {
951
      result += ink_tool->ts_buffer[i];
952
    }
953

954 955 956 957 958 959
#ifdef _MSC_VER
  return (gdouble) (gint64) (result / TIME_SMOOTHER_BUFFER);
#else
  return (result / TIME_SMOOTHER_BUFFER);
#endif
}
960 961

static void
962 963
time_smoother_add (GimpInkTool *ink_tool,
		   guint32      value)
964 965 966 967 968 969 970
{
  ink_tool->ts_buffer[ink_tool->ts_index] = value;

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

971
static void
972
ink_init (GimpInkTool  *ink_tool,
973 974 975
	  GimpDrawable *drawable, 
	  gdouble       x,
	  gdouble       y)
976 977 978 979 980 981 982 983
{
  /*  free the block structures  */
  if (undo_tiles)
    tile_manager_destroy (undo_tiles);
  if (canvas_tiles)
    tile_manager_destroy (canvas_tiles);

  /*  Allocate the undo structure  */
984 985 986
  undo_tiles = tile_manager_new (gimp_drawable_width (drawable),
				 gimp_drawable_height (drawable),
				 gimp_drawable_bytes (drawable));
987 988

  /*  Allocate the canvas blocks structure  */
989 990
  canvas_tiles = tile_manager_new (gimp_drawable_width (drawable),
				   gimp_drawable_height (drawable), 1);
991 992 993 994 995 996 997

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

static void
998 999
ink_finish (GimpInkTool  *ink_tool,
	    GimpDrawable *drawable)
1000 1001
{
  /*  push an undo  */
Michael Natterer's avatar
Michael Natterer committed
1002 1003
  gimp_drawable_apply_image (drawable, ink_tool->x1, ink_tool->y1,
			     ink_tool->x2, ink_tool->y2, undo_tiles, TRUE);
1004 1005 1006 1007 1008
  undo_tiles = NULL;

  /*  invalidate the drawable--have to do it here, because
   *  it is not done during the actual painting.
   */
1009
  gimp_viewable_invalidate_preview (GIMP_VIEWABLE (drawable));
1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030
}

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)
1031 1032 1033 1034
    {
      temp_buf_free (canvas_buf);
      canvas_buf = NULL;
    }
1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050
}

/*********************************
 *  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
1051
ink_set_paint_area (GimpInkTool  *ink_tool, 
1052 1053 1054
		    GimpDrawable *drawable, 
		    Blob         *blob)
{
1055 1056 1057
  gint x, y, width, height;
  gint x1, y1, x2, y2;
  gint bytes;
1058

1059 1060
  blob_bounds (blob, &x, &y, &width, &height);

1061 1062
  bytes = gimp_drawable_has_alpha (drawable) ?
    gimp_drawable_bytes (drawable) : gimp_drawable_bytes (drawable) + 1;
1063

1064 1065 1066 1067
  x1 = CLAMP (x/SUBSAMPLE - 1,            0, gimp_drawable_width (drawable));
  y1 = CLAMP (y/SUBSAMPLE - 1,            0, gimp_drawable_height (drawable));
  x2 = CLAMP ((x + width)/SUBSAMPLE + 2,  0, gimp_drawable_width (drawable));
  y2 = CLAMP ((y + height)/SUBSAMPLE + 2, 0, gimp_drawable_height (drawable));