gimpink.c 18.4 KB
Newer Older
1
/* GIMP - The GNU Image Manipulation Program
2 3
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
4
 * This program is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation; either version 3 of the License, or
7 8 9 10 11 12 13 14
 * (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
15
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
 */
Sven Neumann's avatar
Sven Neumann committed
17

18 19
#include "config.h"

Tor Lillqvist's avatar
Tor Lillqvist committed
20
#include <string.h>
21

22
#include <gegl.h>
Sven Neumann's avatar
Sven Neumann committed
23

24 25
#include "libgimpmath/gimpmath.h"

26
#include "paint-types.h"
Sven Neumann's avatar
Sven Neumann committed
27

Michael Natterer's avatar
Michael Natterer committed
28 29 30
#include "base/pixel-region.h"
#include "base/temp-buf.h"

31 32
#include "paint-funcs/paint-funcs.h"

33
#include "core/gimpdrawable.h"
34
#include "core/gimpimage.h"
35
#include "core/gimpimage-undo.h"
36

37
#include "gimpinkoptions.h"
38 39
#include "gimpink.h"
#include "gimpink-blob.h"
40
#include "gimpinkundo.h"
41

42
#include "gimp-intl.h"
43

44

45
#define SUBSAMPLE 8
46

47

48
/*  local function prototypes  */
49

Michael Natterer's avatar
Michael Natterer committed
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
static void       gimp_ink_finalize       (GObject          *object);

static void       gimp_ink_paint          (GimpPaintCore    *paint_core,
                                           GimpDrawable     *drawable,
                                           GimpPaintOptions *paint_options,
                                           const GimpCoords *coords,
                                           GimpPaintState    paint_state,
                                           guint32           time);
static TempBuf  * gimp_ink_get_paint_area (GimpPaintCore    *paint_core,
                                           GimpDrawable     *drawable,
                                           GimpPaintOptions *paint_options,
                                           const GimpCoords *coords);
static GimpUndo * gimp_ink_push_undo      (GimpPaintCore    *core,
                                           GimpImage        *image,
                                           const gchar      *undo_desc);

static void       gimp_ink_motion         (GimpPaintCore    *paint_core,
                                           GimpDrawable     *drawable,
                                           GimpPaintOptions *paint_options,
                                           const GimpCoords *coords,
                                           guint32           time);

static GimpBlob * ink_pen_ellipse         (GimpInkOptions   *options,
                                           gdouble           x_center,
                                           gdouble           y_center,
                                           gdouble           pressure,
                                           gdouble           xtilt,
                                           gdouble           ytilt,
                                           gdouble           velocity);

static void      render_blob              (GimpBlob         *blob,
                                           PixelRegion      *dest);
82

83

84
G_DEFINE_TYPE (GimpInk, gimp_ink, GIMP_TYPE_PAINT_CORE)
Michael Natterer's avatar
Michael Natterer committed
85 86

#define parent_class gimp_ink_parent_class
87

88

89
void
90 91
gimp_ink_register (Gimp                      *gimp,
                   GimpPaintRegisterCallback  callback)
92
{
93 94
  (* callback) (gimp,
                GIMP_TYPE_INK,
95
                GIMP_TYPE_INK_OPTIONS,
96 97 98
                "gimp-ink",
                _("Ink"),
                "gimp-tool-ink");
99 100 101
}

static void
102
gimp_ink_class_init (GimpInkClass *klass)
103
{
104 105
  GObjectClass       *object_class     = G_OBJECT_CLASS (klass);
  GimpPaintCoreClass *paint_core_class = GIMP_PAINT_CORE_CLASS (klass);
106

Michael Natterer's avatar
Michael Natterer committed
107
  object_class->finalize           = gimp_ink_finalize;
108

109 110
  paint_core_class->paint          = gimp_ink_paint;
  paint_core_class->get_paint_area = gimp_ink_get_paint_area;
Michael Natterer's avatar
Michael Natterer committed
111
  paint_core_class->push_undo      = gimp_ink_push_undo;
112 113 114
}

static void
115
gimp_ink_init (GimpInk *ink)
116 117 118 119
{
}

static void
120
gimp_ink_finalize (GObject *object)
121
{
122
  GimpInk *ink = GIMP_INK (object);
123

Michael Natterer's avatar
Michael Natterer committed
124 125 126 127 128 129
  if (ink->start_blob)
    {
      g_free (ink->start_blob);
      ink->start_blob = NULL;
    }

130
  if (ink->last_blob)
131
    {
132 133
      g_free (ink->last_blob);
      ink->last_blob = NULL;
134
    }
135

136
  G_OBJECT_CLASS (parent_class)->finalize (object);
137 138
}

139
static void
140 141 142
gimp_ink_paint (GimpPaintCore    *paint_core,
                GimpDrawable     *drawable,
                GimpPaintOptions *paint_options,
143
                const GimpCoords *coords,
144 145
                GimpPaintState    paint_state,
                guint32           time)
146
{
147
  GimpInk *ink = GIMP_INK (paint_core);
148 149

  switch (paint_state)
150
    {
151 152
      GimpCoords last_coords;

153
    case GIMP_PAINT_STATE_INIT:
154 155 156 157
      gimp_paint_core_get_last_coords (paint_core, &last_coords);

      if (coords->x == last_coords.x &&
          coords->y == last_coords.y)
158
        {
Michael Natterer's avatar
Michael Natterer committed
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
          /*  start with new blobs if we're not interpolating  */

          if (ink->start_blob)
            {
              g_free (ink->start_blob);
              ink->start_blob = NULL;
            }

          if (ink->last_blob)
            {
              g_free (ink->last_blob);
              ink->last_blob = NULL;
            }
        }
      else if (ink->last_blob)
        {
          /*  save the start blob of the line for undo otherwise  */

          if (ink->start_blob)
            g_free (ink->start_blob);

Michael Natterer's avatar
Michael Natterer committed
180
          ink->start_blob = gimp_blob_duplicate (ink->last_blob);
181
        }
182
      break;
183

184
    case GIMP_PAINT_STATE_MOTION:
185
      gimp_ink_motion (paint_core, drawable, paint_options, coords, time);
186
      break;
187

188
    case GIMP_PAINT_STATE_FINISH:
189 190
      break;
    }
191
}
jtl's avatar
jtl committed
192

193 194 195
static TempBuf *
gimp_ink_get_paint_area (GimpPaintCore    *paint_core,
                         GimpDrawable     *drawable,
196 197
                         GimpPaintOptions *paint_options,
                         const GimpCoords *coords)
198
{
Sven Neumann's avatar
Sven Neumann committed
199
  GimpInk *ink = GIMP_INK (paint_core);
200 201 202 203 204
  gint     x, y;
  gint     width, height;
  gint     dwidth, dheight;
  gint     x1, y1, x2, y2;
  gint     bytes;
jtl's avatar
jtl committed
205

206
  bytes = gimp_drawable_bytes_with_alpha (drawable);
207

Michael Natterer's avatar
Michael Natterer committed
208
  gimp_blob_bounds (ink->cur_blob, &x, &y, &width, &height);
209

210 211
  dwidth  = gimp_item_get_width  (GIMP_ITEM (drawable));
  dheight = gimp_item_get_height (GIMP_ITEM (drawable));
212

213 214 215 216
  x1 = CLAMP (x / SUBSAMPLE - 1,            0, dwidth);
  y1 = CLAMP (y / SUBSAMPLE - 1,            0, dheight);
  x2 = CLAMP ((x + width)  / SUBSAMPLE + 2, 0, dwidth);
  y2 = CLAMP ((y + height) / SUBSAMPLE + 2, 0, dheight);
217

218 219 220 221 222 223 224
  /*  configure the canvas buffer  */
  if ((x2 - x1) && (y2 - y1))
    paint_core->canvas_buf = temp_buf_resize (paint_core->canvas_buf, bytes,
                                              x1, y1,
                                              (x2 - x1), (y2 - y1));
  else
    return NULL;
Raph Levien's avatar
Raph Levien committed
225

226
  return paint_core->canvas_buf;
227
}
228

229 230 231 232 233 234 235 236 237 238 239 240
static GimpUndo *
gimp_ink_push_undo (GimpPaintCore *core,
                    GimpImage     *image,
                    const gchar   *undo_desc)
{
  return gimp_image_undo_push (image, GIMP_TYPE_INK_UNDO,
                               GIMP_UNDO_INK, undo_desc,
                               0,
                               "paint-core", core,
                               NULL);
}

241
static void
242 243 244
gimp_ink_motion (GimpPaintCore    *paint_core,
                 GimpDrawable     *drawable,
                 GimpPaintOptions *paint_options,
245
                 const GimpCoords *coords,
246
                 guint32           time)
247
{
248 249 250
  GimpInk        *ink     = GIMP_INK (paint_core);
  GimpInkOptions *options = GIMP_INK_OPTIONS (paint_options);
  GimpContext    *context = GIMP_CONTEXT (paint_options);
251
  GimpImage      *image;
Michael Natterer's avatar
Michael Natterer committed
252 253
  GimpBlob       *blob_union = NULL;
  GimpBlob       *blob_to_render;
254 255 256
  TempBuf        *area;
  guchar          col[MAX_CHANNELS];
  PixelRegion     blob_maskPR;
257
  GimpCoords      modified_coords;
258

259
  image = gimp_item_get_image (GIMP_ITEM (drawable));
260 261

  modified_coords = gimp_paint_core_get_smoothed_coords(paint_core, paint_options, coords);
262

263 264 265
  if (! ink->last_blob)
    {
      ink->last_blob = ink_pen_ellipse (options,
266 267 268 269 270
                                        modified_coords.x,
                                        modified_coords.y,
                                        modified_coords.pressure,
                                        modified_coords.xtilt,
                                        modified_coords.ytilt,
271
                                        100);
272

Michael Natterer's avatar
Michael Natterer committed
273 274 275
      if (ink->start_blob)
        g_free (ink->start_blob);

Michael Natterer's avatar
Michael Natterer committed
276
      ink->start_blob = gimp_blob_duplicate (ink->last_blob);
Michael Natterer's avatar
Michael Natterer committed
277

278
      blob_to_render = ink->last_blob;
279 280 281
    }
  else
    {
Michael Natterer's avatar
Michael Natterer committed
282
      GimpBlob *blob = ink_pen_ellipse (options,
283 284 285 286 287 288
                                    modified_coords.x,
                                    modified_coords.y,
                                    modified_coords.pressure,
                                    modified_coords.xtilt,
                                    modified_coords.ytilt,
                                    modified_coords.velocity * 100);
289

Michael Natterer's avatar
Michael Natterer committed
290
      blob_union = gimp_blob_convex_union (ink->last_blob, blob);
Sven Neumann's avatar
Sven Neumann committed
291

292 293
      g_free (ink->last_blob);
      ink->last_blob = blob;
294

295 296
      blob_to_render = blob_union;
    }
297

298
  /* Get the buffer */
Michael Natterer's avatar
Michael Natterer committed
299
  ink->cur_blob = blob_to_render;
300 301
  area = gimp_paint_core_get_paint_area (paint_core, drawable, paint_options,
                                         coords);
Michael Natterer's avatar
Michael Natterer committed
302 303
  ink->cur_blob = NULL;

304 305
  if (! area)
    return;
jtl's avatar
jtl committed
306

307 308
  gimp_image_get_foreground (image, context, gimp_drawable_type (drawable),
                             col);
309

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

313
  /*  color the pixels  */
314
  color_pixels (temp_buf_get_data (paint_core->canvas_buf), col,
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
                area->width * area->height, area->bytes);

  gimp_paint_core_validate_canvas_tiles (paint_core,
                                         paint_core->canvas_buf->x,
                                         paint_core->canvas_buf->y,
                                         paint_core->canvas_buf->width,
                                         paint_core->canvas_buf->height);

  /*  draw the blob directly to the canvas_tiles  */
  pixel_region_init (&blob_maskPR, paint_core->canvas_tiles,
                     paint_core->canvas_buf->x,
                     paint_core->canvas_buf->y,
                     paint_core->canvas_buf->width,
                     paint_core->canvas_buf->height,
                     TRUE);

331
  render_blob (blob_to_render, &blob_maskPR);
332 333 334 335 336 337 338 339 340 341 342 343 344 345

  /*  draw the canvas_buf using the just rendered canvas_tiles as mask */
  pixel_region_init (&blob_maskPR, paint_core->canvas_tiles,
                     paint_core->canvas_buf->x,
                     paint_core->canvas_buf->y,
                     paint_core->canvas_buf->width,
                     paint_core->canvas_buf->height,
                     FALSE);

  gimp_paint_core_paste (paint_core, &blob_maskPR, drawable,
                         GIMP_OPACITY_OPAQUE,
                         gimp_context_get_opacity (context),
                         gimp_context_get_paint_mode (context),
                         GIMP_PAINT_CONSTANT);
346

347 348
  if (blob_union)
    g_free (blob_union);
349 350
}

Michael Natterer's avatar
Michael Natterer committed
351
static GimpBlob *
352 353
ink_pen_ellipse (GimpInkOptions *options,
                 gdouble         x_center,
354 355 356 357 358
                 gdouble         y_center,
                 gdouble         pressure,
                 gdouble         xtilt,
                 gdouble         ytilt,
                 gdouble         velocity)
359
{
Michael Natterer's avatar
Michael Natterer committed
360 361 362 363 364 365 366 367
  GimpBlobFunc blob_function;
  gdouble      size;
  gdouble      tsin, tcos;
  gdouble      aspect, radmin;
  gdouble      x,y;
  gdouble      tscale;
  gdouble      tscale_c;
  gdouble      tscale_s;
368

369 370
  /* Adjust the size depending on pressure. */

371
  size = options->size * (1.0 + options->size_sensitivity *
372
                          (2.0 * pressure - 1.0));
373

374 375 376 377
  /* Adjust the size further depending on pointer velocity and
   * velocity-sensitivity.  These 'magic constants' are 'feels
   * natural' tigert-approved. --ADM
   */
378 379 380 381 382

  if (velocity < 3.0)
    velocity = 3.0;

#ifdef VERBOSE
383
  g_printerr ("%g (%g) -> ", size, velocity);
384
#endif
385

386 387 388
  size = (options->vel_sensitivity *
          ((4.5 * size) / (1.0 + options->vel_sensitivity * (2.0 * velocity)))
          + (1.0 - options->vel_sensitivity) * size);
389 390

#ifdef VERBOSE
391
  g_printerr ("%g\n", (gfloat) size);
392 393 394 395
#endif

  /* Clamp resulting size to sane limits */

396 397
  if (size > options->size * (1.0 + options->size_sensitivity))
    size = options->size * (1.0 + options->size_sensitivity);
398

399 400
  if (size * SUBSAMPLE < 1.0)
    size = 1.0 / SUBSAMPLE;
401 402

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

Raph Levien's avatar
Raph Levien committed
404
  /* I'm not happy with the way the brush widget info is combined with
405 406 407 408
   * tilt info from the brush. My personal feeling is that
   * representing both as affine transforms would make the most
   * sense. -RLL
   */
Raph Levien's avatar
Raph Levien committed
409

410
  tscale   = options->tilt_sensitivity * 10.0;
411 412 413
  tscale_c = tscale * cos (gimp_deg_to_rad (options->tilt_angle));
  tscale_s = tscale * sin (gimp_deg_to_rad (options->tilt_angle));

414
  x = (options->blob_aspect * cos (options->blob_angle) +
415
       xtilt * tscale_c - ytilt * tscale_s);
416
  y = (options->blob_aspect * sin (options->blob_angle) +
417 418
       ytilt * tscale_c + xtilt * tscale_s);

Raph Levien's avatar
Raph Levien committed
419
#ifdef VERBOSE
420 421 422
  g_printerr ("angle %g aspect %g; %g %g; %g %g\n",
              options->blob_angle, options->blob_aspect,
              tscale_c, tscale_s, x, y);
Raph Levien's avatar
Raph Levien committed
423
#endif
424

Sven Neumann's avatar
Sven Neumann committed
425
  aspect = sqrt (SQR (x) + SQR (y));
426 427 428

  if (aspect != 0)
    {
429 430
      tcos = x / aspect;
      tsin = y / aspect;
431 432 433
    }
  else
    {
434 435 436 437 438 439 440 441 442 443
      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)
    {
444
    case GIMP_INK_BLOB_TYPE_CIRCLE:
Michael Natterer's avatar
Michael Natterer committed
445
      blob_function = gimp_blob_ellipse;
446 447 448
      break;

    case GIMP_INK_BLOB_TYPE_SQUARE:
Michael Natterer's avatar
Michael Natterer committed
449
      blob_function = gimp_blob_square;
450 451 452
      break;

    case GIMP_INK_BLOB_TYPE_DIAMOND:
Michael Natterer's avatar
Michael Natterer committed
453
      blob_function = gimp_blob_diamond;
454 455 456 457
      break;

    default:
      g_return_val_if_reached (NULL);
458
      break;
459
    }
460

461 462 463 464 465 466
  return (* blob_function) (x_center * SUBSAMPLE,
                            y_center * SUBSAMPLE,
                            radmin * aspect * tcos,
                            radmin * aspect * tsin,
                            -radmin * tsin,
                            radmin * tcos);
467 468
}

469

470 471 472
/*********************************/
/*  Rendering functions          */
/*********************************/
473

474
/* Some of this stuff should probably be combined with the
475 476 477 478 479 480 481 482
 * 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.
 */

Sven Neumann's avatar
Sven Neumann committed
483 484 485 486 487
enum
{
  ROW_START,
  ROW_STOP
};
488 489 490 491

/* The insertion sort here, for SUBSAMPLE = 8, tends to beat out
 * qsort() by 4x with CFLAGS=-O2, 2x with CFLAGS=-g
 */
492 493
static void
insert_sort (gint *data,
494
             gint  n)
495
{
496
  gint i, j, k;
497

498
  for (i = 2; i < 2 * n; i += 2)
499
    {
Sven Neumann's avatar
Sven Neumann committed
500 501 502
      gint tmp1 = data[i];
      gint tmp2 = data[i + 1];

503
      j = 0;
Sven Neumann's avatar
Sven Neumann committed
504

505
      while (data[j] < tmp1)
506
        j += 2;
507

508
      for (k = i; k > j; k -= 2)
509 510 511 512
        {
          data[k]     = data[k - 2];
          data[k + 1] = data[k - 1];
        }
513

514 515
      data[j]     = tmp1;
      data[j + 1] = tmp2;
516 517 518 519 520
    }
}

static void
fill_run (guchar *dest,
521 522
          guchar  alpha,
          gint    w)
523 524 525 526 527 528 529 530
{
  if (alpha == 255)
    {
      memset (dest, 255, w);
    }
  else
    {
      while (w--)
531 532 533 534
        {
          *dest = MAX (*dest, alpha);
          dest++;
        }
535 536 537 538
    }
}

static void
Michael Natterer's avatar
Michael Natterer committed
539 540 541 542 543
render_blob_line (GimpBlob *blob,
                  guchar   *dest,
                  gint      x,
                  gint      y,
                  gint      width)
544
{
545 546 547
  gint  buf[4 * SUBSAMPLE];
  gint *data    = buf;
  gint  n       = 0;
548 549
  gint  i, j;
  gint  current = 0;  /* number of filled rows at this point
550 551
                       * in the scan line
                       */
552
  gint last_x;
553 554

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

556
  j = y * SUBSAMPLE - blob->y;
557
  for (i = 0; i < SUBSAMPLE; i++)
558 559
    {
      if (j >= blob->height)
560
        break;
561

562
      if ((j > 0) && (blob->data[j].left <= blob->data[j].right))
563 564 565 566 567 568 569
        {
          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;
          n++;
        }
570 571 572 573 574 575
      j++;
    }

  /*   If we have less than SUBSAMPLE rows, compress */
  if (n < SUBSAMPLE)
    {
576
      for (i = 0; i < 2 * n; i++)
577
        data[2 * n + i] = data[2 * SUBSAMPLE + i];
578 579 580 581 582 583 584 585 586 587 588 589
    }

  /*   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)
590
        current++;
591
      else
592
        current--;
593 594 595 596 597 598
      data += 2;
      n--;
    }

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

600 601 602
  /* Render the row */

  last_x = 0;
603
  for (i = 0; i < n;)
604
    {
605
      gint cur_x = data[2 * i] / SUBSAMPLE - x;
606
      gint pixel;
607 608 609

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

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

      while (i<n)
616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
        {
          gint tmp_x = data[2 * i] / SUBSAMPLE;

          if (tmp_x - x != cur_x)
            break;

          if (data[2 * i + 1] == ROW_START)
            {
              current++;
              pixel += ((tmp_x + 1) * SUBSAMPLE) - data[2 * i];
            }
          else
            {
              current--;
              pixel -= ((tmp_x + 1) * SUBSAMPLE) - data[2 * i];
            }

          i++;
        }
635

636
      dest[cur_x] = MAX (dest[cur_x], (pixel * 255) / (SUBSAMPLE * SUBSAMPLE));
637 638 639 640 641

      last_x = cur_x + 1;
    }

  if (current != 0)
642
    fill_run (dest + last_x, (255 * current)/ SUBSAMPLE, width - last_x);
643 644 645
}

static void
Michael Natterer's avatar
Michael Natterer committed
646
render_blob (GimpBlob    *blob,
647
             PixelRegion *dest)
648
{
649
  gpointer  pr;
650

651 652
  for (pr = pixel_regions_register (1, dest);
       pr != NULL;
653 654
       pr = pixel_regions_process (pr))
    {
Sven Neumann's avatar
Sven Neumann committed
655 656 657
      guchar *d = dest->data;
      gint    h = dest->h;
      gint    y;
658

Sven Neumann's avatar
Sven Neumann committed
659
      for (y = 0; y < h; y++, d += dest->rowstride)
660
        {
Sven Neumann's avatar
Sven Neumann committed
661
          render_blob_line (blob, d, dest->x, dest->y + y, dest->w);
662
        }
663 664
    }
}