blur-gauss.c 47.8 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
 * (at your option) any later version.
8
 *
9 10 11 12 13 14
 * 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 17 18 19 20 21 22 23 24 25 26
 */

#include "config.h"

#include <string.h>

#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>

#include "libgimp/stdplugins-intl.h"

27 28 29 30 31
#define GAUSS_PROC      "plug-in-gauss"
#define GAUSS_IIR_PROC  "plug-in-gauss-iir"
#define GAUSS_IIR2_PROC "plug-in-gauss-iir2"
#define GAUSS_RLE_PROC  "plug-in-gauss-rle"
#define GAUSS_RLE2_PROC "plug-in-gauss-rle2"
32
#define PLUG_IN_BINARY  "blur-gauss"
33
#define PLUG_IN_ROLE    "gimp-blur-gauss"
34

35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
typedef enum
{
  BLUR_IIR,
  BLUR_RLE
} BlurMethod;

typedef struct
{
  gdouble     horizontal;
  gdouble     vertical;
  BlurMethod  method;
} BlurValues;


/* Declare local functions.
 */
static void      query  (void);
static void      run    (const gchar      *name,
53 54 55 56
                         gint              nparams,
                         const GimpParam  *param,
                         gint             *nreturn_vals,
                         GimpParam       **return_vals);
57

58 59 60 61 62 63 64 65
static void      gauss             (GimpDrawable *drawable,
                                    gdouble       horizontal,
                                    gdouble       vertical,
                                    BlurMethod    method,
                                    GtkWidget    *preview);

static void      update_preview    (GtkWidget    *preview,
                                    GtkWidget    *size);
66 67 68
/*
 * Gaussian blur interface
 */
69 70
static gboolean  gauss_dialog      (gint32        image_ID,
                                    GimpDrawable *drawable);
71 72 73 74

/*
 * Gaussian blur helper functions
 */
75
static void      find_iir_constants    (gdouble  n_p[],
76 77 78 79 80 81 82
                                        gdouble  n_m[],
                                        gdouble  d_p[],
                                        gdouble  d_m[],
                                        gdouble  bd_p[],
                                        gdouble  bd_m[],
                                        gdouble  std_dev);

83 84 85 86 87
static void      transfer_pixels   (const gdouble *src1,
                                    const gdouble *src2,
                                    guchar        *dest,
                                    gint           bytes,
                                    gint           width);
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104

static void      make_rle_curve    (gdouble   sigma,
                                    gint    **p_curve,
                                    gint     *p_length,
                                    gint    **p_sum,
                                    gint     *p_total);

static void      free_rle_curve    (gint     *curve,
                                    gint      length,
                                    gint     *sum);

static inline gint run_length_encode (const guchar *src,
                                      gint         *repeat,
                                      gint         *dest,
                                      gint          bytes,
                                      gint          width,
                                      gint          border,
105
                                      gboolean      pack);
106 107


108
const GimpPlugInInfo PLUG_IN_INFO =
109 110 111 112 113 114 115 116 117 118 119
{
  NULL,  /* init_proc  */
  NULL,  /* quit_proc  */
  query, /* query_proc */
  run,   /* run_proc   */
};

static BlurValues bvals =
{
  5.0,  /*  x radius  */
  5.0,  /*  y radius  */
120
  BLUR_RLE
121 122 123 124 125 126 127 128
};


MAIN ()

static void
query (void)
{
129
  static const GimpParamDef args[] =
130
  {
131
    { GIMP_PDB_INT32,    "run-mode",   "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
132 133 134 135
    { GIMP_PDB_IMAGE,    "image",      "Input image" },
    { GIMP_PDB_DRAWABLE, "drawable",   "Input drawable" },
    { GIMP_PDB_FLOAT,    "horizontal", "Horizontal radius of gaussian blur (in pixels, > 0.0)" },
    { GIMP_PDB_FLOAT,    "vertical",   "Vertical radius of gaussian blur (in pixels, > 0.0)" },
136
    { GIMP_PDB_INT32,    "method",     "Blur method { IIR (0), RLE (1) }" }
137 138
  };

139
  static const GimpParamDef args1[] =
140
  {
141
    { GIMP_PDB_INT32,    "run-mode",   "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
142 143 144 145 146
    { GIMP_PDB_IMAGE,    "image",      "Input image (unused)" },
    { GIMP_PDB_DRAWABLE, "drawable",   "Input drawable" },
    { GIMP_PDB_FLOAT,    "radius",     "Radius of gaussian blur (in pixels, > 0.0)" },
    { GIMP_PDB_INT32,    "horizontal", "Blur in horizontal direction" },
    { GIMP_PDB_INT32,    "vertical",   "Blur in vertical direction" }
147 148
  };

149
  static const GimpParamDef args2[] =
150
  {
151
    { GIMP_PDB_INT32,    "run-mode",   "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
152 153 154 155
    { GIMP_PDB_IMAGE,    "image",      "Input image" },
    { GIMP_PDB_DRAWABLE, "drawable",   "Input drawable" },
    { GIMP_PDB_FLOAT,    "horizontal", "Horizontal radius of gaussian blur (in pixels, > 0.0)" },
    { GIMP_PDB_FLOAT,    "vertical",   "Vertical radius of gaussian blur (in pixels, > 0.0)" }
156 157
  };

158
  gimp_install_procedure (GAUSS_PROC,
159
                          N_("Simplest, most commonly used way of blurring"),
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
                          "Applies a gaussian blur to the drawable, with "
                          "specified radius of affect.  The standard deviation "
                          "of the normal distribution used to modify pixel "
                          "values is calculated based on the supplied radius.  "
                          "Horizontal and vertical blurring can be "
                          "independently invoked by specifying only one to "
                          "run.  The IIR gaussian blurring works best for "
                          "large radius values and for images which are not "
                          "computer-generated.",
                          "Spencer Kimball & Peter Mattis",
                          "Spencer Kimball & Peter Mattis",
                          "1995-1996",
                          N_("_Gaussian Blur..."),
                          "RGB*, GRAY*",
                          GIMP_PLUGIN,
                          G_N_ELEMENTS (args), 0,
                          args, NULL);
177

178
  gimp_install_procedure (GAUSS_IIR_PROC,
179
                          N_("Apply a gaussian blur"),
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
                          "Applies a gaussian blur to the drawable, with "
                          "specified radius of affect.  The standard deviation "
                          "of the normal distribution used to modify pixel "
                          "values is calculated based on the supplied radius.  "
                          "Horizontal and vertical blurring can be "
                          "independently invoked by specifying only one to "
                          "run.  The IIR gaussian blurring works best for "
                          "large radius values and for images which are not "
                          "computer-generated.",
                          "Spencer Kimball & Peter Mattis",
                          "Spencer Kimball & Peter Mattis",
                          "1995-1996",
                          NULL,
                          "RGB*, GRAY*",
                          GIMP_PLUGIN,
                          G_N_ELEMENTS (args1), 0,
                          args1, NULL);
197

198
  gimp_install_procedure (GAUSS_IIR2_PROC,
199
                          N_("Apply a gaussian blur"),
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
                          "Applies a gaussian blur to the drawable, with "
                          "specified radius of affect.  The standard deviation "
                          "of the normal distribution used to modify pixel "
                          "values is calculated based on the supplied radius.  "
                          "This radius can be specified indepently on for the "
                          "horizontal and the vertical direction. The IIR "
                          "gaussian blurring works best for large radius "
                          "values and for images which are not "
                          "computer-generated.",
                          "Spencer Kimball, Peter Mattis & Sven Neumann",
                          "Spencer Kimball, Peter Mattis & Sven Neumann",
                          "1995-2000",
                          NULL,
                          "RGB*, GRAY*",
                          GIMP_PLUGIN,
                          G_N_ELEMENTS (args2), 0,
                          args2, NULL);
217

218
  gimp_install_procedure (GAUSS_RLE_PROC,
219
                          N_("Apply a gaussian blur"),
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
                          "Applies a gaussian blur to the drawable, with "
                          "specified radius of affect.  The standard deviation "
                          "of the normal distribution used to modify pixel "
                          "values is calculated based on the supplied radius.  "
                          "Horizontal and vertical blurring can be "
                          "independently invoked by specifying only one to "
                          "run.  The RLE gaussian blurring performs most "
                          "efficiently on computer-generated images or images "
                          "with large areas of constant intensity.",
                          "Spencer Kimball & Peter Mattis",
                          "Spencer Kimball & Peter Mattis",
                          "1995-1996",
                          NULL,
                          "RGB*, GRAY*",
                          GIMP_PLUGIN,
                          G_N_ELEMENTS (args1), 0,
                          args1, NULL);
237

238
  gimp_install_procedure (GAUSS_RLE2_PROC,
239
                          N_("Apply a gaussian blur"),
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
                          "Applies a gaussian blur to the drawable, with "
                          "specified radius of affect.  The standard deviation "
                          "of the normal distribution used to modify pixel "
                          "values is calculated based on the supplied radius.  "
                          "This radius can be specified indepently on for the "
                          "horizontal and the vertical direction. The RLE "
                          "gaussian blurring performs most efficiently on "
                          "computer-generated images or images with large "
                          "areas of constant intensity.",
                          "Spencer Kimball, Peter Mattis & Sven Neumann",
                          "Spencer Kimball, Peter Mattis & Sven Neumann",
                          "1995-2000",
                          NULL,
                          "RGB*, GRAY*",
                          GIMP_PLUGIN,
                          G_N_ELEMENTS (args2), 0,
                          args2, NULL);
257

258
  gimp_plugin_menu_register (GAUSS_PROC, "<Image>/Filters/Blur");
259 260 261 262 263 264 265 266 267
}

static void
run (const gchar      *name,
     gint              nparams,
     const GimpParam  *param,
     gint             *nreturn_vals,
     GimpParam       **return_vals)
{
268
  static GimpParam   values[2];
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
  gint32             image_ID;
  GimpDrawable      *drawable;
  GimpRunMode        run_mode;
  GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
  gdouble            radius = 0.;

  run_mode = param[0].data.d_int32;

  INIT_I18N ();

  *nreturn_vals = 1;
  *return_vals  = values;

  values[0].type          = GIMP_PDB_STATUS;
  values[0].data.d_status = status;

  /*  Get the specified image and drawable  */
  image_ID = param[1].data.d_image;
  drawable = gimp_drawable_get (param[2].data.d_drawable);

289
  /*  set the tile cache size so that the gaussian blur works well  */
290
  gimp_tile_cache_ntiles (2*
291 292 293 294
                          (MAX (drawable->width, drawable->height) /
                           gimp_tile_width () + 1));


295
  if (strcmp (name, GAUSS_PROC) == 0)
296 297
    {
      switch (run_mode)
298 299 300
        {
        case GIMP_RUN_INTERACTIVE:
          /*  Possibly retrieve data  */
301
          gimp_get_data (GAUSS_PROC, &bvals);
302 303 304 305 306

          /*  First acquire information with a dialog  */
          if (! gauss_dialog (image_ID, drawable))
            return;
          break;
307

308 309 310 311
        case GIMP_RUN_NONINTERACTIVE:
          /*  Make sure all the arguments are there!  */
          if (nparams != 6)
            status = GIMP_PDB_CALLING_ERROR;
312

313 314 315 316
          if (status == GIMP_PDB_SUCCESS)
            {
              bvals.horizontal = param[3].data.d_float;
              bvals.vertical   = param[4].data.d_float;
317
              bvals.method     = param[5].data.d_int32;
318 319 320 321 322
            }
          if (status == GIMP_PDB_SUCCESS &&
              (bvals.horizontal <= 0.0 && bvals.vertical <= 0.0))
            status = GIMP_PDB_CALLING_ERROR;
          break;
323

324 325
        case GIMP_RUN_WITH_LAST_VALS:
          /*  Possibly retrieve data  */
326
          gimp_get_data (GAUSS_PROC, &bvals);
327
          break;
328

329 330 331
        default:
          break;
        }
332
    }
333
  else if (strcmp (name, GAUSS_IIR_PROC) == 0)
334 335
    {
      if (nparams != 6)
336
        status = GIMP_PDB_CALLING_ERROR;
337

338
      if (status == GIMP_PDB_SUCCESS)
339 340 341 342 343 344
        {
          radius           = param[3].data.d_float;
          bvals.horizontal = (param[4].data.d_int32) ? radius : 0.;
          bvals.vertical   = (param[5].data.d_int32) ? radius : 0.;
          bvals.method     = BLUR_IIR;
        }
345

346
      if (radius <= 0.0)
347
        status = GIMP_PDB_CALLING_ERROR;
348 349

      if (run_mode == GIMP_RUN_INTERACTIVE)
Seth Burgess's avatar
Seth Burgess committed
350
        {
351 352
          if (! gauss_dialog (image_ID, drawable))
            return;
Seth Burgess's avatar
Seth Burgess committed
353
        }
354
    }
355
  else if (strcmp (name, GAUSS_IIR2_PROC) == 0)
356 357
    {
      if (nparams != 5)
358
        status = GIMP_PDB_CALLING_ERROR;
359

360
      if (status == GIMP_PDB_SUCCESS)
361 362 363 364 365
        {
          bvals.horizontal = param[3].data.d_float;
          bvals.vertical   = param[4].data.d_float;
          bvals.method     = BLUR_IIR;
        }
366

367
      if (bvals.horizontal <= 0.0 && bvals.vertical <= 0.0)
368
        status = GIMP_PDB_CALLING_ERROR;
369 370

      if (run_mode == GIMP_RUN_INTERACTIVE)
Seth Burgess's avatar
Seth Burgess committed
371
        {
372 373
          if (! gauss_dialog (image_ID, drawable))
            return;
Seth Burgess's avatar
Seth Burgess committed
374
        }
375
    }
376
  else if (strcmp (name, GAUSS_RLE_PROC) == 0)
377 378
    {
      if (nparams != 6)
379
        status = GIMP_PDB_CALLING_ERROR;
380

381
      if (status == GIMP_PDB_SUCCESS)
382 383 384 385 386 387
        {
          radius           = param[3].data.d_float;
          bvals.horizontal = (param[4].data.d_int32) ? radius : 0.;
          bvals.vertical   = (param[5].data.d_int32) ? radius : 0.;
          bvals.method     = BLUR_RLE;
        }
388

389
      if (radius <= 0.0)
390
        status = GIMP_PDB_CALLING_ERROR;
391 392

      if (run_mode == GIMP_RUN_INTERACTIVE)
Seth Burgess's avatar
Seth Burgess committed
393
        {
394 395
          if (! gauss_dialog (image_ID, drawable))
            return;
Seth Burgess's avatar
Seth Burgess committed
396
        }
397
    }
398
  else if (strcmp (name, GAUSS_RLE2_PROC) == 0)
399 400
    {
      if (nparams != 5)
401
        status = GIMP_PDB_CALLING_ERROR;
402

403
      if (status == GIMP_PDB_SUCCESS)
404 405 406 407 408
        {
          bvals.horizontal = param[3].data.d_float;
          bvals.vertical   = param[4].data.d_float;
          bvals.method     = BLUR_RLE;
        }
409

410
      if (bvals.horizontal <= 0.0 && bvals.vertical <= 0.0)
411
        status = GIMP_PDB_CALLING_ERROR;
412 413

      if (run_mode == GIMP_RUN_INTERACTIVE)
Seth Burgess's avatar
Seth Burgess committed
414
        {
415 416
          if (! gauss_dialog (image_ID, drawable))
            return;
Seth Burgess's avatar
Seth Burgess committed
417
        }
418 419 420 421 422 423 424 425
    }
  else
    status = GIMP_PDB_CALLING_ERROR;

  if (status == GIMP_PDB_SUCCESS)
    {
      /*  Make sure that the drawable is gray or RGB color  */
      if (gimp_drawable_is_rgb (drawable->drawable_id) ||
426 427
          gimp_drawable_is_gray (drawable->drawable_id))
        {
428
          gimp_progress_init (_("Gaussian Blur"));
429 430

          /*  run the gaussian blur  */
431 432 433 434 435 436 437
          gauss (drawable,
                 bvals.horizontal, bvals.vertical,
                 bvals.method,
                 NULL);

          /*  Store data  */
          if (run_mode == GIMP_RUN_INTERACTIVE)
438
            gimp_set_data (GAUSS_PROC, &bvals, sizeof (BlurValues));
439 440 441 442

          if (run_mode != GIMP_RUN_NONINTERACTIVE)
            gimp_displays_flush ();
        }
443
      else
444
        {
445 446 447 448
          status        = GIMP_PDB_EXECUTION_ERROR;
          *nreturn_vals = 2;
          values[1].type          = GIMP_PDB_STRING;
          values[1].data.d_string = _("Cannot operate on indexed color images.");
449
        }
450 451 452 453 454 455 456 457 458 459 460

      gimp_drawable_detach (drawable);
    }

  values[0].data.d_status = status;
}



static gboolean
gauss_dialog (gint32        image_ID,
461
              GimpDrawable *drawable)
462
{
463 464
  GtkWidget *dialog;
  GtkWidget *main_vbox;
465 466 467
  GtkWidget *frame;
  GtkWidget *size;
  GtkWidget *hbox;
468
  GtkWidget *button;
469
  GtkWidget *preview;
470 471 472 473 474 475

  GimpUnit   unit;
  gdouble    xres;
  gdouble    yres;
  gboolean   run;

476
  gimp_ui_init (PLUG_IN_BINARY, FALSE);
477

478
  dialog = gimp_dialog_new (_("Gaussian Blur"), PLUG_IN_ROLE,
479
                            NULL, 0,
480
                            gimp_standard_help_func, GAUSS_PROC,
481

482 483
                            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                            GTK_STOCK_OK,     GTK_RESPONSE_OK,
484

485
                            NULL);
486

487
  gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
488 489 490
                                           GTK_RESPONSE_OK,
                                           GTK_RESPONSE_CANCEL,
                                           -1);
491

492
  gimp_window_set_transient (GTK_WINDOW (dialog));
Sven Neumann's avatar
Sven Neumann committed
493

Michael Natterer's avatar
Michael Natterer committed
494
  main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
495
  gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
496 497
  gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
                      main_vbox, TRUE, TRUE, 0);
498
  gtk_widget_show (main_vbox);
499

500
  preview = gimp_drawable_preview_new (drawable, NULL);
501
  gtk_box_pack_start (GTK_BOX (main_vbox), preview, TRUE, TRUE, 0);
502
  gtk_widget_show (preview);
503

Michael Natterer's avatar
Michael Natterer committed
504
  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
505
  gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
506 507 508 509
  gtk_widget_show (hbox);

  /*  parameter settings  */
  frame = gimp_frame_new (_("Blur Radius"));
Sven Neumann's avatar
Sven Neumann committed
510
  gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
511 512 513 514 515 516 517
  gtk_widget_show (frame);

  /*  Get the image resolution and unit  */
  gimp_image_get_resolution (image_ID, &xres, &yres);
  unit = gimp_image_get_unit (image_ID);

  size = gimp_coordinates_new (unit, "%a", TRUE, FALSE, -1,
518
                               GIMP_SIZE_ENTRY_UPDATE_SIZE,
519

520
                               (bvals.horizontal == bvals.vertical),
521
                               FALSE,
522

523 524 525
                               _("_Horizontal:"), bvals.horizontal, xres,
                               0, 8 * MAX (drawable->width, drawable->height),
                               0, 0,
526

527 528 529
                               _("_Vertical:"), bvals.vertical, yres,
                               0, 8 * MAX (drawable->width, drawable->height),
                               0, 0);
530 531

  gtk_container_set_border_width (GTK_CONTAINER (size), 6);
532 533 534 535 536
  gtk_container_add (GTK_CONTAINER (frame), size);
  gtk_widget_show (size);

  gimp_size_entry_set_pixel_digits (GIMP_SIZE_ENTRY (size), 1);

537 538
  /*  FIXME: Shouldn't need two signal connections here,
             gimp_coordinates_new() seems to be severily broken.  */
539
  g_signal_connect_swapped (size, "value-changed",
540
                            G_CALLBACK (gimp_preview_invalidate),
541
                            preview);
542
  g_signal_connect_swapped (size, "refval-changed",
543
                            G_CALLBACK (gimp_preview_invalidate),
544 545
                            preview);

546
  g_signal_connect (preview, "invalidated",
547 548 549
                    G_CALLBACK (update_preview),
                    size);

550 551 552 553
  frame = gimp_int_radio_group_new (TRUE, _("Blur Method"),
                                    G_CALLBACK (gimp_radio_button_update),
                                    &bvals.method, bvals.method,

554
                                    _("_IIR"), BLUR_IIR, &button,
555 556 557
                                    _("_RLE"), BLUR_RLE, NULL,

                                    NULL);
558

559 560 561 562
  g_signal_connect_swapped (button, "toggled",
                            G_CALLBACK (gimp_preview_invalidate),
                            preview);

563 564 565
  gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
  gtk_widget_show (frame);

566
  gtk_widget_show (dialog);
567

568
  run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
569 570 571 572 573 574 575

  if (run)
    {
      bvals.horizontal = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (size), 0);
      bvals.vertical   = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (size), 1);
    }

576
  gtk_widget_destroy (dialog);
577 578 579 580

  return run;
}

581 582 583
static void
update_preview (GtkWidget *preview,
                GtkWidget *size)
584
{
585
  gauss (gimp_drawable_preview_get_drawable (GIMP_DRAWABLE_PREVIEW (preview)),
586 587
         gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (size), 0),
         gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (size), 1),
588 589 590
         bvals.method,
         preview);
}
591 592 593 594

/* Convert from separated to premultiplied alpha, on a single scan line. */
static void
multiply_alpha (guchar *buf,
595 596
                gint    width,
                gint    bytes)
597
{
598
  gint i, j;
599

600
  for (i = 0; i < width; i++, buf += bytes)
601
    {
602 603
      gdouble alpha = buf[bytes - 1] * (1.0 / 255.0);

604
      for (j = 0; j < bytes - 1; j++)
605
        buf[j] = ROUND (buf[j] * alpha);
606 607 608
    }
}

609
/* Convert from premultiplied to separated alpha, on a single scan line. */
610 611
static void
separate_alpha (guchar *buf,
612 613
                gint    width,
                gint    bytes)
614
{
615
  gint i, j;
616

617
  for (i = 0; i < width; i++, buf += bytes)
618
    {
619 620 621
      guchar alpha = buf[bytes - 1];

      switch (alpha)
622
        {
623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638
        case 0:
        case 255:
          break;

        default:
          {
            gdouble recip_alpha = 255.0 / alpha;

            for (j = 0; j < bytes - 1; j++)
              {
                gint new_val = ROUND (buf[j] * recip_alpha);

                buf[j] = MIN (255, new_val);
              }
          }
          break;
639
        }
640 641 642
    }
}

643
/*
644
 * run_length_encode (src, rle, pix, dist, width, border, pack);
645
 *
646 647
 * Copy 'width' 8bit pixels from 'src' to 'pix' and extend both sides
 * by 'border' pixels so 'pix[]' is filled from '-border' to 'width+border-1'.
648 649
 *
 * 'dist' is the distance between the pixels in 'src'.
650
 *
651 652 653 654
 * If 'pack' is TRUE, then 'rle' is filled with a run-length encoding
 * of the pixels. In plain english, that means that rle[i] gives the
 * number of times the same pixel is found pix[i], pix[i+1], ...  A
 * standalone pixel has a rle value of 1.
655 656
 *
 * The function returns the number of times 2 identical consecutive pixels
657 658
 * were found.
 *
659 660 661 662
 * Note: The function must be inlined to insure that all tests on
 *       'pack' are efficiently resolved by the compiler (they are in
 *       the critical loop).  As a consequence, the function should
 *       only be called with known constant value for 'pack'.  In the
663 664 665
 *       current implementation, 'pack' is always TRUE but it might be
 *       more efficient to have an 'adaptive' algorithm that switches
 *       to FALSE when the run-length is innefficient.
666 667
 */
static inline gint
668 669 670 671 672 673 674
run_length_encode (const guchar *src,
                   gint         *rle,
                   gint         *pix,
                   gint          dist,  /* distance between 2 src pixels */
                   gint          width,
                   gint          border,
                   gboolean      pack)
675
{
676 677 678 679
  gint last;
  gint count = 0;
  gint i     = width;
  gint same  = 0;
680

681
  src += dist * (width - 1);
682

683 684 685 686
  if (pack)
    rle += width + border - 1;

  pix += width + border - 1;
687

688 689
  last  = *src;
  count = 0;
690 691

  /* the 'end' border */
692 693 694 695 696 697 698
  for (i = 0; i < border; i++)
    {
      count++;
      *pix--  = last;

      if (pack)
        *rle-- = count;
699 700 701
  }

  /* the real pixels */
702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722
  for (i = 0; i < width; i++)
    {
      gint c = *src;
      src -= dist;

      if (pack && c==last)
        {
          count++;
          *pix-- = last;
          *rle-- = count;
          same++;
        }
      else
        {
          count   = 1;
          last    = c;
          *pix--  = last;

          if (pack)
            *rle-- = count;
        }
723 724 725
    }

  /* the start pixels */
726 727 728 729 730 731 732
  for (i = 0; i < border; i++)
    {
      count++;
      *pix-- = last;

      if (pack)
        *rle-- = count;
733
  }
734 735

  return same;
736 737 738
}


739
static void
740 741 742 743 744 745 746 747 748
do_encoded_lre (const gint *enc,
                const gint *src,
                guchar     *dest,
                gint        width,
                gint        length,
                gint        dist,
                const gint *curve,
                gint        ctotal,
                const gint *csum)
749
{
750 751
  gint col;

752
  for (col = 0; col < width; col++, dest += dist)
753
    {
754 755 756 757 758
      const gint *rpt;
      const gint *pix;
      gint        nb;
      gint        s1;
      gint        i;
759
      gint        val   = ctotal / 2;
760
      gint        start = - length;
761

762 763
      rpt = &enc[col + start];
      pix = &src[col + start];
764 765

      s1 = csum[start];
766 767 768 769
      nb = rpt[0];
      i  = start + nb;

      while (i <= length)
770 771 772 773 774 775 776 777 778 779 780 781 782
        {
          gint s2 = csum[i];

          val += pix[0] * (s2-s1);
          s1 = s2;
          rpt = &rpt[nb];
          pix = &pix[nb];
          nb = rpt[0];
          i += nb;
        }

      val += pix[0] * (csum[length] - s1);

783
      val = val / ctotal;
Sven Neumann's avatar
Sven Neumann committed
784
      *dest = MIN (val, 255);
785 786 787 788
    }
}

static void
789 790 791 792 793 794 795
do_full_lre (const gint *src,
             guchar     *dest,
             gint        width,
             gint        length,
             gint        dist,
             const gint *curve,
             gint        ctotal)
796
{
797
  gint col;
798

799
  for (col = 0; col < width; col++, dest += dist)
800
    {
801 802
      const gint *x1;
      const gint *x2;
803
      const gint *c   = &curve[0];
804
      gint        i;
805
      gint        val = ctotal / 2;
806 807 808 809 810 811 812

      x1 = x2 = &src[col];

      /* The central point is a special case since it should only be
       * processed ONCE
       */

813
      val += x1[0] * c[0];
814 815 816 817 818 819 820 821

      c  += 1;
      x1 += 1;
      x2 -= 1;
      i   = length;

      /* Processing multiple points in a single iteration should be
       * faster but is not strictly required.
822
       * Some precise benchmarking will be needed to figure out
823
       * if this is really interesting.
824
       */
825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853
      while (i >= 8)
        {
          val += (x1[0] + x2[-0]) * c[0];
          val += (x1[1] + x2[-1]) * c[1];
          val += (x1[2] + x2[-2]) * c[2];
          val += (x1[3] + x2[-3]) * c[3];
          val += (x1[4] + x2[-4]) * c[4];
          val += (x1[5] + x2[-5]) * c[5];
          val += (x1[6] + x2[-6]) * c[6];
          val += (x1[7] + x2[-7]) * c[7];

          c  += 8;
          x1 += 8;
          x2 -= 8;
          i  -= 8;
        }

      while (i >= 4)
        {
          val += (x1[0] + x2[-0]) * c[0];
          val += (x1[1] + x2[-1]) * c[1];
          val += (x1[2] + x2[-2]) * c[2];
          val += (x1[3] + x2[-3]) * c[3];
          c  += 4;
          x1 += 4;
          x2 -= 4;
          i  -= 4;
        }

854
      /* Only that final loop is strictly required */
855

856
      while (i >= 1)
857 858 859 860 861 862 863 864 865 866 867
        {
          /* process the pixels at the distance i before and after the
           * central point. They must have the same coefficient
           */
          val += (x1[0] + x2[-0]) * c[0];
          c  += 1;
          x1 += 1;
          x2 -= 1;
          i  -= 1;
        }

868
      val = val / ctotal;
Sven Neumann's avatar
Sven Neumann committed
869
      *dest = MIN (val, 255);
870 871
    }
}
872

873
static void
874 875 876 877 878 879 880 881 882
gauss_iir (GimpDrawable *drawable,
           gdouble       horz,
           gdouble       vert,
           BlurMethod    method,
           guchar       *preview_buffer,
           gint          x1,
           gint          y1,
           gint          width,
           gint          height)
883
{
884 885 886
  GimpPixelRgn  src_rgn, dest_rgn;
  gint          bytes;
  gint          has_alpha;
887 888
  guchar       *dest;
  guchar       *src,  *sp_p, *sp_m;
889 890 891 892 893 894 895 896 897 898 899 900 901
  gdouble       n_p[5], n_m[5];
  gdouble       d_p[5], d_m[5];
  gdouble       bd_p[5], bd_m[5];
  gdouble      *val_p = NULL;
  gdouble      *val_m = NULL;
  gdouble      *vp, *vm;
  gint          i, j;
  gint          row, col, b;
  gint          terms;
  gdouble       progress, max_progress;
  gint          initial_p[4];
  gint          initial_m[4];
  gdouble       std_dev;
902 903
  gboolean      direct;
  gint          progress_step;
904

905
  direct = (preview_buffer == NULL);
906

907 908
  bytes = drawable->bpp;
  has_alpha = gimp_drawable_has_alpha (drawable->drawable_id);
909

910 911
  val_p = g_new (gdouble, MAX (width, height) * bytes);
  val_m = g_new (gdouble, MAX (width, height) * bytes);
912

913 914 915 916 917 918 919
  src =  g_new (guchar, MAX (width, height) * bytes);
  dest = g_new (guchar, MAX (width, height) * bytes);

  gimp_pixel_rgn_init (&src_rgn,
                       drawable, 0, 0, drawable->width, drawable->height,
                       FALSE, FALSE);
  if (direct)
920
    {
921 922 923
      gimp_pixel_rgn_init (&dest_rgn,
                           drawable, 0, 0, drawable->width, drawable->height,
                           TRUE, TRUE);
924
    }
925

926

927
  progress = 0.0;
928 929 930
  max_progress  = (horz <= 0.0) ? 0 : width * height * horz;
  max_progress += (vert <= 0.0) ? 0 : width * height * vert;

931 932 933

  /*  First the vertical pass  */
  if (vert > 0.0)
934
    {
935 936 937
      vert = fabs (vert) + 1.0;
      std_dev = sqrt (-(vert * vert) / (2 * log (1.0 / 255.0)));

938 939
      /* We do not want too many progress updates because they
       * can slow down the processing significantly for very
940 941
       * large images
       */
942 943
      progress_step = width / 16;

944 945
      if (progress_step < 5)
        progress_step = 5;
946 947 948 949 950 951

      /*  derive the constants for calculating the gaussian
       *  from the std dev
       */

      find_iir_constants (n_p, n_m, d_p, d_m, bd_p, bd_m, std_dev);
952

953 954
      for (col = 0; col < width; col++)
        {
955 956 957
          memset (val_p, 0, height * bytes * sizeof (gdouble));
          memset (val_m, 0, height * bytes * sizeof (gdouble));

958 959 960 961 962
          gimp_pixel_rgn_get_col (&src_rgn, src, col + x1, y1, height);

          if (has_alpha)
            multiply_alpha (src, height, bytes);

963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001
          sp_p = src;
          sp_m = src + (height - 1) * bytes;
          vp = val_p;
          vm = val_m + (height - 1) * bytes;

          /*  Set up the first vals  */
          for (i = 0; i < bytes; i++)
            {
              initial_p[i] = sp_p[i];
              initial_m[i] = sp_m[i];
            }

          for (row = 0; row < height; row++)
            {
              gdouble *vpptr, *vmptr;
              terms = (row < 4) ? row : 4;

              for (b = 0; b < bytes; b++)
                {
                  vpptr = vp + b; vmptr = vm + b;
                  for (i = 0; i <= terms; i++)
                    {
                      *vpptr += n_p[i] * sp_p[(-i * bytes) + b] - d_p[i] * vp[(-i * bytes) + b];
                      *vmptr += n_m[i] * sp_m[(i * bytes) + b] - d_m[i] * vm[(i * bytes) + b];
                    }
                  for (j = i; j <= 4; j++)
                    {
                      *vpptr += (n_p[j] - bd_p[j]) * initial_p[b];
                      *vmptr += (n_m[j] - bd_m[j]) * initial_m[b];
                    }
                }

              sp_p += bytes;
              sp_m -= bytes;
              vp += bytes;
              vm -= bytes;
            }

          transfer_pixels (val_p, val_m, dest, bytes, height);
1002 1003 1004 1005 1006 1007


          if (has_alpha)
            separate_alpha (dest, height, bytes);

          if (direct)
1008
            {
1009 1010 1011
              gimp_pixel_rgn_set_col(&dest_rgn, dest, col + x1, y1, height);

              progress += height * vert;
1012

1013
              if ((col % progress_step) == 0)
1014 1015
                gimp_progress_update (progress / max_progress);
            }
1016
          else
1017 1018
            {
              for (row = 0; row < height; row++)
1019 1020 1021 1022 1023 1024 1025 1026
                memcpy (preview_buffer + (row * width + col) * bytes,
                        dest + row * bytes,
                        bytes);
            }
        }

      /*  prepare for the horizontal pass  */
      gimp_pixel_rgn_init (&src_rgn,
1027 1028 1029
                           drawable,
                           0, 0,
                           drawable->width, drawable->height,
1030
                           FALSE, TRUE);
1031
    }
1032
  else if (!direct)
1033
    {
1034
      gimp_pixel_rgn_get_rect (&src_rgn,
1035 1036 1037
                               preview_buffer,
                               x1, y1,
                               width, height);
1038
    }
1039

1040 1041 1042
  /*  Now the horizontal pass  */
  if (horz > 0.0)
    {
1043

1044 1045
      /* We do not want too many progress updates because they
       * can slow down the processing significantly for very
1046 1047
       * large images
       */