gimpink.c 18.6 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
      gimp_paint_core_get_last_coords (paint_core, &last_coords);
155
      ink->queue = gimp_circular_queue_new(sizeof(GimpCoords), paint_options->smoothing_options->smoothing_history);
156 157 158

      if (coords->x == last_coords.x &&
          coords->y == last_coords.y)
159
        {
Michael Natterer's avatar
Michael Natterer committed
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
          /*  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
181
          ink->start_blob = gimp_blob_duplicate (ink->last_blob);
182
        }
183
      break;
184

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

189
    case GIMP_PAINT_STATE_FINISH:
190 191
      gimp_circular_queue_free(ink->queue);
      ink->queue = NULL;
192 193
      break;
    }
194
}
jtl's avatar
jtl committed
195

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

209
  bytes = gimp_drawable_bytes_with_alpha (drawable);
210

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

213 214
  dwidth  = gimp_item_get_width  (GIMP_ITEM (drawable));
  dheight = gimp_item_get_height (GIMP_ITEM (drawable));
215

216 217 218 219
  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);
220

221 222 223 224 225 226 227
  /*  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
228

229
  return paint_core->canvas_buf;
230
}
231

232 233 234 235 236 237 238 239 240 241 242 243
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);
}

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

262
  image = gimp_item_get_image (GIMP_ITEM (drawable));
263 264
  
  modified_coords = gimp_paint_options_get_smoothed_coords(paint_options, coords, ink->queue);
265

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

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

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

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

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

295 296
      g_free (ink->last_blob);
      ink->last_blob = blob;
297

298 299
      blob_to_render = blob_union;
    }
300

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

307 308
  if (! area)
    return;
jtl's avatar
jtl committed
309

310 311
  gimp_image_get_foreground (image, context, gimp_drawable_type (drawable),
                             col);
312

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

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

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

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

350 351
  if (blob_union)
    g_free (blob_union);
352 353
}

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

372 373
  /* Adjust the size depending on pressure. */

374
  size = options->size * (1.0 + options->size_sensitivity *
375
                          (2.0 * pressure - 1.0));
376

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

  if (velocity < 3.0)
    velocity = 3.0;

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

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

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

  /* Clamp resulting size to sane limits */

399 400
  if (size > options->size * (1.0 + options->size_sensitivity))
    size = options->size * (1.0 + options->size_sensitivity);
401

402 403
  if (size * SUBSAMPLE < 1.0)
    size = 1.0 / SUBSAMPLE;
404 405

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

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

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

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

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

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

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

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

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

    default:
      g_return_val_if_reached (NULL);
461
      break;
462
    }
463

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

472

473 474 475
/*********************************/
/*  Rendering functions          */
/*********************************/
476

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

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

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

506
      j = 0;
Sven Neumann's avatar
Sven Neumann committed
507

508
      while (data[j] < tmp1)
509
        j += 2;
510

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

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

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

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

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

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

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

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

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

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

603 604 605
  /* Render the row */

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

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

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

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

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

      last_x = cur_x + 1;
    }

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

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

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

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