gimpink.c 21 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"

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

23
#include <glib-object.h>
Sven Neumann's avatar
Sven Neumann committed
24

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

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

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

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

37
#include "gimpinkoptions.h"
38 39
#include "gimpink.h"
#include "gimpink-blob.h"
Michael Natterer's avatar
Michael Natterer committed
40
#include "gimpink-undo.h"
41

42
#include "gimp-intl.h"
43

44

45
#define SUBSAMPLE 8
46

47

48
/*  local function prototypes  */
49

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
static void      gimp_ink_class_init     (GimpInkClass     *klass);
static void      gimp_ink_init           (GimpInk          *ink);

static void      gimp_ink_finalize       (GObject          *object);

static void      gimp_ink_paint          (GimpPaintCore    *paint_core,
                                          GimpDrawable     *drawable,
                                          GimpPaintOptions *paint_options,
                                          GimpPaintState    paint_state,
                                          guint32           time);
static TempBuf * gimp_ink_get_paint_area (GimpPaintCore    *paint_core,
                                          GimpDrawable     *drawable,
                                          GimpPaintOptions *paint_options);

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

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

static void      time_smoother_add       (GimpInk          *ink,
                                          guint32           value);
79
static guint32   time_smoother_result    (GimpInk          *ink);
80 81
static void      time_smoother_init      (GimpInk          *ink,
                                          guint32           initval);
82

83 84 85 86 87 88 89 90
static void      dist_smoother_add       (GimpInk          *ink,
                                          gdouble           value);
static gdouble   dist_smoother_result    (GimpInk          *ink);
static void      dist_smoother_init      (GimpInk          *ink,
                                          gdouble           initval);

static void      render_blob             (Blob             *blob,
                                          PixelRegion      *dest);
91

92

93
static GimpPaintCoreClass *parent_class = NULL;
94

95

96
void
97 98
gimp_ink_register (Gimp                      *gimp,
                   GimpPaintRegisterCallback  callback)
99
{
100 101
  (* callback) (gimp,
                GIMP_TYPE_INK,
102
                GIMP_TYPE_INK_OPTIONS,
103
                _("Ink"));
104 105
}

106
GType
107
gimp_ink_get_type (void)
108
{
109
  static GType type = 0;
110

111
  if (! type)
112
    {
113
      static const GTypeInfo info =
114
      {
115 116 117 118 119 120 121 122 123
        sizeof (GimpInkClass),
        (GBaseInitFunc) NULL,
        (GBaseFinalizeFunc) NULL,
        (GClassInitFunc) gimp_ink_class_init,
        NULL,           /* class_finalize */
        NULL,           /* class_data     */
        sizeof (GimpInk),
        0,              /* n_preallocs    */
        (GInstanceInitFunc) gimp_ink_init,
124 125
      };

126 127 128
      type = g_type_register_static (GIMP_TYPE_PAINT_CORE,
                                     "GimpInk",
                                     &info, 0);
129 130
    }

131
  return type;
132 133 134
}

static void
135
gimp_ink_class_init (GimpInkClass *klass)
136
{
137 138
  GObjectClass       *object_class     = G_OBJECT_CLASS (klass);
  GimpPaintCoreClass *paint_core_class = GIMP_PAINT_CORE_CLASS (klass);
139

140
  parent_class = g_type_class_peek_parent (klass);
141

142
  object_class->finalize  = gimp_ink_finalize;
143

144 145
  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
146
  paint_core_class->push_undo      = gimp_ink_push_undo;
147 148 149
}

static void
150
gimp_ink_init (GimpInk *ink)
151 152 153 154
{
}

static void
155
gimp_ink_finalize (GObject *object)
156
{
157
  GimpInk *ink = GIMP_INK (object);
158

Michael Natterer's avatar
Michael Natterer committed
159 160 161 162 163 164
  if (ink->start_blob)
    {
      g_free (ink->start_blob);
      ink->start_blob = NULL;
    }

165
  if (ink->last_blob)
166
    {
167 168
      g_free (ink->last_blob);
      ink->last_blob = NULL;
169
    }
170

171
  G_OBJECT_CLASS (parent_class)->finalize (object);
172 173
}

174
static void
175 176 177 178 179
gimp_ink_paint (GimpPaintCore    *paint_core,
                GimpDrawable     *drawable,
                GimpPaintOptions *paint_options,
                GimpPaintState    paint_state,
                guint32           time)
180
{
181
  GimpInk *ink = GIMP_INK (paint_core);
182 183

  switch (paint_state)
184
    {
185
    case GIMP_PAINT_STATE_INIT:
Michael Natterer's avatar
Michael Natterer committed
186
      if (paint_core->cur_coords.x == paint_core->last_coords.x &&
187 188
          paint_core->cur_coords.y == paint_core->last_coords.y)
        {
Michael Natterer's avatar
Michael Natterer committed
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
          /*  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);

          ink->start_blob = blob_duplicate (ink->last_blob);
211
        }
212
      break;
213

214
    case GIMP_PAINT_STATE_MOTION:
215
      gimp_ink_motion (paint_core, drawable, paint_options, time);
216
      break;
217

218
    case GIMP_PAINT_STATE_FINISH:
219 220
      break;
    }
221
}
jtl's avatar
jtl committed
222

223 224 225 226
static TempBuf *
gimp_ink_get_paint_area (GimpPaintCore    *paint_core,
                         GimpDrawable     *drawable,
                         GimpPaintOptions *paint_options)
227
{
228 229 230 231 232 233
  GimpInk *ink  = GIMP_INK (paint_core);
  gint     x, y;
  gint     width, height;
  gint     dwidth, dheight;
  gint     x1, y1, x2, y2;
  gint     bytes;
jtl's avatar
jtl committed
234

235
  bytes = gimp_drawable_bytes_with_alpha (drawable);
236

Michael Natterer's avatar
Michael Natterer committed
237
  blob_bounds (ink->cur_blob, &x, &y, &width, &height);
238

239 240
  dwidth  = gimp_item_width  (GIMP_ITEM (drawable));
  dheight = gimp_item_height (GIMP_ITEM (drawable));
241

242 243 244 245
  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);
246

247 248 249 250 251 252 253
  /*  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
254

255
  return paint_core->canvas_buf;
256
}
257

258
static void
259 260 261 262
gimp_ink_motion (GimpPaintCore    *paint_core,
                 GimpDrawable     *drawable,
                 GimpPaintOptions *paint_options,
                 guint32           time)
263
{
264 265 266 267
  GimpInk        *ink     = GIMP_INK (paint_core);
  GimpInkOptions *options = GIMP_INK_OPTIONS (paint_options);
  GimpContext    *context = GIMP_CONTEXT (paint_options);
  GimpImage      *gimage;
268 269
  Blob           *blob_union = NULL;
  Blob           *blob_to_render;
270 271 272
  TempBuf        *area;
  guchar          col[MAX_CHANNELS];
  PixelRegion     blob_maskPR;
273

274
  gimage = gimp_item_get_image (GIMP_ITEM (drawable));
275

276 277 278 279 280 281 282 283 284
  if (! ink->last_blob)
    {
      ink->last_blob = ink_pen_ellipse (options,
                                        paint_core->cur_coords.x,
                                        paint_core->cur_coords.y,
                                        paint_core->cur_coords.pressure,
                                        paint_core->cur_coords.xtilt,
                                        paint_core->cur_coords.ytilt,
                                        10.0);
285

Michael Natterer's avatar
Michael Natterer committed
286 287 288 289 290
      if (ink->start_blob)
        g_free (ink->start_blob);

      ink->start_blob = blob_duplicate (ink->last_blob);

291 292
      time_smoother_init (ink, time);
      ink->last_time = time;
293

294 295
      dist_smoother_init (ink, 0.0);
      ink->init_velocity = TRUE;
296

297
      blob_to_render = ink->last_blob;
298 299 300
    }
  else
    {
301 302 303
      Blob    *blob;
      gdouble  dist;
      gdouble  velocity;
304 305
      guint32  lasttime = ink->last_time;
      guint32  thistime;
306 307 308 309 310 311 312 313 314 315 316 317 318 319

      time_smoother_add (ink, time);
      thistime = ink->last_time = time_smoother_result (ink);

      /* 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
       */
      if (thistime == lasttime)
        thistime = lasttime + 1;

Michael Natterer's avatar
Michael Natterer committed
320 321 322 323
      dist = sqrt ((paint_core->last_coords.x - paint_core->cur_coords.x) *
                   (paint_core->last_coords.x - paint_core->cur_coords.x) +
                   (paint_core->last_coords.y - paint_core->cur_coords.y) *
                   (paint_core->last_coords.y - paint_core->cur_coords.y));
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348

      if (ink->init_velocity)
        {
          dist_smoother_init (ink, dist);
          ink->init_velocity = FALSE;
        }
      else
        {
          dist_smoother_add (ink, dist);
          dist = dist_smoother_result (ink);
        }

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

      blob = ink_pen_ellipse (options,
                              paint_core->cur_coords.x,
                              paint_core->cur_coords.y,
                              paint_core->cur_coords.pressure,
                              paint_core->cur_coords.xtilt,
                              paint_core->cur_coords.ytilt,
                              velocity);

      blob_union = blob_convex_union (ink->last_blob, blob);
      g_free (ink->last_blob);
      ink->last_blob = blob;
349

350 351
      blob_to_render = blob_union;
    }
352

353
  /* Get the the buffer */
Michael Natterer's avatar
Michael Natterer committed
354
  ink->cur_blob = blob_to_render;
355
  area = gimp_paint_core_get_paint_area (paint_core, drawable, paint_options);
Michael Natterer's avatar
Michael Natterer committed
356 357
  ink->cur_blob = NULL;

358 359
  if (! area)
    return;
jtl's avatar
jtl committed
360

361
  gimp_image_get_foreground (gimage, drawable, context, col);
362

363 364
  /*  set the alpha channel  */
  col[paint_core->canvas_buf->bytes - 1] = OPAQUE_OPACITY;
365

366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
  /*  color the pixels  */
  color_pixels (temp_buf_data (paint_core->canvas_buf), col,
                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);

384
  render_blob (blob_to_render, &blob_maskPR);
385 386 387 388 389 390 391 392 393 394 395 396 397 398

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

400 401
  if (blob_union)
    g_free (blob_union);
402 403 404
}

static Blob *
405 406 407 408 409 410 411
ink_pen_ellipse (GimpInkOptions *options,
                 gdouble         x_center,
		 gdouble         y_center,
		 gdouble         pressure,
		 gdouble         xtilt,
		 gdouble         ytilt,
		 gdouble         velocity)
412
{
413
  BlobFunc blob_function;
414 415 416 417 418 419 420
  gdouble  size;
  gdouble  tsin, tcos;
  gdouble  aspect, radmin;
  gdouble  x,y;
  gdouble  tscale;
  gdouble  tscale_c;
  gdouble  tscale_s;
421

422 423
  /* Adjust the size depending on pressure. */

424
  size = options->size * (1.0 + options->size_sensitivity *
425
                          (2.0 * pressure - 1.0));
426

427 428 429 430
  /* Adjust the size further depending on pointer velocity and
   * velocity-sensitivity.  These 'magic constants' are 'feels
   * natural' tigert-approved. --ADM
   */
431 432 433 434 435

  if (velocity < 3.0)
    velocity = 3.0;

#ifdef VERBOSE
436
  g_printerr ("%g (%g) -> ", size, velocity);
437
#endif
438

439 440 441
  size = (options->vel_sensitivity *
          ((4.5 * size) / (1.0 + options->vel_sensitivity * (2.0 * velocity)))
          + (1.0 - options->vel_sensitivity) * size);
442 443

#ifdef VERBOSE
444
  g_printerr ("%g\n", (gfloat) size);
445 446 447 448
#endif

  /* Clamp resulting size to sane limits */

449 450
  if (size > options->size * (1.0 + options->size_sensitivity))
    size = options->size * (1.0 + options->size_sensitivity);
451

452 453
  if (size * SUBSAMPLE < 1.0)
    size = 1.0 / SUBSAMPLE;
454 455

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

Raph Levien's avatar
Raph Levien committed
457
  /* I'm not happy with the way the brush widget info is combined with
458 459 460 461
   * 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
462

463
  tscale   = options->tilt_sensitivity * 10.0;
464 465 466
  tscale_c = tscale * cos (gimp_deg_to_rad (options->tilt_angle));
  tscale_s = tscale * sin (gimp_deg_to_rad (options->tilt_angle));

467
  x = (options->blob_aspect * cos (options->blob_angle) +
468
       xtilt * tscale_c - ytilt * tscale_s);
469
  y = (options->blob_aspect * sin (options->blob_angle) +
470 471
       ytilt * tscale_c + xtilt * tscale_s);

Raph Levien's avatar
Raph Levien committed
472
#ifdef VERBOSE
473 474 475
  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
476
#endif
477

478
  aspect = sqrt (x * x + y * y);
479 480 481

  if (aspect != 0)
    {
482 483
      tcos = x / aspect;
      tsin = y / aspect;
484 485 486
    }
  else
    {
487 488 489 490 491 492 493 494 495 496 497
      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:
498
      blob_function = blob_ellipse;
499 500 501
      break;

    case GIMP_INK_BLOB_TYPE_SQUARE:
502
      blob_function = blob_square;
503 504 505
      break;

    case GIMP_INK_BLOB_TYPE_DIAMOND:
506 507 508 509 510
      blob_function = blob_diamond;
      break;

    default:
      g_return_val_if_reached (NULL);
511
      break;
512
    }
513

514 515 516 517 518 519
  return (* blob_function) (x_center * SUBSAMPLE,
                            y_center * SUBSAMPLE,
                            radmin * aspect * tcos,
                            radmin * aspect * tsin,
                            -radmin * tsin,
                            radmin * tcos);
520 521
}

522

523
static void
524 525
time_smoother_init (GimpInk *ink,
                    guint32  initval)
526
{
527
  gint i;
528

529
  ink->ts_index = 0;
530

531 532
  for (i = 0; i < TIME_SMOOTHER_BUFFER; i++)
    ink->ts_buffer[i] = initval;
533
}
534

535 536
static guint32
time_smoother_result (GimpInk *ink)
537 538
{
  gint    i;
539
  guint64 result = 0;
540

541 542
  for (i = 0; i < TIME_SMOOTHER_BUFFER; i++)
    result += ink->ts_buffer[i];
543

544
  return (result / (guint64) TIME_SMOOTHER_BUFFER);
545 546 547
}

static void
548 549
time_smoother_add (GimpInk *ink,
                   guint32  value)
550
{
551
  guint64 long_value = (guint64) value;
Michael Natterer's avatar
Michael Natterer committed
552

553 554 555 556 557 558 559 560 561 562
  /*  handle wrap-around of time values  */
  if (long_value < ink->ts_buffer[ink->ts_index])
    long_value += (guint64) + G_MAXUINT32;

  ink->ts_buffer[ink->ts_index++] = long_value;

  ink->ts_buffer[ink->ts_index++] = value;

  if (ink->ts_index == TIME_SMOOTHER_BUFFER)
    ink->ts_index = 0;
563
}
564

565

566
static void
567 568
dist_smoother_init (GimpInk *ink,
                    gdouble  initval)
569 570
{
  gint i;
571

572
  ink->dt_index = 0;
573

574 575
  for (i = 0; i < DIST_SMOOTHER_BUFFER; i++)
    ink->dt_buffer[i] = initval;
576 577 578
}

static gdouble
579
dist_smoother_result (GimpInk *ink)
580 581
{
  gint    i;
582
  gdouble result = 0.0;
583

584 585
  for (i = 0; i < DIST_SMOOTHER_BUFFER; i++)
    result += ink->dt_buffer[i];
586

587
  return (result / (gdouble) DIST_SMOOTHER_BUFFER);
588
}
589 590

static void
591 592
dist_smoother_add (GimpInk *ink,
                   gdouble  value)
593
{
594
  ink->dt_buffer[ink->dt_index++] = value;
595

596 597
  if (ink->dt_index == DIST_SMOOTHER_BUFFER)
    ink->dt_index = 0;
598 599 600
}


601 602 603
/*********************************/
/*  Rendering functions          */
/*********************************/
604

605
/* Some of this stuff should probably be combined with the
606 607 608 609 610 611 612 613 614 615 616 617 618
 * 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.
 */

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
 */
619 620 621
static void
insert_sort (gint *data,
	     gint  n)
622
{
623 624
  gint i, j, k;
  gint tmp1, tmp2;
625

626
  for (i = 2; i < 2 * n; i += 2)
627 628
    {
      tmp1 = data[i];
629
      tmp2 = data[i + 1];
630 631 632 633
      j = 0;
      while (data[j] < tmp1)
	j += 2;

634
      for (k = i; k > j; k -= 2)
635
	{
636 637
	  data[k]     = data[k - 2];
	  data[k + 1] = data[k - 1];
638 639
	}

640 641
      data[j]     = tmp1;
      data[j + 1] = tmp2;
642 643 644 645 646 647
    }
}

static void
fill_run (guchar *dest,
	  guchar  alpha,
648
	  gint    w)
649 650 651 652 653 654 655 656
{
  if (alpha == 255)
    {
      memset (dest, 255, w);
    }
  else
    {
      while (w--)
657 658 659 660
        {
          *dest = MAX (*dest, alpha);
          dest++;
        }
661 662 663 664
    }
}

static void
665 666 667 668 669
render_blob_line (Blob   *blob,
		  guchar *dest,
		  gint    x,
		  gint    y,
		  gint    width)
670
{
671 672 673
  gint  buf[4 * SUBSAMPLE];
  gint *data    = buf;
  gint  n       = 0;
674 675 676 677 678
  gint  i, j;
  gint  current = 0;  /* number of filled rows at this point
		       * in the scan line
		       */
  gint last_x;
679 680

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

682
  j = y * SUBSAMPLE - blob->y;
683
  for (i = 0; i < SUBSAMPLE; i++)
684 685 686
    {
      if (j >= blob->height)
	break;
687

688 689
      if ((j > 0) && (blob->data[j].left <= blob->data[j].right))
	{
690 691 692 693
	  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;
694 695 696 697 698 699 700 701
	  n++;
	}
      j++;
    }

  /*   If we have less than SUBSAMPLE rows, compress */
  if (n < SUBSAMPLE)
    {
702 703
      for (i = 0; i < 2 * n; i++)
	data[2 * n + i] = data[2 * SUBSAMPLE + i];
704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724
    }

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

726 727 728
  /* Render the row */

  last_x = 0;
729
  for (i = 0; i < n;)
730
    {
731
      gint cur_x = data[2 * i] / SUBSAMPLE - x;
732
      gint pixel;
733 734 735

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

      /* Compute the value for this pixel */
739
      pixel = current * SUBSAMPLE;
740 741 742

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

745 746 747
	  if (tmp_x - x != cur_x)
	    break;

748
	  if (data[2 * i + 1] == ROW_START)
749 750
	    {
	      current++;
751
	      pixel += ((tmp_x + 1) * SUBSAMPLE) - data[2 * i];
752 753 754 755
	    }
	  else
	    {
	      current--;
756
	      pixel -= ((tmp_x + 1) * SUBSAMPLE) - data[2 * i];
757
	    }
758

759 760 761
	  i++;
	}

762
      dest[cur_x] = MAX (dest[cur_x], (pixel * 255) / (SUBSAMPLE * SUBSAMPLE));
763 764 765 766 767

      last_x = cur_x + 1;
    }

  if (current != 0)
768
    fill_run (dest + last_x, (255 * current)/ SUBSAMPLE, width - last_x);
769 770 771
}

static void
772 773
render_blob (Blob        *blob,
             PixelRegion *dest)
774
{
775 776 777 778
  gint      i;
  gint      h;
  guchar   *s;
  gpointer  pr;
779

780 781
  for (pr = pixel_regions_register (1, dest);
       pr != NULL;
782 783 784 785 786 787 788 789 790 791 792 793 794
       pr = pixel_regions_process (pr))
    {
      h = dest->h;
      s = dest->data;

      for (i=0; i<h; i++)
	{
	  render_blob_line (blob, s,
			    dest->x, dest->y + i, dest->w);
	  s += dest->rowstride;
	}
    }
}