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

41
#include "gimp-intl.h"
42

43

44
#define SUBSAMPLE 8
45

46

47
/*  local function prototypes  */
48

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 79 80 81 82 83 84 85 86 87 88
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);
static gdouble   time_smoother_result    (GimpInk          *ink);
static void      time_smoother_init      (GimpInk          *ink,
                                          guint32           initval);
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);
89

90

91
static GimpPaintCoreClass *parent_class = NULL;
92

93

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

104
GType
105
gimp_ink_get_type (void)
106
{
107
  static GType type = 0;
108

109
  if (! type)
110
    {
111
      static const GTypeInfo info =
112
      {
113 114 115 116 117 118 119 120 121
        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,
122 123
      };

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

129
  return type;
130 131 132
}

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

138
  parent_class = g_type_class_peek_parent (klass);
139

140
  object_class->finalize  = gimp_ink_finalize;
141

142 143
  paint_core_class->paint          = gimp_ink_paint;
  paint_core_class->get_paint_area = gimp_ink_get_paint_area;
144 145 146
}

static void
147
gimp_ink_init (GimpInk *ink)
148 149 150 151
{
}

static void
152
gimp_ink_finalize (GObject *object)
153
{
154
  GimpInk *ink = GIMP_INK (object);
155

156
  if (ink->last_blob)
157
    {
158 159
      g_free (ink->last_blob);
      ink->last_blob = NULL;
160
    }
161

162
  G_OBJECT_CLASS (parent_class)->finalize (object);
163 164
}

165
static void
166 167 168 169 170
gimp_ink_paint (GimpPaintCore    *paint_core,
                GimpDrawable     *drawable,
                GimpPaintOptions *paint_options,
                GimpPaintState    paint_state,
                guint32           time)
171
{
172
  GimpInk *ink = GIMP_INK (paint_core);
173 174

  switch (paint_state)
175
    {
176
    case GIMP_PAINT_STATE_INIT:
177 178 179 180 181 182 183 184
      if (ink->last_blob                                        &&
          paint_core->cur_coords.x == paint_core->last_coords.x &&
          paint_core->cur_coords.y == paint_core->last_coords.y)
        {
          /*  start with a new blob if we're not interpolating  */
          g_free (ink->last_blob);
          ink->last_blob = NULL;
        }
185
      break;
186

187
    case GIMP_PAINT_STATE_MOTION:
188
      gimp_ink_motion (paint_core, drawable, paint_options, time);
189
      break;
190

191
    case GIMP_PAINT_STATE_FINISH:
192 193
      break;
    }
194
}
jtl's avatar
jtl committed
195

196 197 198 199
static TempBuf *
gimp_ink_get_paint_area (GimpPaintCore    *paint_core,
                         GimpDrawable     *drawable,
                         GimpPaintOptions *paint_options)
200
{
201 202 203 204 205 206
  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
207

208
  bytes = gimp_drawable_bytes_with_alpha (drawable);
209

210
  blob_bounds (ink->blob, &x, &y, &width, &height);
211

212 213
  dwidth  = gimp_item_width  (GIMP_ITEM (drawable));
  dheight = gimp_item_height (GIMP_ITEM (drawable));
214

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

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

228
  return paint_core->canvas_buf;
229
}
230

231
static void
232 233 234 235
gimp_ink_motion (GimpPaintCore    *paint_core,
                 GimpDrawable     *drawable,
                 GimpPaintOptions *paint_options,
                 guint32           time)
236
{
237 238 239 240
  GimpInk        *ink     = GIMP_INK (paint_core);
  GimpInkOptions *options = GIMP_INK_OPTIONS (paint_options);
  GimpContext    *context = GIMP_CONTEXT (paint_options);
  GimpImage      *gimage;
241 242
  Blob           *blob_union = NULL;
  Blob           *blob_to_render;
243 244 245
  TempBuf        *area;
  guchar          col[MAX_CHANNELS];
  PixelRegion     blob_maskPR;
246

247
  gimage = gimp_item_get_image (GIMP_ITEM (drawable));
248

249 250 251 252 253 254 255 256 257
  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);
258

259 260
      time_smoother_init (ink, time);
      ink->last_time = time;
261

262 263
      dist_smoother_init (ink, 0.0);
      ink->init_velocity = TRUE;
264

265 266
      ink->lastx = paint_core->cur_coords.x;
      ink->lasty = paint_core->cur_coords.y;
267

268
      blob_to_render = ink->last_blob;
269 270 271
    }
  else
    {
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
      Blob    *blob;
      gdouble  lasttime, thistime;
      gdouble  dist;
      gdouble  velocity;

      lasttime = ink->last_time;

      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;

      dist = sqrt ((ink->lastx - paint_core->cur_coords.x) *
                   (ink->lastx - paint_core->cur_coords.x) +
                   (ink->lasty - paint_core->cur_coords.y) *
                   (ink->lasty - paint_core->cur_coords.y));

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

      ink->lastx = paint_core->cur_coords.x;
      ink->lasty = paint_core->cur_coords.y;

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

325 326
      blob_to_render = blob_union;
    }
327

328
  /* Get the the buffer */
329
  ink->blob = blob_to_render;
330

331 332 333
  area = gimp_paint_core_get_paint_area (paint_core, drawable, paint_options);
  if (! area)
    return;
jtl's avatar
jtl committed
334

335
  gimp_image_get_foreground (gimage, drawable, context, col);
336

337 338
  /*  set the alpha channel  */
  col[paint_core->canvas_buf->bytes - 1] = OPAQUE_OPACITY;
339

340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
  /*  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);

358
  render_blob (blob_to_render, &blob_maskPR);
359 360 361 362 363 364 365 366 367 368 369 370 371 372

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

374 375
  if (blob_union)
    g_free (blob_union);
376 377 378
}

static Blob *
379 380 381 382 383 384 385
ink_pen_ellipse (GimpInkOptions *options,
                 gdouble         x_center,
		 gdouble         y_center,
		 gdouble         pressure,
		 gdouble         xtilt,
		 gdouble         ytilt,
		 gdouble         velocity)
386
{
387
  BlobFunc blob_function;
388 389 390 391 392 393 394
  gdouble  size;
  gdouble  tsin, tcos;
  gdouble  aspect, radmin;
  gdouble  x,y;
  gdouble  tscale;
  gdouble  tscale_c;
  gdouble  tscale_s;
395

396 397
  /* Adjust the size depending on pressure. */

398
  size = options->size * (1.0 + options->size_sensitivity *
399
                          (2.0 * pressure - 1.0));
400

401 402 403 404
  /* Adjust the size further depending on pointer velocity and
   * velocity-sensitivity.  These 'magic constants' are 'feels
   * natural' tigert-approved. --ADM
   */
405 406 407 408 409

  if (velocity < 3.0)
    velocity = 3.0;

#ifdef VERBOSE
410
  g_printerr ("%g (%g) -> ", size, velocity);
411
#endif
412

413 414 415
  size = (options->vel_sensitivity *
          ((4.5 * size) / (1.0 + options->vel_sensitivity * (2.0 * velocity)))
          + (1.0 - options->vel_sensitivity) * size);
416 417

#ifdef VERBOSE
418
  g_printerr ("%g\n", (gfloat) size);
419 420 421 422
#endif

  /* Clamp resulting size to sane limits */

423 424
  if (size > options->size * (1.0 + options->size_sensitivity))
    size = options->size * (1.0 + options->size_sensitivity);
425

426 427
  if (size * SUBSAMPLE < 1.0)
    size = 1.0 / SUBSAMPLE;
428 429

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

Raph Levien's avatar
Raph Levien committed
431
  /* I'm not happy with the way the brush widget info is combined with
432 433 434 435
   * 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
436

437
  tscale   = options->tilt_sensitivity * 10.0;
438 439 440
  tscale_c = tscale * cos (gimp_deg_to_rad (options->tilt_angle));
  tscale_s = tscale * sin (gimp_deg_to_rad (options->tilt_angle));

441
  x = (options->blob_aspect * cos (options->blob_angle) +
442
       xtilt * tscale_c - ytilt * tscale_s);
443
  y = (options->blob_aspect * sin (options->blob_angle) +
444 445
       ytilt * tscale_c + xtilt * tscale_s);

Raph Levien's avatar
Raph Levien committed
446
#ifdef VERBOSE
447 448 449
  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
450
#endif
451

452
  aspect = sqrt (x * x + y * y);
453 454 455

  if (aspect != 0)
    {
456 457
      tcos = x / aspect;
      tsin = y / aspect;
458 459 460
    }
  else
    {
461 462 463 464 465 466 467 468 469 470 471
      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:
472
      blob_function = blob_ellipse;
473 474 475
      break;

    case GIMP_INK_BLOB_TYPE_SQUARE:
476
      blob_function = blob_square;
477 478 479
      break;

    case GIMP_INK_BLOB_TYPE_DIAMOND:
480 481 482 483 484
      blob_function = blob_diamond;
      break;

    default:
      g_return_val_if_reached (NULL);
485
      break;
486
    }
487

488 489 490 491 492 493
  return (* blob_function) (x_center * SUBSAMPLE,
                            y_center * SUBSAMPLE,
                            radmin * aspect * tcos,
                            radmin * aspect * tsin,
                            -radmin * tsin,
                            radmin * tcos);
494 495 496
}

static void
497 498
dist_smoother_init (GimpInk *ink,
                    gdouble  initval)
499
{
500
  gint i;
501

502
  ink->dt_index = 0;
503

504
  for (i = 0; i < DIST_SMOOTHER_BUFFER; i++)
505
    {
506
      ink->dt_buffer[i] = initval;
507 508
    }
}
509

510
static gdouble
511
dist_smoother_result (GimpInk *ink)
512 513 514
{
  gint    i;
  gdouble result = 0.0;
515

516 517
  for (i = 0; i < DIST_SMOOTHER_BUFFER; i++)
    {
518
      result += ink->dt_buffer[i];
519
    }
520 521

  return (result / (gdouble) DIST_SMOOTHER_BUFFER);
522 523 524
}

static void
525 526
dist_smoother_add (GimpInk *ink,
                   gdouble  value)
527
{
528
  ink->dt_buffer[ink->dt_index] = value;
Michael Natterer's avatar
Michael Natterer committed
529

530 531
  if ((++ink->dt_index) == DIST_SMOOTHER_BUFFER)
    ink->dt_index = 0;
532
}
533

534
static void
535 536
time_smoother_init (GimpInk *ink,
                    guint32  initval)
537 538
{
  gint i;
539

540
  ink->ts_index = 0;
541

542
  for (i = 0; i < TIME_SMOOTHER_BUFFER; i++)
543
    {
544
      ink->ts_buffer[i] = initval;
545
    }
546 547 548
}

static gdouble
549
time_smoother_result (GimpInk *ink)
550 551 552 553 554
{
  gint    i;
  guint64 result = 0;

  for (i = 0; i < TIME_SMOOTHER_BUFFER; i++)
555
    {
556
      result += ink->ts_buffer[i];
557
    }
558

559 560 561 562 563 564
#ifdef _MSC_VER
  return (gdouble) (gint64) (result / TIME_SMOOTHER_BUFFER);
#else
  return (result / TIME_SMOOTHER_BUFFER);
#endif
}
565 566

static void
567 568
time_smoother_add (GimpInk *ink,
                   guint32  value)
569
{
570
  ink->ts_buffer[ink->ts_index] = value;
571

572 573
  if ((++ink->ts_index) == TIME_SMOOTHER_BUFFER)
    ink->ts_index = 0;
574 575 576
}


577 578 579
/*********************************/
/*  Rendering functions          */
/*********************************/
580

581
/* Some of this stuff should probably be combined with the
582 583 584 585 586 587 588 589 590 591 592 593 594
 * 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
 */
595 596 597
static void
insert_sort (gint *data,
	     gint  n)
598
{
599 600
  gint i, j, k;
  gint tmp1, tmp2;
601

602
  for (i = 2; i < 2 * n; i += 2)
603 604
    {
      tmp1 = data[i];
605
      tmp2 = data[i + 1];
606 607 608 609
      j = 0;
      while (data[j] < tmp1)
	j += 2;

610
      for (k = i; k > j; k -= 2)
611
	{
612 613
	  data[k]     = data[k - 2];
	  data[k + 1] = data[k - 1];
614 615
	}

616 617
      data[j]     = tmp1;
      data[j + 1] = tmp2;
618 619 620 621 622 623
    }
}

static void
fill_run (guchar *dest,
	  guchar  alpha,
624
	  gint    w)
625 626 627 628 629 630 631 632
{
  if (alpha == 255)
    {
      memset (dest, 255, w);
    }
  else
    {
      while (w--)
633 634 635 636
        {
          *dest = MAX (*dest, alpha);
          dest++;
        }
637 638 639 640
    }
}

static void
641 642 643 644 645
render_blob_line (Blob   *blob,
		  guchar *dest,
		  gint    x,
		  gint    y,
		  gint    width)
646
{
647 648 649
  gint  buf[4 * SUBSAMPLE];
  gint *data    = buf;
  gint  n       = 0;
650 651 652 653 654
  gint  i, j;
  gint  current = 0;  /* number of filled rows at this point
		       * in the scan line
		       */
  gint last_x;
655 656

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

658
  j = y * SUBSAMPLE - blob->y;
659
  for (i = 0; i < SUBSAMPLE; i++)
660 661 662
    {
      if (j >= blob->height)
	break;
663

664 665
      if ((j > 0) && (blob->data[j].left <= blob->data[j].right))
	{
666 667 668 669
	  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;
670 671 672 673 674 675 676 677
	  n++;
	}
      j++;
    }

  /*   If we have less than SUBSAMPLE rows, compress */
  if (n < SUBSAMPLE)
    {
678 679
      for (i = 0; i < 2 * n; i++)
	data[2 * n + i] = data[2 * SUBSAMPLE + i];
680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700
    }

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

702 703 704
  /* Render the row */

  last_x = 0;
705
  for (i = 0; i < n;)
706
    {
707
      gint cur_x = data[2 * i] / SUBSAMPLE - x;
708
      gint pixel;
709 710 711

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

      /* Compute the value for this pixel */
715
      pixel = current * SUBSAMPLE;
716 717 718

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

721 722 723
	  if (tmp_x - x != cur_x)
	    break;

724
	  if (data[2 * i + 1] == ROW_START)
725 726
	    {
	      current++;
727
	      pixel += ((tmp_x + 1) * SUBSAMPLE) - data[2 * i];
728 729 730 731
	    }
	  else
	    {
	      current--;
732
	      pixel -= ((tmp_x + 1) * SUBSAMPLE) - data[2 * i];
733
	    }
734

735 736 737
	  i++;
	}

738
      dest[cur_x] = MAX (dest[cur_x], (pixel * 255) / (SUBSAMPLE * SUBSAMPLE));
739 740 741 742 743

      last_x = cur_x + 1;
    }

  if (current != 0)
744
    fill_run (dest + last_x, (255 * current)/ SUBSAMPLE, width - last_x);
745 746 747
}

static void
748 749
render_blob (Blob        *blob,
             PixelRegion *dest)
750
{
751 752 753 754
  gint      i;
  gint      h;
  guchar   *s;
  gpointer  pr;
755

756 757
  for (pr = pixel_regions_register (1, dest);
       pr != NULL;
758 759 760 761 762 763 764 765 766 767 768 769 770
       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;
	}
    }
}