gimpink.c 29.5 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 "widgets/gimphelp-ids.h"

48 49
#include "display/gimpdisplay.h"

50
#include "gimpinkoptions.h"
51 52
#include "gimpinktool.h"
#include "gimpinktool-blob.h"
53
#include "gimptoolcontrol.h"
54

55
#include "gimp-intl.h"
56

57

58
#define SUBSAMPLE 8
59

60

61
/*  local function prototypes  */
62

63 64 65
static void        gimp_ink_tool_class_init      (GimpInkToolClass *klass);
static void        gimp_ink_tool_init            (GimpInkTool      *tool);

66
static void        gimp_ink_tool_finalize        (GObject          *object);
67

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

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

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

110 111 112
static void        ink_init             (GimpInkTool     *ink_tool,
					 GimpDrawable    *drawable,
					 gdouble          x,
113
					 gdouble          y);
114
static void        ink_finish           (GimpInkTool     *ink_tool,
115
					 GimpDrawable    *drawable);
116 117
static void        ink_cleanup          (void);

Michael Natterer's avatar
Michael Natterer committed
118
/*  Rendering functions  */
119 120
static void        ink_set_paint_area   (GimpInkTool     *ink_tool,
					 GimpDrawable    *drawable,
121
					 Blob            *blob);
122
static void        ink_paste            (GimpInkTool     *ink_tool,
123 124 125 126 127 128 129 130
					 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,
131
					 gint             x,
132
					 gint             y,
133
					 gint             w,
134
					 gint             h);
135
static void        ink_set_canvas_tiles (gint             x,
136
					 gint             y,
137
					 gint             w,
138
					 gint             h);
139 140


141 142 143 144 145 146 147 148 149 150 151 152 153
/* 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;

154
static GimpToolClass *parent_class = NULL;
155

156

157
/*  public functions  */
158

159
void
Nate Summers's avatar
Nate Summers committed
160
gimp_ink_tool_register (GimpToolRegisterCallback  callback,
161
                        gpointer                  data)
162
{
Nate Summers's avatar
Nate Summers committed
163
  (* callback) (GIMP_TYPE_INK_TOOL,
164 165
                GIMP_TYPE_INK_OPTIONS,
                gimp_ink_options_gui,
166 167
                GIMP_CONTEXT_OPACITY_MASK |
                GIMP_CONTEXT_PAINT_MODE_MASK,
168
                "gimp-ink-tool",
169
                _("Ink"),
170
                _("Draw in ink"),
171
                N_("/Tools/Paint Tools/In_k"), "K",
172
                NULL, GIMP_HELP_TOOL_INK,
Nate Summers's avatar
Nate Summers committed
173
                GIMP_STOCK_TOOL_INK,
174
                data);
175 176
}

177
GType
178 179
gimp_ink_tool_get_type (void)
{
180
  static GType tool_type = 0;
181 182 183

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

197
      tool_type = g_type_register_static (GIMP_TYPE_TOOL,
198
					  "GimpInkTool",
199
                                          &tool_info, 0);
200 201 202 203 204
    }

  return tool_type;
}

205 206 207

/*  private functions  */

208 209 210
static void
gimp_ink_tool_class_init (GimpInkToolClass *klass)
{
211
  GObjectClass   *object_class;
212 213
  GimpToolClass  *tool_class;

214 215
  object_class = G_OBJECT_CLASS (klass);
  tool_class   = GIMP_TOOL_CLASS (klass);
216

217
  parent_class = g_type_class_peek_parent (klass);
218

219
  object_class->finalize     = gimp_ink_tool_finalize;
220

221 222 223 224 225
  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;
226 227 228 229 230 231 232 233
}

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

  tool = GIMP_TOOL (ink_tool);
234

235 236
  gimp_tool_control_set_motion_mode (tool->control, GIMP_MOTION_MODE_EXACT);
  gimp_tool_control_set_tool_cursor (tool->control, GIMP_INK_TOOL_CURSOR);
237 238 239
}

static void
240
gimp_ink_tool_finalize (GObject *object)
241 242 243 244 245 246
{
  GimpInkTool *ink_tool;

  ink_tool = GIMP_INK_TOOL (object);

  if (ink_tool->last_blob)
247 248 249 250
    {
      g_free (ink_tool->last_blob);
      ink_tool->last_blob = NULL;
    }
251 252 253

  ink_cleanup ();

254
  G_OBJECT_CLASS (parent_class)->finalize (object);
255 256
}

257
static void
258 259 260
gimp_ink_tool_control (GimpTool       *tool,
                       GimpToolAction  action,
                       GimpDisplay    *gdisp)
261
{
262
  GimpInkTool *ink_tool;
263

264
  ink_tool = GIMP_INK_TOOL (tool);
265

266 267 268 269
  switch (action)
    {
    case PAUSE:
      break;
270

271 272
    case RESUME:
      break;
273

274 275 276
    case HALT:
      ink_cleanup ();
      break;
277

278 279 280
    default:
      break;
    }
281 282

  GIMP_TOOL_CLASS (parent_class)->control (tool, action, gdisp);
283 284
}

285 286 287 288 289 290
static void
gimp_ink_tool_button_press (GimpTool        *tool,
                            GimpCoords      *coords,
                            guint32          time,
                            GdkModifierType  state,
                            GimpDisplay     *gdisp)
291
{
292 293 294
  GimpInkTool    *ink_tool;
  GimpInkOptions *options;
  GimpDrawable   *drawable;
295
  GimpCoords      curr_coords;
296
  gint            off_x, off_y;
297
  Blob           *b;
298

299
  ink_tool = GIMP_INK_TOOL (tool);
300
  options  = GIMP_INK_OPTIONS (tool->tool_info->tool_options);
301

302
  drawable = gimp_image_active_drawable (gdisp->gimage);
jtl's avatar
jtl committed
303

304 305
  curr_coords = *coords;

306
  gimp_item_offsets (GIMP_ITEM (drawable), &off_x, &off_y);
307

308 309
  curr_coords.x -= off_x;
  curr_coords.y -= off_y;
310 311

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

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

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

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

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

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

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

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

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

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

352
  ink_tool = GIMP_INK_TOOL (tool);
353

354
  gimage = gdisp->gimage;
355

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

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

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

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

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

388
  ink_tool = GIMP_INK_TOOL (tool);
389
  options  = GIMP_INK_OPTIONS (tool->tool_info->tool_options);
390

391
  drawable = gimp_image_active_drawable (gdisp->gimage);
392

393 394
  curr_coords = *coords;

395
  gimp_item_offsets (GIMP_ITEM (drawable), &off_x, &off_y);
396

397 398
  curr_coords.x -= off_x;
  curr_coords.y -= off_y;
399

400
  lasttime = ink_tool->last_time;
401

402 403
  time_smoother_add (ink_tool, time);
  thistime = ink_tool->last_time = time_smoother_result (ink_tool);
404

405 406 407 408 409 410
  /* 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 */
411

412 413
  if (thistime == lasttime)
    thistime = lasttime + 1;
414

415 416 417
  if (ink_tool->init_velocity)
    {
      dist_smoother_init (ink_tool,
418 419 420 421
			  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)));
422 423 424 425 426
      ink_tool->init_velocity = FALSE;
    }
  else
    {
      dist_smoother_add (ink_tool,
427 428 429 430
			 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)));
431

432 433
      dist = dist_smoother_result (ink_tool);
    }
434

435 436
  ink_tool->lastx = curr_coords.x;
  ink_tool->lasty = curr_coords.y;
437

438
  velocity = 10.0 * sqrt ((dist) / (gdouble) (thistime - lasttime));
439

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

448 449 450
  blob_union = blob_convex_union (ink_tool->last_blob, b);
  g_free (ink_tool->last_blob);
  ink_tool->last_blob = b;
451

452
  ink_paste (ink_tool, drawable, blob_union);
453
  g_free (blob_union);
454

455 456
  gimp_display_flush_now (gdisp);
}
jtl's avatar
jtl committed
457

458 459 460 461 462 463
static void
gimp_ink_tool_cursor_update (GimpTool        *tool,
                             GimpCoords      *coords,
                             GdkModifierType  state,
                             GimpDisplay     *gdisp)
{
464
  GdkCursorType ctype = GDK_TOP_LEFT_ARROW;
465

466
  if (gimp_display_coords_in_active_drawable (gdisp, coords))
467
    {
468 469 470 471 472 473 474
      /*  One more test--is there a selected region?
       *  if so, is cursor inside?
       */
      if (gimp_image_mask_is_empty (gdisp->gimage))
        ctype = GIMP_MOUSE_CURSOR;
      else if (gimp_image_mask_value (gdisp->gimage, coords->x, coords->y))
        ctype = GIMP_MOUSE_CURSOR;
475
    }
476

477
  gimp_tool_control_set_cursor (tool->control, ctype);
478 479

  GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, gdisp);
480 481
}

482

483
static Blob *
484 485 486 487 488 489 490
ink_pen_ellipse (GimpInkOptions *options,
                 gdouble         x_center,
		 gdouble         y_center,
		 gdouble         pressure,
		 gdouble         xtilt,
		 gdouble         ytilt,
		 gdouble         velocity)
491
{
492 493 494 495 496 497 498 499
  BlobFunc function = blob_ellipse;
  gdouble  size;
  gdouble  tsin, tcos;
  gdouble  aspect, radmin;
  gdouble  x,y;
  gdouble  tscale;
  gdouble  tscale_c;
  gdouble  tscale_s;
500

501 502
  /* Adjust the size depending on pressure. */

503
  size = options->size * (1.0 + options->size_sensitivity *
504
                          (2.0 * pressure - 1.0) );
505 506 507 508 509 510 511 512 513

  /* 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
514
  g_print ("%f (%f) -> ", (float)size, (float)velocity);
515
#endif
516

517 518 519
  size = (options->vel_sensitivity *
          ((4.5 * size) / (1.0 + options->vel_sensitivity * (2.0 * velocity)))
          + (1.0 - options->vel_sensitivity) * size);
520 521

#ifdef VERBOSE
522
  g_print ("%f\n", (float)size);
523 524 525 526
#endif

  /* Clamp resulting size to sane limits */

527 528
  if (size > options->size * (1.0 + options->size_sensitivity))
    size = options->size * (1.0 + options->size_sensitivity);
529

530 531
  if (size * SUBSAMPLE < 1.0)
    size = 1.0 / SUBSAMPLE;
532 533

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

Raph Levien's avatar
Raph Levien committed
535 536 537 538
  /* 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 */

539 540 541 542
  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));

543
  x = (options->blob_aspect * cos (options->blob_angle) +
544
       xtilt * tscale_c - ytilt * tscale_s);
545
  y = (options->blob_aspect * sin (options->blob_angle) +
546 547
       ytilt * tscale_c + xtilt * tscale_s);

Raph Levien's avatar
Raph Levien committed
548 549
#ifdef VERBOSE
  g_print ("angle %g aspect %g; %g %g; %g %g\n",
550
	   options->blob_angle, options->blob_aspect, tscale_c, tscale_s, x, y);
Raph Levien's avatar
Raph Levien committed
551
#endif
552 553

  aspect = sqrt (x*x + y*y);
554 555 556

  if (aspect != 0)
    {
557 558
      tcos = x / aspect;
      tsin = y / aspect;
559 560 561
    }
  else
    {
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582
      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;
583
    }
584

585 586 587
  return (* function) (x_center * SUBSAMPLE,
                       y_center * SUBSAMPLE,
                       radmin * aspect * tcos,
588
                       radmin * aspect * tsin,
589 590
                       -radmin * tsin,
                       radmin * tcos);
591 592 593
}

static void
594 595
dist_smoother_init (GimpInkTool *ink_tool,
		    gdouble      initval)
596
{
597
  gint i;
598

599
  ink_tool->dt_index = 0;
600

601
  for (i = 0; i < DIST_SMOOTHER_BUFFER; i++)
602
    {
603 604 605
      ink_tool->dt_buffer[i] = initval;
    }
}
606

607 608 609 610 611
static gdouble
dist_smoother_result (GimpInkTool *ink_tool)
{
  gint    i;
  gdouble result = 0.0;
612

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

  return (result / (gdouble) DIST_SMOOTHER_BUFFER);
619 620 621
}

static void
622 623
dist_smoother_add (GimpInkTool *ink_tool,
		   gdouble      value)
624
{
625
  ink_tool->dt_buffer[ink_tool->dt_index] = value;
Michael Natterer's avatar
Michael Natterer committed
626

627 628 629
  if ((++ink_tool->dt_index) == DIST_SMOOTHER_BUFFER)
    ink_tool->dt_index = 0;
}
630 631


632 633 634 635 636
static void
time_smoother_init (GimpInkTool *ink_tool,
		    guint32      initval)
{
  gint i;
637

638
  ink_tool->ts_index = 0;
639

640
  for (i = 0; i < TIME_SMOOTHER_BUFFER; i++)
641
    {
642
      ink_tool->ts_buffer[i] = initval;
643
    }
644 645 646 647 648 649 650 651 652
}

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

  for (i = 0; i < TIME_SMOOTHER_BUFFER; i++)
653
    {
654
      result += ink_tool->ts_buffer[i];
655
    }
656

657 658 659 660 661 662
#ifdef _MSC_VER
  return (gdouble) (gint64) (result / TIME_SMOOTHER_BUFFER);
#else
  return (result / TIME_SMOOTHER_BUFFER);
#endif
}
663 664

static void
665 666
time_smoother_add (GimpInkTool *ink_tool,
		   guint32      value)
667 668 669 670 671 672 673
{
  ink_tool->ts_buffer[ink_tool->ts_index] = value;

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

674
static void
675
ink_init (GimpInkTool  *ink_tool,
676
	  GimpDrawable *drawable,
677 678
	  gdouble       x,
	  gdouble       y)
679
{
680 681
  GimpItem *item = GIMP_ITEM (drawable);

682
  /*  Allocate the undo structure  */
683
  if (undo_tiles)
684
    tile_manager_unref (undo_tiles);
685

686 687
  undo_tiles = tile_manager_new (gimp_item_width  (item),
				 gimp_item_height (item),
688
				 gimp_drawable_bytes (drawable));
689 690

  /*  Allocate the canvas blocks structure  */
691 692 693
  if (canvas_tiles)
    tile_manager_unref (canvas_tiles);

694 695
  canvas_tiles = tile_manager_new (gimp_item_width  (item),
				   gimp_item_height (item), 1);
696 697 698 699 700 701 702

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

static void
703 704
ink_finish (GimpInkTool  *ink_tool,
	    GimpDrawable *drawable)
705
{
706
  gimp_drawable_push_undo (drawable, _("Ink"),
707 708 709
                           ink_tool->x1, ink_tool->y1,
                           ink_tool->x2, ink_tool->y2,
                           undo_tiles, TRUE);
710 711

  tile_manager_unref (undo_tiles);
712 713 714 715 716
  undo_tiles = NULL;

  /*  invalidate the drawable--have to do it here, because
   *  it is not done during the actual painting.
   */
717
  gimp_viewable_invalidate_preview (GIMP_VIEWABLE (drawable));
718 719 720 721 722 723 724 725
}

static void
ink_cleanup (void)
{
  /*  If the undo tiles exist, nuke them  */
  if (undo_tiles)
    {
726
      tile_manager_unref (undo_tiles);
727 728 729 730 731 732
      undo_tiles = NULL;
    }

  /*  If the canvas blocks exist, nuke them  */
  if (canvas_tiles)
    {
733
      tile_manager_unref (canvas_tiles);
734 735 736 737 738
      canvas_tiles = NULL;
    }

  /*  Free the temporary buffer if it exist  */
  if (canvas_buf)
739 740 741 742
    {
      temp_buf_free (canvas_buf);
      canvas_buf = NULL;
    }
743 744 745 746 747 748
}

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

749
/* Some of this stuff should probably be combined with the
750 751 752 753 754 755 756 757 758
 * 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
759 760
ink_set_paint_area (GimpInkTool  *ink_tool,
		    GimpDrawable *drawable,
761 762
		    Blob         *blob)
{
763 764 765 766
  GimpItem *item = GIMP_ITEM (drawable);
  gint      x, y, width, height;
  gint      x1, y1, x2, y2;
  gint      bytes;
767

768 769
  blob_bounds (blob, &x, &y, &width, &height);

770 771
  bytes = gimp_drawable_has_alpha (drawable) ?
    gimp_drawable_bytes (drawable) : gimp_drawable_bytes (drawable) + 1;
772

773 774 775 776
  x1 = CLAMP (x/SUBSAMPLE - 1,            0, gimp_item_width  (item));
  y1 = CLAMP (y/SUBSAMPLE - 1,            0, gimp_item_height (item));
  x2 = CLAMP ((x + width)/SUBSAMPLE + 2,  0, gimp_item_width  (item));
  y2 = CLAMP ((y + height)/SUBSAMPLE + 2, 0, gimp_item_height (item));
777 778 779 780 781 782 783 784 785 786 787 788

  /*  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
 */
789 790 791
static void
insert_sort (gint *data,
	     gint  n)
792
{
793 794
  gint i, j, k;
  gint tmp1, tmp2;
795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817

  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,
818
	  gint    w)
819 820 821 822 823 824 825 826 827
{
  if (alpha == 255)
    {
      memset (dest, 255, w);
    }
  else
    {
      while (w--)
	{
828
	  *dest = MAX(*dest, alpha);
829 830 831 832 833 834
	  dest++;
	}
    }
}

static void
835 836 837 838 839
render_blob_line (Blob   *blob,
		  guchar *dest,
		  gint    x,
		  gint    y,
		  gint    width)
840
{
841 842 843
  gint  buf[4 * SUBSAMPLE];
  gint *data    = buf;
  gint  n       = 0;
844 845 846 847 848
  gint  i, j;
  gint  current = 0;  /* number of filled rows at this point
		       * in the scan line
		       */
  gint last_x;
849 850

  /* Sort start and ends for all lines */
851

852
  j = y * SUBSAMPLE - blob->y;
853
  for (i = 0; i < SUBSAMPLE; i++)
854 855 856
    {
      if (j >= blob->height)
	break;
857

858 859
      if ((j > 0) && (blob->data[j].left <= blob->data[j].right))
	{
860 861 862 863
	  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;
864 865 866 867 868 869 870 871
	  n++;
	}
      j++;
    }

  /*   If we have less than SUBSAMPLE rows, compress */
  if (n < SUBSAMPLE)
    {
872 873
      for (i = 0; i < 2 * n; i++)
	data[2 * n + i] = data[2 * SUBSAMPLE + i];
874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894
    }

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