gimpink.c 18.5 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 262
  modified_coords = gimp_paint_core_get_smoothed_coords (paint_core,
                                                         paint_options, coords);
263

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

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

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

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

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

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

296 297
      blob_to_render = blob_union;
    }
298

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

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

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

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

314
  /*  color the pixels  */
315
  color_pixels (temp_buf_get_data (paint_core->canvas_buf), col,
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
                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);

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

  /*  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);
347

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

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

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

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

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

  if (velocity < 3.0)
    velocity = 3.0;

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

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

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

  /* Clamp resulting size to sane limits */

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

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

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

Raph Levien's avatar
Raph Levien committed
405
  /* I'm not happy with the way the brush widget info is combined with
406 407 408 409
   * 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
410

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

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

Raph Levien's avatar
Raph Levien committed
420
#ifdef VERBOSE
421 422 423
  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
424
#endif
425

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

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

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

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

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

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

470

471 472 473
/*********************************/
/*  Rendering functions          */
/*********************************/
474

475
/* Some of this stuff should probably be combined with the
476 477 478 479 480 481 482 483
 * 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
484 485 486 487 488
enum
{
  ROW_START,
  ROW_STOP
};
489 490 491 492

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

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

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

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

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

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

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

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

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

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

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

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

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

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

601 602 603
  /* Render the row */

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

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

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

      while (i<n)
617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635
        {
          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++;
        }
636

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

      last_x = cur_x + 1;
    }

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

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

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

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