gimpink.c 29.6 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
#include "core/gimpdrawable.h"
#include "core/gimpimage.h"
#include "core/gimpimage-mask.h"
41
#include "core/gimpimage-undo-push.h"
42
#include "core/gimptoolinfo.h"
43

44 45
#include "paint/gimppaintoptions.h"

46 47
#include "display/gimpdisplay.h"

48
#include "gimpinkoptions.h"
49 50
#include "gimpinktool.h"
#include "gimpinktool-blob.h"
51

52 53
#include "libgimp/gimpintl.h"

54

55
#define SUBSAMPLE 8
56

57

58
/*  local function prototypes  */
59

60 61 62
static void        gimp_ink_tool_class_init      (GimpInkToolClass *klass);
static void        gimp_ink_tool_init            (GimpInkTool      *tool);

63
static void        gimp_ink_tool_finalize        (GObject          *object);
64

65
static void        gimp_ink_tool_control         (GimpTool         *tool,
66
                                                  GimpToolAction    action,
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
                                                  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);

88
static Blob *      ink_pen_ellipse      (GimpInkOptions  *options,
89
                                         gdouble          x_center,
90 91 92 93 94
                                         gdouble          y_center,
                                         gdouble          pressure,
                                         gdouble          xtilt,
                                         gdouble          ytilt,
                                         gdouble          velocity);
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112

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);
113 114
static void        ink_cleanup          (void);

Michael Natterer's avatar
Michael Natterer committed
115
/*  Rendering functions  */
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
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);
136 137


138 139 140 141 142 143 144 145 146 147 148 149 150
/* 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;

151
static GimpToolClass *parent_class = NULL;
152

153

154
/*  public functions  */
155

156
void
Nate Summers's avatar
Nate Summers committed
157
gimp_ink_tool_register (GimpToolRegisterCallback  callback,
158
                        gpointer                  data)
159
{
Nate Summers's avatar
Nate Summers committed
160
  (* callback) (GIMP_TYPE_INK_TOOL,
161 162
                GIMP_TYPE_INK_OPTIONS,
                gimp_ink_options_gui,
163
                TRUE,
164
                "gimp-ink-tool",
165
                _("Ink"),
166 167 168
                _("Draw in ink"),
                N_("/Tools/Paint Tools/Ink"), "K",
                NULL, "tools/ink.html",
Nate Summers's avatar
Nate Summers committed
169
                GIMP_STOCK_TOOL_INK,
170
                data);
171 172
}

173
GType
174 175
gimp_ink_tool_get_type (void)
{
176
  static GType tool_type = 0;
177 178 179

  if (! tool_type)
    {
180
      static const GTypeInfo tool_info =
181
      {
182 183 184 185 186 187
        sizeof (GimpInkToolClass),
	(GBaseInitFunc) NULL,
	(GBaseFinalizeFunc) NULL,
	(GClassInitFunc) gimp_ink_tool_class_init,
	NULL,           /* class_finalize */
	NULL,           /* class_data     */
188
	sizeof (GimpInkTool),
189 190
	0,              /* n_preallocs    */
	(GInstanceInitFunc) gimp_ink_tool_init,
191 192
      };

193 194 195
      tool_type = g_type_register_static (GIMP_TYPE_TOOL,
					  "GimpInkTool", 
                                          &tool_info, 0);
196 197 198 199 200
    }

  return tool_type;
}

201 202 203

/*  private functions  */

204 205 206
static void
gimp_ink_tool_class_init (GimpInkToolClass *klass)
{
207
  GObjectClass   *object_class;
208 209
  GimpToolClass  *tool_class;

210 211
  object_class = G_OBJECT_CLASS (klass);
  tool_class   = GIMP_TOOL_CLASS (klass);
212

213
  parent_class = g_type_class_peek_parent (klass);
214

215
  object_class->finalize     = gimp_ink_tool_finalize;
216

217 218 219 220 221
  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;
222 223 224 225 226 227 228 229
}

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

  tool = GIMP_TOOL (ink_tool);
230

231 232
  gimp_tool_control_set_motion_mode (tool->control, GIMP_MOTION_MODE_EXACT);
  gimp_tool_control_set_tool_cursor (tool->control, GIMP_INK_TOOL_CURSOR);
233 234 235
}

static void
236
gimp_ink_tool_finalize (GObject *object)
237 238 239 240 241 242
{
  GimpInkTool *ink_tool;

  ink_tool = GIMP_INK_TOOL (object);

  if (ink_tool->last_blob)
243 244 245 246
    {
      g_free (ink_tool->last_blob);
      ink_tool->last_blob = NULL;
    }
247 248 249

  ink_cleanup ();

250
  G_OBJECT_CLASS (parent_class)->finalize (object);
251 252
}

253
static void
254 255 256
gimp_ink_tool_control (GimpTool       *tool,
                       GimpToolAction  action,
                       GimpDisplay    *gdisp)
257
{
258
  GimpInkTool *ink_tool;
259

260
  ink_tool = GIMP_INK_TOOL (tool);
261

262 263 264 265
  switch (action)
    {
    case PAUSE:
      break;
266

267 268
    case RESUME:
      break;
269

270 271 272
    case HALT:
      ink_cleanup ();
      break;
273

274 275 276
    default:
      break;
    }
277 278

  GIMP_TOOL_CLASS (parent_class)->control (tool, action, gdisp);
279 280
}

281 282 283 284 285 286
static void
gimp_ink_tool_button_press (GimpTool        *tool,
                            GimpCoords      *coords,
                            guint32          time,
                            GdkModifierType  state,
                            GimpDisplay     *gdisp)
287
{
288 289 290
  GimpInkTool    *ink_tool;
  GimpInkOptions *options;
  GimpDrawable   *drawable;
291
  GimpCoords      curr_coords;
292
  Blob           *b;
293

294
  ink_tool = GIMP_INK_TOOL (tool);
295
  options  = GIMP_INK_OPTIONS (tool->tool_info->tool_options);
296

297
  drawable = gimp_image_active_drawable (gdisp->gimage);
jtl's avatar
jtl committed
298

299 300 301 302 303 304 305 306 307 308 309 310
  curr_coords = *coords;

  {
    gint off_x, off_y;

    gimp_drawable_offsets (drawable, &off_x, &off_y);

    curr_coords.x -= off_x;
    curr_coords.y -= off_y;
  }

  ink_init (ink_tool, drawable, curr_coords.x, curr_coords.y);
jtl's avatar
jtl committed
311

312
  gimp_tool_control_activate (tool->control);
313
  tool->gdisp = gdisp; 
314

315
  /*  pause the current selection  */
316
  gimp_image_selection_control (gdisp->gimage, GIMP_SELECTION_PAUSE);
317

318
  b = ink_pen_ellipse (options,
319 320 321 322 323
                       curr_coords.x,
                       curr_coords.y,
		       curr_coords.pressure,
                       curr_coords.xtilt,
                       curr_coords.ytilt,
324
		       10.0);
jtl's avatar
jtl committed
325

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

329 330
  time_smoother_init (ink_tool, time);
  ink_tool->last_time = time;
331

332 333
  dist_smoother_init (ink_tool, 0.0);
  ink_tool->init_velocity = TRUE;
334 335 336

  ink_tool->lastx = curr_coords.x;
  ink_tool->lasty = curr_coords.y;
jtl's avatar
jtl committed
337

338 339
  gimp_display_flush_now (gdisp);
}
jtl's avatar
jtl committed
340

341 342 343 344 345 346 347 348 349
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
350

351
  ink_tool = GIMP_INK_TOOL (tool);
352

353
  gimage = gdisp->gimage;
354

355
  /*  resume the current selection  */
356
  gimp_image_selection_control (gdisp->gimage, GIMP_SELECTION_RESUME);
357

358
  /*  Set tool state to inactive -- no longer painting */
359
  gimp_tool_control_halt (tool->control);
360

361 362 363
  /*  free the last blob  */
  g_free (ink_tool->last_blob);
  ink_tool->last_blob = NULL;
Raph Levien's avatar
Raph Levien committed
364

365
  ink_finish (ink_tool, gimp_image_active_drawable (gdisp->gimage));
366
  gimp_image_flush (gdisp->gimage);
367
}
368

369 370 371 372 373 374 375
static void
gimp_ink_tool_motion (GimpTool        *tool,
                      GimpCoords      *coords,
                      guint32          time,
                      GdkModifierType  state,
                      GimpDisplay     *gdisp)
{
376 377 378
  GimpInkTool    *ink_tool;
  GimpInkOptions *options;
  GimpDrawable   *drawable;
379 380 381 382 383 384
  GimpCoords      curr_coords;
  Blob           *b;
  Blob           *blob_union;
  gdouble         velocity;
  gdouble         dist;
  gdouble         lasttime, thistime;
385

386
  ink_tool = GIMP_INK_TOOL (tool);
387
  options  = GIMP_INK_OPTIONS (tool->tool_info->tool_options);
388

389
  drawable = gimp_image_active_drawable (gdisp->gimage);
390

391 392 393 394 395 396 397 398 399 400 401
  curr_coords = *coords;

  {
    gint off_x, off_y;

    gimp_drawable_offsets (drawable, &off_x, &off_y);

    curr_coords.x -= off_x;
    curr_coords.y -= off_y;
  }

402
  lasttime = ink_tool->last_time;
403

404 405
  time_smoother_add (ink_tool, time);
  thistime = ink_tool->last_time = time_smoother_result (ink_tool);
406

407 408 409 410 411 412
  /* 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 */
413

414 415
  if (thistime == lasttime)
    thistime = lasttime + 1;
416

417 418 419
  if (ink_tool->init_velocity)
    {
      dist_smoother_init (ink_tool,
420 421 422 423
			  dist = sqrt ((ink_tool->lastx - curr_coords.x) *
                                       (ink_tool->lastx - curr_coords.x) +
				       (ink_tool->lasty - curr_coords.y) *
                                       (ink_tool->lasty - curr_coords.y)));
424 425 426 427 428
      ink_tool->init_velocity = FALSE;
    }
  else
    {
      dist_smoother_add (ink_tool,
429 430 431 432
			 sqrt ((ink_tool->lastx - curr_coords.x) *
                               (ink_tool->lastx - curr_coords.x) +
			       (ink_tool->lasty - curr_coords.y) *
                               (ink_tool->lasty - curr_coords.y)));
433

434 435
      dist = dist_smoother_result (ink_tool);
    }
436

437 438
  ink_tool->lastx = curr_coords.x;
  ink_tool->lasty = curr_coords.y;
439

440
  velocity = 10.0 * sqrt ((dist) / (gdouble) (thistime - lasttime));
441

442
  b = ink_pen_ellipse (options,
443 444 445 446 447
                       curr_coords.x,
                       curr_coords.y,
                       curr_coords.pressure,
                       curr_coords.xtilt,
		       curr_coords.ytilt,
448
                       velocity);
449

450 451 452
  blob_union = blob_convex_union (ink_tool->last_blob, b);
  g_free (ink_tool->last_blob);
  ink_tool->last_blob = b;
453

454 455
  ink_paste (ink_tool, drawable, blob_union);  
  g_free (blob_union);
456

457 458
  gimp_display_flush_now (gdisp);
}
jtl's avatar
jtl committed
459

460 461 462 463 464 465
static void
gimp_ink_tool_cursor_update (GimpTool        *tool,
                             GimpCoords      *coords,
                             GdkModifierType  state,
                             GimpDisplay     *gdisp)
{
466 467
  GimpLayer     *layer;
  GdkCursorType  ctype = GDK_TOP_LEFT_ARROW;
468

469 470 471
  if ((layer = gimp_image_get_active_layer (gdisp->gimage))) 
    {
      gint off_x, off_y;
472

473
      gimp_drawable_offsets (GIMP_DRAWABLE (layer), &off_x, &off_y);
474

475 476 477 478 479 480 481 482
      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?
	   */
483
	  if (gimp_image_mask_is_empty (gdisp->gimage))
484
	    ctype = GIMP_MOUSE_CURSOR;
485
	  else if (gimp_image_mask_value (gdisp->gimage, coords->x, coords->y))
486 487 488
	    ctype = GIMP_MOUSE_CURSOR;
	}
    }
489

490
  gimp_tool_control_set_cursor (tool->control, ctype);
491 492

  GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, gdisp);
493 494
}

495

496
static Blob *
497 498 499 500 501 502 503
ink_pen_ellipse (GimpInkOptions *options,
                 gdouble         x_center,
		 gdouble         y_center,
		 gdouble         pressure,
		 gdouble         xtilt,
		 gdouble         ytilt,
		 gdouble         velocity)
504
{
505 506 507 508 509 510 511 512
  BlobFunc function = blob_ellipse;
  gdouble  size;
  gdouble  tsin, tcos;
  gdouble  aspect, radmin;
  gdouble  x,y;
  gdouble  tscale;
  gdouble  tscale_c;
  gdouble  tscale_s;
513

514 515
  /* Adjust the size depending on pressure. */

516
  size = options->size * (1.0 + options->size_sensitivity *
517
                          (2.0 * pressure - 1.0) );
518 519 520 521 522 523 524 525 526

  /* 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
527
  g_print ("%f (%f) -> ", (float)size, (float)velocity);
528 529
#endif  

530 531 532
  size = (options->vel_sensitivity *
          ((4.5 * size) / (1.0 + options->vel_sensitivity * (2.0 * velocity)))
          + (1.0 - options->vel_sensitivity) * size);
533 534

#ifdef VERBOSE
535
  g_print ("%f\n", (float)size);
536 537 538 539
#endif

  /* Clamp resulting size to sane limits */

540 541
  if (size > options->size * (1.0 + options->size_sensitivity))
    size = options->size * (1.0 + options->size_sensitivity);
542

543 544
  if (size * SUBSAMPLE < 1.0)
    size = 1.0 / SUBSAMPLE;
545 546

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

Raph Levien's avatar
Raph Levien committed
548 549 550 551
  /* 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 */

552 553 554 555
  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));

556
  x = (options->blob_aspect * cos (options->blob_angle) +
557
       xtilt * tscale_c - ytilt * tscale_s);
558
  y = (options->blob_aspect * sin (options->blob_angle) +
559 560
       ytilt * tscale_c + xtilt * tscale_s);

Raph Levien's avatar
Raph Levien committed
561 562
#ifdef VERBOSE
  g_print ("angle %g aspect %g; %g %g; %g %g\n",
563
	   options->blob_angle, options->blob_aspect, tscale_c, tscale_s, x, y);
Raph Levien's avatar
Raph Levien committed
564
#endif
565 566

  aspect = sqrt (x*x + y*y);
567 568 569

  if (aspect != 0)
    {
570 571
      tcos = x / aspect;
      tsin = y / aspect;
572 573 574
    }
  else
    {
575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595
      tsin = sin (options->blob_angle);
      tcos = cos (options->blob_angle);
    }

  aspect = CLAMP (aspect, 1.0, 10.0);

  radmin = MAX (1.0, SUBSAMPLE * size / aspect);

  switch (options->blob_type)
    {
    case GIMP_INK_BLOB_TYPE_ELLIPSE:
      function = blob_ellipse;
      break;

    case GIMP_INK_BLOB_TYPE_SQUARE:
      function = blob_square;
      break;

    case GIMP_INK_BLOB_TYPE_DIAMOND:
      function = blob_diamond;
      break;
596
    }
597

598 599 600 601 602 603
  return (* function) (x_center * SUBSAMPLE,
                       y_center * SUBSAMPLE,
                       radmin * aspect * tcos,
                       radmin * aspect * tsin,  
                       -radmin * tsin,
                       radmin * tcos);
604 605 606
}

static void
607 608
dist_smoother_init (GimpInkTool *ink_tool,
		    gdouble      initval)
609
{
610
  gint i;
611

612
  ink_tool->dt_index = 0;
613

614
  for (i = 0; i < DIST_SMOOTHER_BUFFER; i++)
615
    {
616 617 618
      ink_tool->dt_buffer[i] = initval;
    }
}
619

620 621 622 623 624
static gdouble
dist_smoother_result (GimpInkTool *ink_tool)
{
  gint    i;
  gdouble result = 0.0;
625

626 627 628
  for (i = 0; i < DIST_SMOOTHER_BUFFER; i++)
    {
      result += ink_tool->dt_buffer[i];
629
    }
630 631

  return (result / (gdouble) DIST_SMOOTHER_BUFFER);
632 633 634
}

static void
635 636
dist_smoother_add (GimpInkTool *ink_tool,
		   gdouble      value)
637
{
638
  ink_tool->dt_buffer[ink_tool->dt_index] = value;
Michael Natterer's avatar
Michael Natterer committed
639

640 641 642
  if ((++ink_tool->dt_index) == DIST_SMOOTHER_BUFFER)
    ink_tool->dt_index = 0;
}
643 644


645 646 647 648 649
static void
time_smoother_init (GimpInkTool *ink_tool,
		    guint32      initval)
{
  gint i;
650

651
  ink_tool->ts_index = 0;
652

653
  for (i = 0; i < TIME_SMOOTHER_BUFFER; i++)
654
    {
655
      ink_tool->ts_buffer[i] = initval;
656
    }
657 658 659 660 661 662 663 664 665
}

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

  for (i = 0; i < TIME_SMOOTHER_BUFFER; i++)
666
    {
667
      result += ink_tool->ts_buffer[i];
668
    }
669

670 671 672 673 674 675
#ifdef _MSC_VER
  return (gdouble) (gint64) (result / TIME_SMOOTHER_BUFFER);
#else
  return (result / TIME_SMOOTHER_BUFFER);
#endif
}
676 677

static void
678 679
time_smoother_add (GimpInkTool *ink_tool,
		   guint32      value)
680 681 682 683 684 685 686
{
  ink_tool->ts_buffer[ink_tool->ts_index] = value;

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

687
static void
688
ink_init (GimpInkTool  *ink_tool,
689 690 691
	  GimpDrawable *drawable, 
	  gdouble       x,
	  gdouble       y)
692 693 694 695 696 697 698 699
{
  /*  free the block structures  */
  if (undo_tiles)
    tile_manager_destroy (undo_tiles);
  if (canvas_tiles)
    tile_manager_destroy (canvas_tiles);

  /*  Allocate the undo structure  */
700 701 702
  undo_tiles = tile_manager_new (gimp_drawable_width (drawable),
				 gimp_drawable_height (drawable),
				 gimp_drawable_bytes (drawable));
703 704

  /*  Allocate the canvas blocks structure  */
705 706
  canvas_tiles = tile_manager_new (gimp_drawable_width (drawable),
				   gimp_drawable_height (drawable), 1);
707 708 709 710 711 712 713

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

static void
714 715
ink_finish (GimpInkTool  *ink_tool,
	    GimpDrawable *drawable)
716
{
717
  gimp_drawable_push_undo (drawable, _("Ink"),
718 719 720
                           ink_tool->x1, ink_tool->y1,
                           ink_tool->x2, ink_tool->y2,
                           undo_tiles, TRUE);
721 722 723 724 725
  undo_tiles = NULL;

  /*  invalidate the drawable--have to do it here, because
   *  it is not done during the actual painting.
   */
726
  gimp_viewable_invalidate_preview (GIMP_VIEWABLE (drawable));
727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747
}

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)
748 749 750 751
    {
      temp_buf_free (canvas_buf);
      canvas_buf = NULL;
    }
752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767
}

/*********************************
 *  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
768
ink_set_paint_area (GimpInkTool  *ink_tool, 
769 770 771
		    GimpDrawable *drawable, 
		    Blob         *blob)
{
772 773 774
  gint x, y, width, height;
  gint x1, y1, x2, y2;
  gint bytes;
775

776 777
  blob_bounds (blob, &x, &y, &width, &height);

778 779
  bytes = gimp_drawable_has_alpha (drawable) ?
    gimp_drawable_bytes (drawable) : gimp_drawable_bytes (drawable) + 1;
780

781 782 783 784
  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));
785 786 787 788 789 790 791 792 793 794 795 796

  /*  configure the canvas buffer  */
  if ((x2 - x1) && (y2 - y1))
    canvas_buf = temp_buf_resize (canvas_buf, bytes, x1, y1,
				  (x2 - x1), (y2 - y1));
}

enum { ROW_START, ROW_STOP };

/* The insertion sort here, for SUBSAMPLE = 8, tends to beat out
 * qsort() by 4x with CFLAGS=-O2, 2x with CFLAGS=-g
 */
797 798 799
static void
insert_sort (gint *data,
	     gint  n)
800
{
801 802
  gint i, j, k;
  gint tmp1, tmp2;
803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825

  for (i=2; i<2*n; i+=2)
    {
      tmp1 = data[i];
      tmp2 = data[i+1];
      j = 0;
      while (data[j] < tmp1)
	j += 2;

      for (k=i; k>j; k-=2)
	{
	  data[k] = data[k-2];
	  data[k+1] = data[k-1];
	}

      data[j] = tmp1;
      data[j+1] = tmp2;
    }
}

static void
fill_run (guchar *dest,
	  guchar  alpha,
826
	  gint    w)
827 828 829 830 831 832 833 834 835
{
  if (alpha == 255)
    {
      memset (dest, 255, w);
    }
  else
    {
      while (w--)
	{
836
	  *dest = MAX(*dest, alpha);
837 838 839 840 841 842
	  dest++;
	}
    }
}

static void
843 844 845 846 847
render_blob_line (Blob   *blob,
		  guchar *dest,
		  gint    x,
		  gint    y,
		  gint    width)
848
{
849 850 851
  gint  buf[4 * SUBSAMPLE];
  gint *data    = buf;
  gint  n       = 0;
852 853 854 855 856
  gint  i, j;
  gint  current = 0;  /* number of filled rows at this point
		       * in the scan line
		       */
  gint last_x;
857 858 859 860

  /* Sort start and ends for all lines */
  
  j = y * SUBSAMPLE - blob->y;
861
  for (i = 0; i < SUBSAMPLE; i++)
862 863 864
    {
      if (j >= blob->height)
	break;
865

866 867
      if ((j > 0) && (blob->data[j].left <= blob->data[j].right))
	{
868 869 870 871
	  data[2 * n]                     = blob->data[j].left;
	  data[2 * n + 1]                 = ROW_START;
	  data[2 * SUBSAMPLE + 2 * n]     = blob->data[j].right;
	  data[2 * SUBSAMPLE + 2 * n + 1] = ROW_STOP;
872 873 874 875 876 877 878 879
	  n++;
	}
      j++;
    }

  /*   If we have less than SUBSAMPLE rows, compress */
  if (n < SUBSAMPLE)
    {
880 881
      for (i = 0; i < 2 * n; i++)
	data[2 * n + i] = data[2 * SUBSAMPLE + i];
882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906
    }

  /*   Now count start and end separately */
  n *= 2;

  insert_sort (data, n);

  /* Discard portions outside of tile */

  while ((n > 0) && (data[0] < SUBSAMPLE*x))
    {
      if (data[1] == ROW_START)
	current++;
      else
	current--;
      data += 2;
      n--;
    }

  while ((n > 0) && (data[2*(n-1)] >= SUBSAMPLE*(x+width)))
    n--;
  
  /* Render the row */

  last_x = 0;
907
  for (i = 0; i < n;)
908
    {
909
      gint cur_x = data[2 * i] / SUBSAMPLE - x;
910
      gint pixel;
911 912 913

      /* Fill in portion leading up to this pixel */
      if (current && cur_x != last_x)
914
	fill_run (dest + last_x, (255 * current) / SUBSAMPLE, cur_x - last_x);
915 916 917 918 919 920

      /* Compute the value for this pixel */
      pixel = current * SUBSAMPLE; 

      while (i<n)
	{
921 922
	  gint tmp_x = data[2 * i] / SUBSAMPLE;

923 924 925
	  if (tmp_x - x != cur_x)
	    break;

926
	  if (data[2 * i + 1] == ROW_START)
927 928
	    {
	      current++;
929
	      pixel += ((tmp_x + 1) * SUBSAMPLE) - data[2 * i];
930 931 932 933
	    }
	  else
	    {
	      current--;
934
	      pixel -= ((tmp_x + 1) * SUBSAMPLE) - data[2 * i];
935 936 937 938 939
	    }
	  
	  i++;
	}

940
      dest[cur_x] = MAX (dest[cur_x], (pixel * 255) / (SUBSAMPLE * SUBSAMPLE));
941 942 943 944 945

      last_x = cur_x + 1;
    }

  if (current != 0)
946
    fill_run (dest + last_x, (255 * current)/ SUBSAMPLE, width - last_x);
947 948 949
}

static void
950 951
render_blob (PixelRegion *dest,
	     Blob        *blob)
952
{
953 954 955 956
  gint      i;
  gint      h;
  guchar   *s;
  gpointer  pr;
957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974

  for (pr = pixel_regions_register (1, dest); 
       pr != NULL; 
       pr = pixel_regions_process (pr))
    {
      h = dest->h;
      s = dest->data;

      for (i=0; i<h; i++)
	{
	  render_blob_line (blob, s,
			    dest->x, dest->y + i, dest->w);
	  s += dest->rowstride;
	}
    }
}

static void
975
ink_paste (GimpInkTool  *ink_tool, 
976 977 978
	   GimpDrawable *drawable,
	   Blob         *blob)
{
979
  GimpImage   *gimage;
Michael Natterer's avatar
Michael Natterer committed
980
  GimpContext *context;
981 982 983
  PixelRegion  srcPR;
  gint         offx, offy;
  gchar        col[MAX_CHANNELS];
984

985
  if (! (gimage = gimp_item_get_image (GIMP_ITEM (drawable))))
986
    return;
987

988
  context = GIMP_CONTEXT (GIMP_TOOL (ink_tool)->tool_info->tool_options);
Michael Natterer's avatar
Michael Natterer committed
989

990 991
  /* Get the the buffer */
  ink_set_paint_area (ink_tool, drawable, blob);
992 993 994 995
 
  /* check to make sure there is actually a canvas to draw on */
  if (!canvas_buf)
    return;
996

997
  gimp_image_get_foreground (gimage, drawable, col);
998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025

  /*  set the alpha channel  */
  col[canvas_buf->bytes - 1] = OPAQUE_OPACITY;

  /*  color the pixels  */
  color_pixels (temp_buf_data (canvas_buf), col,
		canvas_buf->width * canvas_buf->height, canvas_buf->bytes);

  /*  set undo blocks  */
  ink_set_undo_tiles (drawable,
		      canvas_buf->x, canvas_buf->y,
		      canvas_buf->width, canvas_buf->height);

  /*  initialize any invalid canvas tiles  */
  ink_set_canvas_tiles (canvas_buf->x, canvas_buf->y,
			canvas_buf->width, canvas_buf->height);

  ink_to_canvas_tiles (ink_tool, blob, col);

  /*  initialize canvas buf source pixel regions  */
  srcPR.bytes = canvas_buf->bytes;
  srcPR.x = 0; srcPR.y = 0;
  srcPR.w = canvas_buf->width;
  srcPR.h = canvas_buf->height;
  srcPR.rowstride = canvas_buf->width * canvas_buf->bytes;
  srcPR.data = temp_buf_data (canvas_buf);

  /*  apply the paint area to the gimage  */
1026
  gimp_image_apply_image (gimage, drawable, &srcPR,
1027
			  FALSE, NULL,
1028
			  gimp_context_get_opacity (context),
Michael Natterer's avatar
Michael Natterer committed
1029
			  gimp_context_get_paint_mode (context),
1030 1031
			  undo_tiles,  /*  specify an alternative src1  */
			  canvas_buf->x, canvas_buf->y);
1032 1033 1034 1035 1036 1037 1038

  /*  Update the undo extents  */
  ink_tool->x1 = MIN (ink_tool->x1, canvas_buf->x);
  ink_tool->y1 = MIN (ink_tool->y1, canvas_buf->y);
  ink_tool->x2 = MAX (ink_tool->x2, (canvas_buf->x + canvas_buf->width));
  ink_tool->y2 = MAX (ink_tool->y2, (canvas_buf->y + canvas_buf->height));

Michael Natterer's avatar
Michael Natterer committed
1039
  /*  Update the gimage--it is important to call gimp_image_update
1040 1041 1042
   *  instead of drawable_update because we don't want the drawable
   *  preview to be constantly invalidated
   */
1043
  gimp_drawable_offsets (drawable, &offx, &offy);
Michael Natterer's avatar
Michael Natterer committed
1044 1045 1046 1047 1048
  gimp_image_update (gimage,
                     canvas_buf->x + offx,
                     canvas_buf->y + offy,
                     canvas_buf->width,
                     canvas_buf->height);
1049 1050
}