~ubuntu-branches/ubuntu/jaunty/gimp/jaunty-security

« back to all changes in this revision

Viewing changes to plug-ins/common/edge-sobel.c

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2008-10-06 13:30:41 UTC
  • mto: This revision was merged to the branch mainline in revision 35.
  • Revision ID: james.westby@ubuntu.com-20081006133041-3panbkcanaymfsmp
Tags: upstream-2.6.0
ImportĀ upstreamĀ versionĀ 2.6.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* GIMP - The GNU Image Manipulation Program
 
2
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 
3
 *
 
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 2 of the License, or
 
7
 * (at your option) any later version.
 
8
 *
 
9
 * This program is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
 * GNU General Public License for more details.
 
13
 *
 
14
 * You should have received a copy of the GNU General Public License
 
15
 * along with this program; if not, write to the Free Software
 
16
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
17
 */
 
18
 
 
19
/* This plugin by thorsten@arch.usyd.edu.au           */
 
20
/* Based on S&P's Gauss and Blur filters              */
 
21
 
 
22
#include "config.h"
 
23
 
 
24
#include <stdlib.h>
 
25
#include <string.h>
 
26
 
 
27
#include <libgimp/gimp.h>
 
28
#include <libgimp/gimpui.h>
 
29
 
 
30
#include "libgimp/stdplugins-intl.h"
 
31
 
 
32
 
 
33
#define PLUG_IN_PROC   "plug-in-sobel"
 
34
#define PLUG_IN_BINARY "edge-sobel"
 
35
 
 
36
 
 
37
typedef struct
 
38
{
 
39
  gboolean horizontal;
 
40
  gboolean vertical;
 
41
  gboolean keep_sign;
 
42
} SobelValues;
 
43
 
 
44
 
 
45
/* Declare local functions.
 
46
 */
 
47
static void   query  (void);
 
48
static void   run    (const gchar      *name,
 
49
                      gint              nparams,
 
50
                      const GimpParam  *param,
 
51
                      gint             *nreturn_vals,
 
52
                      GimpParam       **return_vals);
 
53
 
 
54
static void   sobel  (GimpDrawable     *drawable,
 
55
                      gboolean          horizontal,
 
56
                      gboolean          vertical,
 
57
                      gboolean          keep_sign,
 
58
                      GimpPreview      *preview);
 
59
 
 
60
/*
 
61
 * Sobel interface
 
62
 */
 
63
static gboolean  sobel_dialog         (GimpDrawable *drawable);
 
64
static void      sobel_preview_update (GimpPreview  *preview);
 
65
 
 
66
/*
 
67
 * Sobel helper functions
 
68
 */
 
69
static void      sobel_prepare_row (GimpPixelRgn *pixel_rgn,
 
70
                                    guchar       *data,
 
71
                                    gint          x,
 
72
                                    gint          y,
 
73
                                    gint          w);
 
74
 
 
75
 
 
76
const GimpPlugInInfo PLUG_IN_INFO =
 
77
{
 
78
  NULL,  /* init_proc  */
 
79
  NULL,  /* quit_proc  */
 
80
  query, /* query_proc */
 
81
  run,   /* run_proc   */
 
82
};
 
83
 
 
84
static SobelValues bvals =
 
85
{
 
86
  TRUE,  /*  horizontal sobel  */
 
87
  TRUE,  /*  vertical sobel    */
 
88
  TRUE   /*  keep sign         */
 
89
};
 
90
 
 
91
 
 
92
MAIN ()
 
93
 
 
94
static void
 
95
query (void)
 
96
{
 
97
  static const GimpParamDef args[] =
 
98
  {
 
99
    { GIMP_PDB_INT32,    "run-mode",   "Interactive, non-interactive"  },
 
100
    { GIMP_PDB_IMAGE,    "image",      "Input image (unused)"          },
 
101
    { GIMP_PDB_DRAWABLE, "drawable",   "Input drawable"                },
 
102
    { GIMP_PDB_INT32,    "horizontal", "Sobel in horizontal direction" },
 
103
    { GIMP_PDB_INT32,    "vertical",   "Sobel in vertical direction"   },
 
104
    { GIMP_PDB_INT32,    "keep-sign",  "Keep sign of result (one direction only)" }
 
105
  };
 
106
 
 
107
  gimp_install_procedure (PLUG_IN_PROC,
 
108
                          N_("Specialized direction-dependent edge detection"),
 
109
                          "This plugin calculates the gradient with a sobel "
 
110
                          "operator. The user can specify which direction to "
 
111
                          "use. When both directions are used, the result is "
 
112
                          "the RMS of the two gradients; if only one direction "
 
113
                          "is used, the result either the absolut value of the "
 
114
                          "gradient, or 127 + gradient (if the 'keep sign' "
 
115
                          "switch is on). This way, information about the "
 
116
                          "direction of the gradient is preserved. Resulting "
 
117
                          "images are not autoscaled.",
 
118
                          "Thorsten Schnier",
 
119
                          "Thorsten Schnier",
 
120
                          "1997",
 
121
                          N_("_Sobel..."),
 
122
                          "RGB*, GRAY*",
 
123
                          GIMP_PLUGIN,
 
124
                          G_N_ELEMENTS (args), 0,
 
125
                          args, NULL);
 
126
 
 
127
  gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Edge-Detect");
 
128
}
 
129
 
 
130
static void
 
131
run (const gchar      *name,
 
132
     gint              nparams,
 
133
     const GimpParam  *param,
 
134
     gint             *nreturn_vals,
 
135
     GimpParam       **return_vals)
 
136
{
 
137
  static GimpParam   values[2];
 
138
  GimpDrawable      *drawable;
 
139
  GimpRunMode        run_mode;
 
140
  GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
 
141
 
 
142
  run_mode = param[0].data.d_int32;
 
143
 
 
144
  INIT_I18N ();
 
145
 
 
146
  *nreturn_vals = 1;
 
147
  *return_vals  = values;
 
148
 
 
149
  values[0].type          = GIMP_PDB_STATUS;
 
150
  values[0].data.d_status = status;
 
151
 
 
152
  /*  Get the specified drawable  */
 
153
  drawable = gimp_drawable_get (param[2].data.d_drawable);
 
154
 
 
155
  gimp_tile_cache_ntiles (2 * drawable->ntile_cols);
 
156
 
 
157
  switch (run_mode)
 
158
   {
 
159
    case GIMP_RUN_INTERACTIVE:
 
160
      /*  Possibly retrieve data  */
 
161
      gimp_get_data (PLUG_IN_PROC, &bvals);
 
162
 
 
163
      /*  First acquire information with a dialog  */
 
164
      if (! sobel_dialog (drawable))
 
165
        return;
 
166
      break;
 
167
 
 
168
    case GIMP_RUN_NONINTERACTIVE:
 
169
      /*  Make sure all the arguments are there!  */
 
170
      if (nparams != 6)
 
171
        {
 
172
          status = GIMP_PDB_CALLING_ERROR;
 
173
        }
 
174
      else
 
175
        {
 
176
          bvals.horizontal = (param[4].data.d_int32) ? TRUE : FALSE;
 
177
          bvals.vertical   = (param[5].data.d_int32) ? TRUE : FALSE;
 
178
          bvals.keep_sign  = (param[6].data.d_int32) ? TRUE : FALSE;
 
179
        }
 
180
      break;
 
181
 
 
182
    case GIMP_RUN_WITH_LAST_VALS:
 
183
      /*  Possibly retrieve data  */
 
184
      gimp_get_data (PLUG_IN_PROC, &bvals);
 
185
      break;
 
186
 
 
187
    default:
 
188
      break;
 
189
    }
 
190
 
 
191
  /*  Make sure that the drawable is gray or RGB color  */
 
192
  if (gimp_drawable_is_rgb (drawable->drawable_id) ||
 
193
      gimp_drawable_is_gray (drawable->drawable_id))
 
194
    {
 
195
      sobel (drawable,
 
196
             bvals.horizontal, bvals.vertical, bvals.keep_sign,
 
197
             NULL);
 
198
 
 
199
      if (run_mode != GIMP_RUN_NONINTERACTIVE)
 
200
        gimp_displays_flush ();
 
201
 
 
202
 
 
203
      /*  Store data  */
 
204
      if (run_mode == GIMP_RUN_INTERACTIVE)
 
205
        gimp_set_data (PLUG_IN_PROC, &bvals, sizeof (bvals));
 
206
    }
 
207
  else
 
208
    {
 
209
      status        = GIMP_PDB_EXECUTION_ERROR;
 
210
      *nreturn_vals = 2;
 
211
      values[1].type          = GIMP_PDB_STRING;
 
212
      values[1].data.d_string = _("Cannot operate on indexed color images.");
 
213
    }
 
214
 
 
215
  gimp_drawable_detach (drawable);
 
216
 
 
217
  values[0].data.d_status = status;
 
218
}
 
219
 
 
220
static gboolean
 
221
sobel_dialog (GimpDrawable *drawable)
 
222
{
 
223
  GtkWidget *dialog;
 
224
  GtkWidget *main_vbox;
 
225
  GtkWidget *preview;
 
226
  GtkWidget *toggle;
 
227
  gboolean   run;
 
228
 
 
229
  gimp_ui_init (PLUG_IN_BINARY, FALSE);
 
230
 
 
231
  dialog = gimp_dialog_new (_("Sobel Edge Detection"), PLUG_IN_BINARY,
 
232
                            NULL, 0,
 
233
                            gimp_standard_help_func, PLUG_IN_PROC,
 
234
 
 
235
                            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
 
236
                            GTK_STOCK_OK,     GTK_RESPONSE_OK,
 
237
 
 
238
                            NULL);
 
239
 
 
240
  gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
 
241
                                           GTK_RESPONSE_OK,
 
242
                                           GTK_RESPONSE_CANCEL,
 
243
                                           -1);
 
244
 
 
245
  gimp_window_set_transient (GTK_WINDOW (dialog));
 
246
 
 
247
  main_vbox = gtk_vbox_new (FALSE, 12);
 
248
  gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
 
249
  gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), main_vbox);
 
250
  gtk_widget_show (main_vbox);
 
251
 
 
252
  preview = gimp_drawable_preview_new (drawable, NULL);
 
253
  gtk_box_pack_start (GTK_BOX (main_vbox), preview, TRUE, TRUE, 0);
 
254
  gtk_widget_show (preview);
 
255
 
 
256
  g_signal_connect (preview, "invalidated",
 
257
                    G_CALLBACK (sobel_preview_update),
 
258
                    NULL);
 
259
 
 
260
  toggle = gtk_check_button_new_with_mnemonic (_("Sobel _horizontally"));
 
261
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), bvals.horizontal);
 
262
  gtk_box_pack_start (GTK_BOX (main_vbox), toggle, FALSE, FALSE, 0);
 
263
  gtk_widget_show (toggle);
 
264
 
 
265
  g_signal_connect (toggle, "toggled",
 
266
                    G_CALLBACK (gimp_toggle_button_update),
 
267
                    &bvals.horizontal);
 
268
  g_signal_connect_swapped (toggle, "toggled",
 
269
                            G_CALLBACK (gimp_preview_invalidate),
 
270
                            preview);
 
271
 
 
272
  toggle = gtk_check_button_new_with_mnemonic (_("Sobel _vertically"));
 
273
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), bvals.vertical);
 
274
  gtk_box_pack_start (GTK_BOX (main_vbox), toggle, FALSE, FALSE, 0);
 
275
  gtk_widget_show (toggle);
 
276
 
 
277
  g_signal_connect (toggle, "toggled",
 
278
                    G_CALLBACK (gimp_toggle_button_update),
 
279
                    &bvals.vertical);
 
280
  g_signal_connect_swapped (toggle, "toggled",
 
281
                            G_CALLBACK (gimp_preview_invalidate),
 
282
                            preview);
 
283
 
 
284
  toggle = gtk_check_button_new_with_mnemonic (_("_Keep sign of result "
 
285
                                                 "(one direction only)"));
 
286
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), bvals.keep_sign);
 
287
  gtk_box_pack_start (GTK_BOX (main_vbox), toggle, FALSE, FALSE, 0);
 
288
  gtk_widget_show (toggle);
 
289
 
 
290
  g_signal_connect (toggle, "toggled",
 
291
                    G_CALLBACK (gimp_toggle_button_update),
 
292
                    &bvals.keep_sign);
 
293
  g_signal_connect_swapped (toggle, "toggled",
 
294
                            G_CALLBACK (gimp_preview_invalidate),
 
295
                            preview);
 
296
 
 
297
  gtk_widget_show (dialog);
 
298
 
 
299
  run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
 
300
 
 
301
  gtk_widget_destroy (dialog);
 
302
 
 
303
  return run;
 
304
}
 
305
 
 
306
static void
 
307
sobel_preview_update (GimpPreview *preview)
 
308
{
 
309
  sobel (gimp_drawable_preview_get_drawable (GIMP_DRAWABLE_PREVIEW (preview)),
 
310
         bvals.horizontal,
 
311
         bvals.vertical,
 
312
         bvals.keep_sign,
 
313
         preview);
 
314
}
 
315
 
 
316
static void
 
317
sobel_prepare_row (GimpPixelRgn *pixel_rgn,
 
318
                   guchar       *data,
 
319
                   gint          x,
 
320
                   gint          y,
 
321
                   gint          w)
 
322
{
 
323
  gint b;
 
324
 
 
325
  y = CLAMP (y, 0, pixel_rgn->h - 1);
 
326
  gimp_pixel_rgn_get_row (pixel_rgn, data, x, y, w);
 
327
 
 
328
  /*  Fill in edge pixels  */
 
329
  for (b = 0; b < pixel_rgn->bpp; b++)
 
330
    {
 
331
      data[-(int)pixel_rgn->bpp + b] = data[b];
 
332
      data[w * pixel_rgn->bpp + b] = data[(w - 1) * pixel_rgn->bpp + b];
 
333
    }
 
334
}
 
335
 
 
336
#define RMS(a, b) (sqrt ((a) * (a) + (b) * (b)))
 
337
 
 
338
static void
 
339
sobel (GimpDrawable *drawable,
 
340
       gboolean      do_horizontal,
 
341
       gboolean      do_vertical,
 
342
       gboolean      keep_sign,
 
343
       GimpPreview  *preview)
 
344
{
 
345
  GimpPixelRgn  srcPR, destPR;
 
346
  gint          width, height;
 
347
  gint          bytes;
 
348
  gint          gradient, hor_gradient, ver_gradient;
 
349
  guchar       *dest, *d;
 
350
  guchar       *prev_row, *pr;
 
351
  guchar       *cur_row, *cr;
 
352
  guchar       *next_row, *nr;
 
353
  guchar       *tmp;
 
354
  gint          row, col;
 
355
  gint          x1, y1, x2, y2;
 
356
  gboolean      alpha;
 
357
  gint          counter;
 
358
  guchar       *preview_buffer = NULL;
 
359
 
 
360
  if (preview)
 
361
    {
 
362
      gimp_preview_get_position (preview, &x1, &y1);
 
363
      gimp_preview_get_size (preview, &width, &height);
 
364
      x2 = x1 + width;
 
365
      y2 = y1 + height;
 
366
    }
 
367
  else
 
368
    {
 
369
      gimp_drawable_mask_bounds (drawable->drawable_id, &x1, &y1, &x2, &y2);
 
370
      gimp_progress_init (_("Sobel edge detecting"));
 
371
      width  = x2 - x1;
 
372
      height = y2 - y1;
 
373
    }
 
374
 
 
375
  /* Get the size of the input image. (This will/must be the same
 
376
   *  as the size of the output image.
 
377
   */
 
378
  bytes  = drawable->bpp;
 
379
  alpha  = gimp_drawable_has_alpha (drawable->drawable_id);
 
380
 
 
381
  /*  allocate row buffers  */
 
382
  prev_row = g_new (guchar, (width + 2) * bytes);
 
383
  cur_row  = g_new (guchar, (width + 2) * bytes);
 
384
  next_row = g_new (guchar, (width + 2) * bytes);
 
385
  dest     = g_new (guchar, width * bytes);
 
386
 
 
387
  /*  initialize the pixel regions  */
 
388
  gimp_pixel_rgn_init (&srcPR, drawable, 0, 0,
 
389
                       drawable->width, drawable->height,
 
390
                       FALSE, FALSE);
 
391
 
 
392
  if (preview)
 
393
    {
 
394
      preview_buffer = g_new (guchar, width * height * bytes);
 
395
    }
 
396
  else
 
397
    {
 
398
      gimp_pixel_rgn_init (&destPR, drawable, 0, 0,
 
399
                           drawable->width, drawable->height,
 
400
                           TRUE, TRUE);
 
401
    }
 
402
 
 
403
  pr = prev_row + bytes;
 
404
  cr = cur_row  + bytes;
 
405
  nr = next_row + bytes;
 
406
 
 
407
  sobel_prepare_row (&srcPR, pr, x1, y1 - 1, width);
 
408
  sobel_prepare_row (&srcPR, cr, x1, y1, width);
 
409
  counter =0;
 
410
  /*  loop through the rows, applying the sobel convolution  */
 
411
  for (row = y1; row < y2; row++)
 
412
    {
 
413
      /*  prepare the next row  */
 
414
      sobel_prepare_row (&srcPR, nr, x1, row + 1, width);
 
415
 
 
416
      d = dest;
 
417
      for (col = 0; col < width * bytes; col++)
 
418
        {
 
419
          hor_gradient = (do_horizontal ?
 
420
                          ((pr[col - bytes] +  2 * pr[col] + pr[col + bytes]) -
 
421
                           (nr[col - bytes] + 2 * nr[col] + nr[col + bytes]))
 
422
                          : 0);
 
423
          ver_gradient = (do_vertical ?
 
424
                          ((pr[col - bytes] + 2 * cr[col - bytes] + nr[col - bytes]) -
 
425
                           (pr[col + bytes] + 2 * cr[col + bytes] + nr[col + bytes]))
 
426
                          : 0);
 
427
          gradient = (do_vertical && do_horizontal) ?
 
428
            (ROUND (RMS (hor_gradient, ver_gradient)) / 5.66) /* always >0 */
 
429
            : (keep_sign ? (127 + (ROUND ((hor_gradient + ver_gradient) / 8.0)))
 
430
               : (ROUND (abs (hor_gradient + ver_gradient) / 4.0)));
 
431
 
 
432
          if (alpha && (((col + 1) % bytes) == 0))
 
433
            { /* the alpha channel */
 
434
              *d++ = (counter == 0) ? 0 : 255;
 
435
              counter = 0;
 
436
            }
 
437
          else
 
438
            {
 
439
              *d++ = gradient;
 
440
              if (gradient > 10) counter ++;
 
441
            }
 
442
        }
 
443
      /*  shuffle the row pointers  */
 
444
      tmp = pr;
 
445
      pr = cr;
 
446
      cr = nr;
 
447
      nr = tmp;
 
448
 
 
449
      /*  store the dest  */
 
450
      if (preview)
 
451
        {
 
452
          memcpy (preview_buffer + width * (row - y1) * bytes,
 
453
                  dest,
 
454
                  width * bytes);
 
455
        }
 
456
      else
 
457
        {
 
458
          gimp_pixel_rgn_set_row (&destPR, dest, x1, row, width);
 
459
 
 
460
          if ((row % 20) == 0)
 
461
            gimp_progress_update ((double) row / (double) (y2 - y1));
 
462
        }
 
463
    }
 
464
 
 
465
  if (preview)
 
466
    {
 
467
      gimp_preview_draw_buffer (preview, preview_buffer, width * bytes);
 
468
      g_free (preview_buffer);
 
469
    }
 
470
  else
 
471
    {
 
472
      /*  update the sobeled region  */
 
473
      gimp_drawable_flush (drawable);
 
474
      gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
 
475
      gimp_drawable_update (drawable->drawable_id, x1, y1, width, height);
 
476
    }
 
477
 
 
478
  g_free (prev_row);
 
479
  g_free (cur_row);
 
480
  g_free (next_row);
 
481
  g_free (dest);
 
482
}