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
      Blob    *blob;
      gdouble  dist;
      gdouble  velocity;
275 276
      guint32  lasttime = ink->last_time;
      guint32  thistime;
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

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

324 325
      blob_to_render = blob_union;
    }
326

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

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

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

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

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

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

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

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

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

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

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

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

  if (velocity < 3.0)
    velocity = 3.0;

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

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

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

  /* Clamp resulting size to sane limits */

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

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

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

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

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

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

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

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

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

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

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

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

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

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

501
  ink->dt_index = 0;
502

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

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

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

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

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

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

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

539
  ink->ts_index = 0;
540

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

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

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

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

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

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


576 577 578
/*********************************/
/*  Rendering functions          */
/*********************************/
579

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

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

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

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

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

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

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

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

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

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

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

701 702 703
  /* Render the row */

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

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

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

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

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

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

734 735 736
	  i++;
	}

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

      last_x = cur_x + 1;
    }

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

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

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