gimpink.c 29.7 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
#include "core/gimpchannel.h"
39
#include "core/gimpimage.h"
40
#include "core/gimpimage-undo-push.h"
41
#include "core/gimptoolinfo.h"
42

43 44
#include "paint/gimppaintoptions.h"

45 46
#include "widgets/gimphelp-ids.h"

47 48
#include "display/gimpdisplay.h"

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

54
#include "gimp-intl.h"
55

56

57
#define SUBSAMPLE 8
58

59

60
/*  local function prototypes  */
61

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

65
static void        gimp_ink_tool_finalize        (GObject          *object);
66

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

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

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

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

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


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

153
static GimpToolClass *parent_class = NULL;
154

155

156
/*  public functions  */
157

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

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

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

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

  return tool_type;
}

204 205 206

/*  private functions  */

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

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

216
  parent_class = g_type_class_peek_parent (klass);
217

218
  object_class->finalize     = gimp_ink_tool_finalize;
219

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

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

  tool = GIMP_TOOL (ink_tool);
233

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

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

  ink_tool = GIMP_INK_TOOL (object);

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

  ink_cleanup ();

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

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

263
  ink_tool = GIMP_INK_TOOL (tool);
264

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

270 271
    case RESUME:
      break;
272

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

277 278 279
    default:
      break;
    }
280 281

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

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

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

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

303 304
  curr_coords = *coords;

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

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

  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
  GimpCoords      curr_coords;
380
  gint            off_x, off_y;
381 382 383 384 385
  Blob           *b;
  Blob           *blob_union;
  gdouble         velocity;
  gdouble         dist;
  gdouble         lasttime, thistime;
386

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

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

392 393
  curr_coords = *coords;

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

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

399
  lasttime = ink_tool->last_time;
400

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

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

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

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

431 432
      dist = dist_smoother_result (ink_tool);
    }
433

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

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

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

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

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

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

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

465
  if (gimp_display_coords_in_active_drawable (gdisp, coords))
466
    {
467 468
      GimpChannel *selection = gimp_image_get_mask (gdisp->gimage);

469 470 471
      /*  One more test--is there a selected region?
       *  if so, is cursor inside?
       */
472
      if (gimp_channel_is_empty (selection))
473
        ctype = GIMP_MOUSE_CURSOR;
474
      else if (gimp_channel_value (selection, coords->x, coords->y))
475
        ctype = GIMP_MOUSE_CURSOR;
476
    }
477

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

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

483

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

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

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

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

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

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

  /* Clamp resulting size to sane limits */

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

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

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

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

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

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

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

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

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

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

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

600
  ink_tool->dt_index = 0;
601

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

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

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

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

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

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


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

639
  ink_tool->ts_index = 0;
640

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

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

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

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

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

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

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

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

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

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

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

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

static void
704 705
ink_finish (GimpInkTool  *ink_tool,
	    GimpDrawable *drawable)
706
{
707 708
  GimpImage *gimage;

709
  gimp_drawable_push_undo (drawable, _("Ink"),
710 711 712
                           ink_tool->x1, ink_tool->y1,
                           ink_tool->x2, ink_tool->y2,
                           undo_tiles, TRUE);
713 714

  tile_manager_unref (undo_tiles);
715 716
  undo_tiles = NULL;

717 718 719
  gimage = gimp_item_get_image (GIMP_ITEM (drawable));

  /*  invalidate the previews -- have to do it here, because
720 721
   *  it is not done during the actual painting.
   */
722
  gimp_viewable_invalidate_preview (GIMP_VIEWABLE (drawable));
723
  gimp_viewable_invalidate_preview (GIMP_VIEWABLE (gimage));
724 725 726 727 728 729 730 731
}

static void
ink_cleanup (void)
{
  /*  If the undo tiles exist, nuke them  */
  if (undo_tiles)
    {
732
      tile_manager_unref (undo_tiles);
733 734 735 736 737 738
      undo_tiles = NULL;
    }

  /*  If the canvas blocks exist, nuke them  */
  if (canvas_tiles)
    {
739
      tile_manager_unref (canvas_tiles);
740 741 742 743 744
      canvas_tiles = NULL;
    }

  /*  Free the temporary buffer if it exist  */
  if (canvas_buf)
745 746 747 748
    {
      temp_buf_free (canvas_buf);
      canvas_buf = NULL;
    }
749 750 751 752 753 754
}

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

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

774 775
  blob_bounds (blob, &x, &y, &width, &height);

776
  bytes = gimp_drawable_bytes_with_alpha (drawable);
777

778 779 780 781
  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));
782 783 784 785 786 787 788 789 790 791 792 793

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

  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,
823
	  gint    w)
824 825 826 827 828 829 830 831 832
{
  if (alpha == 255)
    {
      memset (dest, 255, w);
    }
  else
    {
      while (w--)
	{
833
	  *dest = MAX(*dest, alpha);
834 835 836 837 838 839
	  dest++;
	}
    }
}

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

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

857
  j = y * SUBSAMPLE - blob->y;
858
  for (i = 0; i < SUBSAMPLE; i++)
859 860 861
    {
      if (j >= blob->height)
	break;
862

863 864
      if ((j > 0) && (blob->data[j].left <= blob->data[j].right))
	{
865 866 867 868
	  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;
869 870 871 872 873 874 875 876
	  n++;
	}
      j++;
    }

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

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

901 902 903
  /* Render the row */

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

      /* Fill in portion leading up to this pixel */
      if (current && cur_x != last_x)
911
	fill_run (dest + last_x, (255 * current) / SUBSAMPLE, cur_x - last_x);
912 913

      /* Compute the value for this pixel */
914
      pixel = current * SUBSAMPLE;
915 916 917

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

920 921 922
	  if (tmp_x - x != cur_x)
	    break;

923
	  if (data[2 * i + 1] == ROW_START)
924 925
	    {
	      current++;
926
	      pixel += ((tmp_x + 1) * SUBSAMPLE) - data[2 * i];
927 928 929 930
	    }
	  else
	    {
	      current--;
931
	      pixel -= ((tmp_x + 1) * SUBSAMPLE) - data[2 * i];
932
	    }
933

934 935 936
	  i++;
	}

937
      dest[cur_x] = MAX (dest[cur_x], (pixel * 255) / (SUBSAMPLE * SUBSAMPLE));
938 939 940 941 942

      last_x = cur_x + 1;
    }

  if (current != 0)
Michael Natterer's avatar