convolution-matrix.c 31.6 KB
Newer Older
1
/* Convolution Matrix plug-in for GIMP -- Version 0.1
Elliot Lee's avatar
Elliot Lee committed
2 3 4
 * Copyright (C) 1997 Lauri Alanko <la@iki.fi>
 *
 *
5
 * This program is free software: you can redistribute it and/or modify
Elliot Lee's avatar
Elliot Lee committed
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 3 of the License, or
Elliot Lee's avatar
Elliot Lee committed
8 9 10 11 12 13 14 15
 * (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
16
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Elliot Lee's avatar
Elliot Lee committed
17 18
 */

Tor Lillqvist's avatar
Tor Lillqvist committed
19 20
#include "config.h"

Elliot Lee's avatar
Elliot Lee committed
21 22
#include <stdlib.h>
#include <string.h>
23

24 25
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>
26

27
#include "libgimp/stdplugins-intl.h"
Elliot Lee's avatar
Elliot Lee committed
28

29

30
#define PLUG_IN_PROC   "plug-in-convmatrix"
31
#define PLUG_IN_BINARY "convolution-matrix"
32
#define PLUG_IN_ROLE   "gimp-convolution-matrix"
33

34 35
#define RESPONSE_RESET 1

36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54

#define BIG_MATRIX  /* toggle for 11x11 matrix code experimental*/
#undef BIG_MATRIX


#ifndef BIG_MATRIX
#define MATRIX_SIZE   (5)
#else
#define MATRIX_SIZE   (11)
#endif

#define HALF_WINDOW   (MATRIX_SIZE/2)
#define MATRIX_CELLS  (MATRIX_SIZE*MATRIX_SIZE)
#define DEST_ROWS     (MATRIX_SIZE/2 + 1)
#define CHANNELS      (5)
#define BORDER_MODES  (3)



55 56 57 58
typedef enum
{
  EXTEND,
  WRAP,
David Odin's avatar
David Odin committed
59
  CLEAR
60
} BorderMode;
Elliot Lee's avatar
Elliot Lee committed
61

62
static gchar * const channel_labels[] =
63
{
64 65 66 67 68
  N_("Gr_ey"),
  N_("Re_d"),
  N_("_Green"),
  N_("_Blue"),
  N_("_Alpha")
Elliot Lee's avatar
Elliot Lee committed
69 70
};

71
static gchar * const bmode_labels[] =
72
{
73 74 75
  N_("E_xtend"),
  N_("_Wrap"),
  N_("Cro_p")
Elliot Lee's avatar
Elliot Lee committed
76 77 78
};

/* Declare local functions. */
79
static void query (void);
80
static void run   (const gchar      *name,
81 82 83 84
                   gint              nparams,
                   const GimpParam  *param,
                   gint             *nreturn_vals,
                   GimpParam       **return_vals);
Elliot Lee's avatar
Elliot Lee committed
85

86 87 88 89
static gboolean  convolve_image_dialog (GimpDrawable  *drawable);

static void      convolve_image        (GimpDrawable  *drawable,
                                        GimpPreview   *preview);
90

91
static void      check_config          (GimpDrawable  *drawable);
92

93 94 95 96
static gfloat    convolve_pixel        (guchar       **src_row,
                                        gint           x_offset,
                                        gint           channel,
                                        GimpDrawable  *drawable);
Elliot Lee's avatar
Elliot Lee committed
97

98
const GimpPlugInInfo PLUG_IN_INFO =
Elliot Lee's avatar
Elliot Lee committed
99
{
100 101 102 103
  NULL,   /* init_proc  */
  NULL,   /* quit_proc  */
  query,  /* query_proc */
  run,    /* run_proc   */
Elliot Lee's avatar
Elliot Lee committed
104 105
};

106
static gboolean run_flag = FALSE;
Elliot Lee's avatar
Elliot Lee committed
107

108 109
typedef struct
{
110
  gfloat     matrix[MATRIX_SIZE][MATRIX_SIZE];
111 112
  gfloat     divisor;
  gfloat     offset;
113
  gint       alpha_weighting;
114
  BorderMode bmode;
115
  gboolean   channels[CHANNELS];
116
  gboolean   autoset;
117
} config_struct;
Elliot Lee's avatar
Elliot Lee committed
118

119 120
#ifndef BIG_MATRIX
static const config_struct default_config =
Elliot Lee's avatar
Elliot Lee committed
121
{
122 123 124 125 126 127 128 129 130
  {
    { 0.0, 0.0, 0.0, 0.0, 0.0 },
    { 0.0, 0.0, 0.0, 0.0, 0.0 },
    { 0.0, 0.0, 1.0, 0.0, 0.0 },
    { 0.0, 0.0, 0.0, 0.0, 0.0 },
    { 0.0, 0.0, 0.0, 0.0, 0.0 }
  },                 /* matrix */
  1,                 /* divisor */
  0,                 /* offset */
131
  1,                 /* Alpha-handling algorithm */
132
  CLEAR,             /* border-mode */
David Odin's avatar
David Odin committed
133
  { TRUE, TRUE, TRUE, TRUE, TRUE }, /* Channels mask */
134
  FALSE,              /* autoset */
Elliot Lee's avatar
Elliot Lee committed
135 136
};

137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
#else

static const config_struct default_config =
{
  {
    { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0  },
    { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0  },
    { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0  },
    { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0  },
    { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0  },
    { 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0  },
    { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0  },
    { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0  },
    { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0  },
    { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0  },
    { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0  }
  },                 /* matrix */
  1,                 /* divisor */
  0,                 /* offset */
  1,                 /* Alpha-handling algorithm */
  CLEAR,             /* border-mode */
  { TRUE, TRUE, TRUE, TRUE, TRUE }, /* Channels mask */
  FALSE,              /* autoset */
};

#endif


static config_struct config;
Elliot Lee's avatar
Elliot Lee committed
166

167 168
struct
{
169
  GtkWidget *matrix[MATRIX_SIZE][MATRIX_SIZE];
170 171
  GtkWidget *divisor;
  GtkWidget *offset;
172 173 174
  GtkWidget *alpha_weighting;
  GtkWidget *bmode[BORDER_MODES];
  GtkWidget *channels[CHANNELS];
175
  GtkWidget *autoset;
176
} static widget_set;
Elliot Lee's avatar
Elliot Lee committed
177 178


179
MAIN ()
Elliot Lee's avatar
Elliot Lee committed
180

181 182
static void
query (void)
Elliot Lee's avatar
Elliot Lee committed
183
{
184
  static const GimpParamDef args[] =
185
  {
186
    { GIMP_PDB_INT32,      "run-mode",    "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
187 188
    { GIMP_PDB_IMAGE,      "image",       "Input image (unused)" },
    { GIMP_PDB_DRAWABLE,   "drawable",    "Input drawable" },
189
    { GIMP_PDB_INT32,      "argc-matrix", "The number of elements in the following array. Should be always 25." },
190
    { GIMP_PDB_FLOATARRAY, "matrix",      "The 5x5 convolution matrix" },
191
    { GIMP_PDB_INT32,      "alpha-alg",   "Enable weighting by alpha channel" },
192 193 194
    { GIMP_PDB_FLOAT,      "divisor",     "Divisor" },
    { GIMP_PDB_FLOAT,      "offset",      "Offset" },

195
    { GIMP_PDB_INT32,      "argc-channels", "The number of elements in following array. Should be always 5." },
196
    { GIMP_PDB_INT32ARRAY, "channels",      "Mask of the channels to be filtered" },
197
    { GIMP_PDB_INT32,      "bmode",         "Mode for treating image borders { EXTEND (0), WRAP (1), CLEAR (2) }" },
198 199
  };

200
  gimp_install_procedure (PLUG_IN_PROC,
201
                          N_("Apply a generic 5x5 convolution matrix"),
202 203 204 205 206 207 208 209 210
                          "",
                          "Lauri Alanko",
                          "Lauri Alanko",
                          "1997",
                          N_("_Convolution Matrix..."),
                          "RGB*, GRAY*",
                          GIMP_PLUGIN,
                          G_N_ELEMENTS (args), 0,
                          args, NULL);
211

212
  gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Generic");
Elliot Lee's avatar
Elliot Lee committed
213 214
}

215
static void
216 217 218 219 220
run (const gchar      *name,
     gint              nparams,
     const GimpParam  *param,
     gint             *nreturn_vals,
     GimpParam       **return_vals)
221
{
222 223 224 225 226
  static GimpParam   values[1];
  GimpRunMode        run_mode;
  GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
  gint               x, y;
  GimpDrawable      *drawable;
Elliot Lee's avatar
Elliot Lee committed
227

228
  INIT_I18N ();
229

230 231
  *nreturn_vals = 1;
  *return_vals = values;
Elliot Lee's avatar
Elliot Lee committed
232

233
  run_mode = param[0].data.d_int32;
234

235 236 237
  /*  Get the specified drawable  */
  drawable = gimp_drawable_get (param[2].data.d_drawable);

238
  /*  The plug-in is not able to handle images smaller than 3x3 pixels  */
239 240
  if (drawable->width < 3 || drawable->height < 3)
    {
241 242
      g_message (_("Convolution does not work on layers "
                   "smaller than 3x3 pixels."));
243 244 245 246 247 248
      status = GIMP_PDB_EXECUTION_ERROR;
      values[0].type = GIMP_PDB_STATUS;
      values[0].data.d_status = status;
      return;
    }

249
  config = default_config;
Sven Neumann's avatar
Sven Neumann committed
250
  if (run_mode == GIMP_RUN_NONINTERACTIVE)
251
    {
252
      if ((nparams != 11) && (nparams != 12))
253 254 255
        {
          status = GIMP_PDB_CALLING_ERROR;
        }
256
      else
257
        {
258
          if (param[3].data.d_int32 != MATRIX_CELLS)
259 260 261 262 263
            {
              status = GIMP_PDB_CALLING_ERROR;
            }
          else
            {
264 265 266 267
              for (y = 0; y < MATRIX_SIZE; y++)
                for (x = 0; x < MATRIX_SIZE; x++)
                  config.matrix[x][y]
                    = param[4].data.d_floatarray[x * MATRIX_SIZE + y];
268
            }
269

270
          config.alpha_weighting = param[5].data.d_int32;
271 272
          config.divisor         = param[6].data.d_float;
          config.offset          = param[7].data.d_float;
273

274
          if (param[8].data.d_int32 != CHANNELS)
275 276 277 278 279
            {
              status = GIMP_PDB_CALLING_ERROR;
            }
          else
            {
280 281
              for (y = 0; y < CHANNELS; y++)
                config.channels[y] = param[9].data.d_int32array[y];
282
            }
283

284
          config.bmode = param[10].data.d_int32;
285

286 287
          check_config (drawable);
        }
288 289 290
    }
  else
    {
291
      gimp_get_data (PLUG_IN_PROC, &config);
Elliot Lee's avatar
Elliot Lee committed
292

Sven Neumann's avatar
Sven Neumann committed
293
      if (run_mode == GIMP_RUN_INTERACTIVE)
294 295 296 297 298 299
        {
          /*  Oh boy. We get to do a dialog box, because we can't really
           *  expect the user to set us up with the right values using gdb.
           */
          check_config (drawable);

300
          if (! convolve_image_dialog (drawable))
301 302 303 304 305
            {
              /* The dialog was closed, or something similarly evil happened. */
              status = GIMP_PDB_EXECUTION_ERROR;
            }
        }
306
    }
Elliot Lee's avatar
Elliot Lee committed
307

Sven Neumann's avatar
Sven Neumann committed
308
  if (status == GIMP_PDB_SUCCESS)
309 310
    {
      /*  Make sure that the drawable is gray or RGB color  */
311
      if (gimp_drawable_is_rgb (drawable->drawable_id) ||
312 313 314 315 316
          gimp_drawable_is_gray (drawable->drawable_id))
        {
          gimp_progress_init (_("Applying convolution"));
          gimp_tile_cache_ntiles (2 * (drawable->width /
                                  gimp_tile_width () + 1));
317
          convolve_image (drawable, NULL);
Elliot Lee's avatar
Elliot Lee committed
318

319 320
          if (run_mode != GIMP_RUN_NONINTERACTIVE)
            gimp_displays_flush ();
Elliot Lee's avatar
Elliot Lee committed
321

322
          if (run_mode == GIMP_RUN_INTERACTIVE)
323
            gimp_set_data (PLUG_IN_PROC, &config, sizeof (config));
324
        }
325
      else
326 327 328
        {
          status = GIMP_PDB_EXECUTION_ERROR;
        }
329

330 331
      gimp_drawable_detach (drawable);
    }
Elliot Lee's avatar
Elliot Lee committed
332

Sven Neumann's avatar
Sven Neumann committed
333
  values[0].type = GIMP_PDB_STATUS;
334
  values[0].data.d_status = status;
Elliot Lee's avatar
Elliot Lee committed
335 336 337
}


338
/*  A generic wrapper to gimp_pixel_rgn_get_row which handles unlimited
339 340
 *  wrapping or gives the transparent regions outside the image
 *  fills additional bytes before and after image row to provide border modes.
341
 */
Elliot Lee's avatar
Elliot Lee committed
342

343
static void
Sven Neumann's avatar
Sven Neumann committed
344
my_get_row (GimpPixelRgn *PR,
345 346 347 348
            guchar       *dest,
            gint          x,
            gint          y,
            gint          w)
349
{
350
  gint width, height, bpp;
351 352 353 354
  gint i;

  width  = PR->drawable->width;
  height = PR->drawable->height;
355
  bpp  = PR->drawable->bpp;
356 357

  /* Y-wrappings */
358
  switch (config.bmode)
359 360 361 362
    {
    case WRAP:
      /* Wrapped, so we get the proper row from the other side */
      while (y < 0) /* This is the _sure_ way to wrap. :) */
363
        y += height;
364
      while (y >= height)
365
        y -= height;
366
      break;
367

368 369 370
    case CLEAR:
      /* Beyond borders, so set full transparent. */
      if (y < 0 || y >= height)
371
        {
372
          memset (dest, 0, w * bpp);
373 374
          return; /* Done, so back. */
        }
375

376
    case EXTEND:
377
      y = CLAMP (y , 0 , height - 1);
378 379
      break;
    }
380

381 382 383

  /* X-wrappings */
  switch (config.bmode)
384 385 386
    {
    case CLEAR:
      if (x < 0)
387 388
        {
          i = MIN (w, -x);
389 390
          memset (dest, 0, i * bpp);
          dest += i * bpp;
391 392 393
          w -= i;
          x += i;
        }
394
      if (w)
395 396 397
        {
          i = MIN (w, width);
          gimp_pixel_rgn_get_row (PR, dest, x, y, i);
398
          dest += i * bpp;
399 400 401
          w -= i;
          x += i;
        }
402
      if (w)
403
        memset (dest, 0, w * bpp);
404 405 406 407
      break;

    case WRAP:
      while (x < 0)
408
        x += width;
409 410 411
      i = MIN (w, width - x);
      gimp_pixel_rgn_get_row (PR, dest, x, y, i);
      w -= i;
412
      dest += i * bpp;
413 414
      x = 0;
      while (w)
415 416 417 418
        {
          i = MIN (w, width);
          gimp_pixel_rgn_get_row (PR, dest, x, y, i);
          w -= i;
419
          dest += i * bpp;
420
        }
421
      break;
422

423 424
    case EXTEND:
      if (x < 0)
425 426 427 428
        {
          gimp_pixel_rgn_get_pixel (PR, dest, 0, y);
          x++;
          w--;
429
          dest += bpp;
430 431 432

          while (x < 0 && w)
            {
433
              for (i = 0; i < bpp; i++)
434
                {
435
                  *dest = *(dest - bpp);
436 437 438 439 440 441
                  dest++;
                }
              x++;
              w--;
            }
        }
442
      if (w && width - x > 0)
443 444 445 446
        {
          i = MIN (w, width - x);
          gimp_pixel_rgn_get_row (PR, dest, x, y, i);
          w -= i;
447
          dest += i * bpp;
448
        }
449
      while (w)
450
        {
451
          for (i = 0; i < bpp; i++)
452
            {
453
              *dest= *(dest - bpp);
454 455 456 457 458
              dest++;
            }
          x++;
          w--;
        }
459
      break;
460

461
    }
Elliot Lee's avatar
Elliot Lee committed
462 463
}

464
static gfloat
465 466 467 468
convolve_pixel (guchar       **src_row,
                gint           x_offset,
                gint           channel,
                GimpDrawable  *drawable)
469
{
David Odin's avatar
David Odin committed
470
  static gfloat matrixsum = 0; /* FIXME: this certainly breaks the preview */
471
  static gint bpp         = 0;
472

473 474
  gfloat sum              = 0;
  gfloat alphasum         = 0;
475
  gfloat temp;
476
  gint   x, y;
477
  gint   alpha_channel;
478

479
  if (!bpp)
480
    {
481 482 483 484
      bpp = drawable->bpp;

      for (y = 0; y < MATRIX_SIZE; y++)
        for (x = 0; x < MATRIX_SIZE; x++)
485
          {
486 487
            temp = config.matrix[x][y];
            matrixsum += ABS (config.matrix[x][y]);
488
          }
489
    }
490 491 492 493 494

  alpha_channel = bpp - 1;

  for (y = 0; y < MATRIX_SIZE; y++)
    for (x = 0; x < MATRIX_SIZE; x++)
495
      {
496 497 498
        temp = config.matrix[x][y];

        if (channel != alpha_channel && config.alpha_weighting == 1)
499
          {
500
            temp *= src_row[y][x_offset + x * bpp + alpha_channel - channel];
501 502
            alphasum += ABS (temp);
          }
503 504

        temp *= src_row[y][x_offset + x * bpp];
505
        sum += temp;
506
      }
507 508 509 510

  sum /= config.divisor;

  if (channel != alpha_channel && config.alpha_weighting == 1)
511 512
    {
      if (alphasum != 0)
513
        sum = sum * matrixsum / alphasum;
514
      else
515
        sum = 0;
516
    }
517 518

  sum += config.offset;
519 520 521

  return sum;
}
522

523
static void
524 525
convolve_image (GimpDrawable *drawable,
                GimpPreview  *preview)
526
{
527 528
  GimpPixelRgn  srcPR, destPR;
  gint          width, height, row, col;
529 530
  gint          src_w, src_row_w, src_h, i;
  gint          src_x1, src_y1, src_x2, src_y2;
531
  gint          x1, x2, y1, y2;
532 533 534 535
  guchar       *dest_row[DEST_ROWS];
  guchar       *src_row[MATRIX_SIZE];
  guchar       *tmp_row;
  gint          x_offset;
536 537 538
  gboolean      chanmask[CHANNELS - 1];
  gint          bpp;
  gint          alpha_channel;
539

540 541 542 543 544 545
  /* Get the input area. This is the bounding box of the selection in
   *  the image (or the entire image if there is no selection). Only
   *  operating on the input area is simply an optimization. It doesn't
   *  need to be done for correct operation. (It simply makes it go
   *  faster, since fewer pixels need to be operated on).
   */
546 547
  if (preview)
    {
548 549 550 551
      gimp_preview_get_position (preview, &src_x1, &src_y1);
      gimp_preview_get_size (preview, &src_w, &src_h);
      src_x2 = src_x1 + src_w;
      src_y2 = src_y1 + src_h;
552 553 554
    }
  else
    {
555 556 557 558
      gimp_drawable_mask_bounds (drawable->drawable_id,
                                 &src_x1, &src_y1, &src_x2, &src_y2);
      src_w = src_x2 - src_x1;
      src_h = src_y2 - src_y1;
559
    }
560 561 562 563 564 565

  /* Get the size of the input image. (This will/must be the same
   *  as the size of the output image.
   */
  width  = drawable->width;
  height = drawable->height;
566 567
  bpp  = drawable->bpp;
  alpha_channel = bpp - 1;
568

569
  if (gimp_drawable_is_rgb (drawable->drawable_id))
570
    {
571
      for (i = 0; i < CHANNELS - 1; i++)
572 573
        chanmask[i] = config.channels[i + 1];
    }
574
  else /* Grayscale */
575 576 577
    {
      chanmask[0] = config.channels[0];
    }
578

579
  if (gimp_drawable_has_alpha (drawable->drawable_id))
580
    chanmask[alpha_channel] = config.channels[4];
581 582 583 584 585

  src_row_w = src_w + HALF_WINDOW + HALF_WINDOW;

  for (i = 0; i < MATRIX_SIZE; i++)
    src_row[i] = g_new (guchar, src_row_w * bpp);
586

587 588
  for (i = 0; i < DEST_ROWS; i++)
    dest_row[i]= g_new (guchar, src_w * bpp);
589 590

  /*  initialize the pixel regions  */
591 592 593 594
  x1 = MAX (src_x1 - HALF_WINDOW, 0);
  y1 = MAX (src_y1 - HALF_WINDOW, 0);
  x2 = MIN (src_x2 + HALF_WINDOW, width);
  y2 = MIN (src_y2 + HALF_WINDOW, height);
595
  gimp_pixel_rgn_init (&srcPR, drawable,
596
                       x1, y1, x2 - x1, y2 - y1, FALSE, FALSE);
597
  gimp_pixel_rgn_init (&destPR, drawable,
598
                       src_x1, src_y1, src_w, src_h,
599
                       preview == NULL, TRUE);
600 601

  /* initialize source arrays */
602 603 604
  for (i = 0; i < MATRIX_SIZE; i++)
    my_get_row (&srcPR, src_row[i], src_x1 - HALF_WINDOW,
                src_y1 - HALF_WINDOW + i , src_row_w);
605

606
  for (row = src_y1; row < src_y2; row++)
607
    {
608 609
      gint channel;

610
      x_offset = 0;
611

612 613
      for (col = src_x1; col < src_x2; col++)
        for (channel = 0; channel < bpp; channel++)
614
          {
615 616
            guchar d;

617
            if (chanmask[channel])
618 619 620 621 622 623 624
              {
                gint result;

                result = ROUND (convolve_pixel (src_row,
                                                x_offset, channel, drawable));
                d = CLAMP (result, 0, 255);
              }
David Odin's avatar
David Odin committed
625
            else
626 627 628 629
              {
                /* copy unmodified pixel */
                d = src_row[HALF_WINDOW][x_offset + HALF_WINDOW * bpp];
              }
630

631
            dest_row[HALF_WINDOW][x_offset] = d;
632
            x_offset++;
633
          }
634

635
      if (row >= src_y1 + HALF_WINDOW)
636 637
        gimp_pixel_rgn_set_row (&destPR,
                                dest_row[0], src_x1, row - HALF_WINDOW, src_w);
638

639 640 641
      if (row < src_y2 - 1)
        {
          tmp_row = dest_row[0];
642

643 644 645 646
          for (i = 0; i < DEST_ROWS - 1; i++)
            dest_row[i] = dest_row[i + 1];

          dest_row[DEST_ROWS - 1] = tmp_row;
647

648
          tmp_row = src_row[0];
649

650 651
          for (i = 0; i < MATRIX_SIZE - 1; i++)
            src_row[i] = src_row[i + 1];
652

653
          src_row[MATRIX_SIZE-1] = tmp_row;
654

655 656 657
          my_get_row (&srcPR, src_row[MATRIX_SIZE - 1],
                      src_x1 - HALF_WINDOW, row + HALF_WINDOW + 1, src_row_w);
        }
658

659
      if ((row % 10 == 0) && !preview)
660
        gimp_progress_update ((double) (row - src_y1) / src_h);
661
    }
662

663 664 665 666
  /* put the remaining rows in the buffer in place */
  for (i = 1; i <  DEST_ROWS; i++)
    gimp_pixel_rgn_set_row (&destPR, dest_row[i],
                            src_x1, src_y2 + i - 1 - HALF_WINDOW, src_w);
667

668 669

  /*  update the region  */
670 671 672 673 674 675 676
  if (preview)
    {
      gimp_drawable_preview_draw_region (GIMP_DRAWABLE_PREVIEW (preview),
                                         &destPR);
    }
  else
    {
677
      gimp_progress_update (1.0);
678 679
      gimp_drawable_flush (drawable);
      gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
680 681
      gimp_drawable_update (drawable->drawable_id,
                            src_x1, src_y1, src_x2 - src_x1, src_y2 - src_y1);
682
    }
683

684 685
  for (i = 0; i < MATRIX_SIZE; i++)
    g_free (src_row[i]);
686

687 688
  for (i = 0; i < DEST_ROWS; i++)
    g_free (dest_row[i]);
Elliot Lee's avatar
Elliot Lee committed
689 690 691 692 693 694
}

/***************************************************
 * GUI stuff
 */

695 696 697
static void
redraw_matrix (void)
{
698
  gint  x, y;
699
  gchar buffer[12];
Elliot Lee's avatar
Elliot Lee committed
700

701 702
  for (y = 0; y < MATRIX_SIZE; y++)
    for (x = 0; x < MATRIX_SIZE; x++)
703
      {
704
        g_snprintf (buffer, sizeof (buffer), "%g", config.matrix[x][y]);
705
        gtk_entry_set_text (GTK_ENTRY (widget_set.matrix[x][y]), buffer);
706
      }
Elliot Lee's avatar
Elliot Lee committed
707 708
}

709 710 711
static void
redraw_channels (void)
{
712
  gint i;
Elliot Lee's avatar
Elliot Lee committed
713

714 715 716 717
  for (i = 0; i < CHANNELS; i++)
    if (widget_set.channels[i])
      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget_set.channels[i]),
                                    config.channels[i]);
Elliot Lee's avatar
Elliot Lee committed
718 719
}

720 721 722
static void
redraw_autoset (void)
{
723 724
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget_set.autoset),
                                config.autoset);
Elliot Lee's avatar
Elliot Lee committed
725 726
}

727
static void
728
redraw_alpha_weighting (void)
729
{
730 731
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget_set.alpha_weighting),
                                config.alpha_weighting > 0);
Elliot Lee's avatar
Elliot Lee committed
732 733
}

734 735 736 737
static void
redraw_off_and_div (void)
{
  gchar buffer[12];
Elliot Lee's avatar
Elliot Lee committed
738

739
  g_snprintf (buffer, sizeof (buffer), "%g", config.divisor);
740
  gtk_entry_set_text (GTK_ENTRY (widget_set.divisor), buffer);
741

742
  g_snprintf (buffer, sizeof (buffer), "%g", config.offset);
743
  gtk_entry_set_text (GTK_ENTRY (widget_set.offset), buffer);
Elliot Lee's avatar
Elliot Lee committed
744 745
}

746 747 748 749
static void
redraw_bmode (void)
{
  gtk_toggle_button_set_active
750
    (GTK_TOGGLE_BUTTON (widget_set.bmode[config.bmode]), TRUE);
Elliot Lee's avatar
Elliot Lee committed
751 752
}

753 754 755 756 757 758
static void
redraw_all (void)
{
  redraw_matrix ();
  redraw_off_and_div ();
  redraw_autoset ();
759
  redraw_alpha_weighting ();
760 761 762
  redraw_bmode ();
  redraw_channels ();
}
Elliot Lee's avatar
Elliot Lee committed
763

764 765 766
static void
check_matrix (void)
{
767 768 769
  gint      x, y;
  gboolean  valid = FALSE;
  gfloat    sum   = 0.0;
770

771 772
  for (y = 0; y < MATRIX_SIZE; y++)
    for (x = 0; x < MATRIX_SIZE; x++)
773
      {
774 775
        sum += config.matrix[x][y];
        if (config.matrix[x][y] != 0.0)
776
          valid = TRUE;
777 778
      }

779
  if (config.autoset)
780 781
    {
      if (sum > 0)
782
        {
783 784
          config.offset = 0;
          config.divisor = sum;
785
        }
786
      else if (sum < 0)
787
        {
788 789
          config.offset = 255;
          config.divisor = -sum;
790
        }
791
      else
792
        {
793
          config.offset = 128;
794 795 796
          /* The sum is 0, so this is probably some sort of
           * embossing filter. Should divisor be autoset to 1
           * or left undefined, ie. for the user to define? */
797
          config.divisor = 1;
798
        }
799 800
      redraw_off_and_div ();
    }
Elliot Lee's avatar
Elliot Lee committed
801 802
}

803
static void
804 805 806
response_callback (GtkWidget    *widget,
                   gint          response_id,
                   GimpDrawable *drawable)
807
{
808 809 810
  switch (response_id)
    {
    case RESPONSE_RESET:
811
      config = default_config;
812
      check_config (drawable);
813 814 815 816 817
      redraw_all ();
      break;

    case GTK_RESPONSE_OK:
      run_flag = TRUE;
Elliot Lee's avatar
Elliot Lee committed
818

819
    default:
820
      gtk_widget_destroy (GTK_WIDGET (widget));
821 822
      break;
    }
Elliot Lee's avatar
Elliot Lee committed
823 824
}

825 826
/* Checks that the configuration is valid for the image type */
static void
827
check_config (GimpDrawable *drawable)
Elliot Lee's avatar
Elliot Lee committed
828
{
829
  config.alpha_weighting =