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
       */
1048
      progress_step = height / 16;
1049
1050
1051

      if (progress_step < 5)
        progress_step = 5;
1052
1053
1054
1055
1056
1057
1058

      horz = fabs (horz) + 1.0;

      if (horz != vert)
        {
          std_dev = sqrt (-(horz * horz) / (2 * log (1.0 / 255.0)));

1059
1060
1061
1062
1063
          /*  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);

1064
1065
        }

1066

1067
1068
      for (row = 0; row < height; row++)
        {
1069
1070
1071

          memset (val_p, 0, width * bytes * sizeof (gdouble));
          memset (val_m, 0, width * bytes * sizeof (gdouble));
1072
1073

          if (direct)
1074
            {
1075
1076
              gimp_pixel_rgn_get_row (&src_rgn, src, x1, row + y1, width);
            }
1077
1078
          else
            {
1079
1080
1081
1082
              memcpy (src,
                      preview_buffer + row * width * bytes,
                      width * bytes);
            }
1083

1084
1085
1086
1087
1088

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


1089
1090
1091
1092
          sp_p = src;
          sp_m = src + (width - 1) * bytes;
          vp = val_p;
          vm = val_m + (width - 1) * bytes;
1093

1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
          /*  Set up the first vals  */
          for (i = 0; i < bytes; i++)
            {
              initial_p[i] = sp_p[i];
              initial_m[i] = sp_m[i];
            }

          for (col = 0; col < width; col++)
            {
              gdouble *vpptr, *vmptr;
1104

1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
              terms = (col < 4) ? col : 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, width);
1132
1133
1134
1135
1136
1137

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

          if (direct)
            {
1138
              gimp_pixel_rgn_set_row (&dest_rgn, dest, x1, row + y1, width);
1139
1140

              progress += width * horz;
1141
1142
1143

              if ((row % progress_step) == 0)
                gimp_progress_update (progress / max_progress);
1144
            }
1145
1146
          else
            {
1147
1148
1149
1150
1151
              memcpy (