home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / plug-ins / common / edge.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-08-24  |  20.5 KB  |  734 lines

  1. /* edge filter for the GIMP
  2.  *  -Peter Mattis
  3.  *
  4.  * This filter performs edge detection on the input image.
  5.  *  The code for this filter is based on "pgmedge", a program
  6.  *  that is part of the netpbm package.
  7.  */
  8.  
  9. /* pgmedge.c - edge-detect a portable graymap
  10. **
  11. ** Copyright (C) 1989 by Jef Poskanzer.
  12. **
  13. ** Permission to use, copy, modify, and distribute this software and its
  14. ** documentation for any purpose and without fee is hereby granted, provided
  15. ** that the above copyright notice appear in all copies and that both that
  16. ** copyright notice and this permission notice appear in supporting
  17. ** documentation.  This software is provided "as is" without express or
  18. ** implied warranty.
  19. */
  20.  
  21. /*
  22.  *  Ported to GIMP Plug-in API 1.0
  23.  *  version 1.07
  24.  *  This version requires GIMP v0.99.10 or above.
  25.  *
  26.  *  This plug-in performs edge detection. The code is based on edge.c
  27.  *  for GIMP 0.54 by Peter Mattis.
  28.  *
  29.  *    Eiichi Takamori <taka@ma1.seikyou.ne.jp>
  30.  *    http://ha1.seikyou.ne.jp/home/taka/gimp/
  31.  *
  32.  *  Tips: you can enter arbitrary value into entry.
  33.  *    (not bounded between 1.0 and 10.0)
  34.  *
  35.  *  Changes from version 1.06 to version 1.07:
  36.  *  - Added entry
  37.  *  - Cleaned up code a bit
  38.  *
  39.  *  Differences from Peter Mattis's original `edge' plug-in:
  40.  *    - Added Wrapmode. (useful for tilable images)
  41.  *    - Enhanced speed in this version.
  42.  *    - It works with the alpha channel.
  43.  */
  44.  
  45. #include "config.h"
  46.  
  47. #include <stdio.h>
  48. #include <stdlib.h>
  49.  
  50. #include <gtk/gtk.h>
  51.  
  52. #include <libgimp/gimp.h>
  53. #include <libgimp/gimpui.h>
  54.  
  55. #include "libgimp/stdplugins-intl.h"
  56.  
  57.  
  58. #ifdef RCSID
  59. static gchar rcsid[] = "$Id: edge.c,v 1.18 2000/08/22 01:26:54 neo Exp $";
  60. #endif
  61.  
  62. /* Some useful macros */
  63.  
  64. #define TILE_CACHE_SIZE 48
  65.  
  66. enum
  67. {
  68.   WRAP,
  69.   SMEAR,
  70.   BLACK
  71. };
  72.  
  73. typedef struct
  74. {
  75.   gdouble amount;
  76.   gint    wrapmode;
  77. } EdgeVals;
  78.  
  79. typedef struct
  80. {
  81.   gint run;
  82. } EdgeInterface;
  83.  
  84. typedef struct
  85. {
  86.   GimpTile     *tile;
  87.   gint         row, col;    /* tile's row, col */
  88.   gint       bpp;
  89.   gint       tile_width, tile_height;
  90.   GimpDrawable *drawable;
  91.   gint       drawable_width, drawable_height;
  92. } TileBuf;
  93.  
  94. /*
  95.  * Function prototypes.
  96.  */
  97.  
  98. static void      query  (void);
  99. static void      run    (gchar    *name,
  100.              gint      nparams,
  101.              GimpParam   *param,
  102.              gint     *nreturn_vals,
  103.              GimpParam  **return_vals);
  104.  
  105. static void      edge        (GimpDrawable *drawable);
  106. static gint      edge_dialog (GimpDrawable *drawable);
  107.  
  108. static long      long_sqrt   (long n);
  109.  
  110. static void   init_tile_buf  (TileBuf   *buf,
  111.                   GimpDrawable *drawable);
  112. static void   get_tile_pixel (TileBuf   *buf,
  113.                   gint       x,
  114.                   gint       y, 
  115.                   guchar    *pixel,
  116.                   gint       wrapmode);
  117. static void   end_tile_buf   (TileBuf   *buf);
  118.  
  119. /***** Local vars *****/
  120.  
  121. GimpPlugInInfo PLUG_IN_INFO =
  122. {
  123.   NULL,  /* init  */
  124.   NULL,  /* quit  */
  125.   query, /* query */
  126.   run,   /* run   */
  127. };
  128.  
  129. static EdgeVals evals =
  130. {
  131.   2.0,   /* amount   */
  132.   SMEAR  /* wrapmode */
  133. };
  134.  
  135. static EdgeInterface eint =
  136. {
  137.   FALSE  /* run */
  138. };
  139.  
  140. /***** Functions *****/
  141.  
  142. MAIN ()
  143.  
  144. static void
  145. query (void)
  146. {
  147.   static GimpParamDef args[] =
  148.   {
  149.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  150.     { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
  151.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  152.     { GIMP_PDB_FLOAT, "amount", "Edge detection amount" },
  153.     { GIMP_PDB_INT32, "wrapmode", "Edge detection behavior: { WRAP (0), SMEAR (1), BLACK (2) }" }
  154.   };
  155.   static gint nargs = sizeof (args) / sizeof (args[0]);
  156.  
  157.   gchar *help_string =
  158.     "Perform edge detection on the contents of the specified drawable. It "
  159.     "applies, I think, convolution with 3x3 kernel. AMOUNT is an arbitrary "
  160.     "constant, WRAPMODE is like displace plug-in (useful for tilable image).";
  161.  
  162.   gimp_install_procedure ("plug_in_edge",
  163.               "Perform edge detection on the contents of the specified drawable",
  164.               help_string,
  165.               "Peter Mattis & (ported to 1.0 by) Eiichi Takamori",
  166.               "Peter Mattis",
  167.               "1996",
  168.               N_("<Image>/Filters/Edge-Detect/Edge..."),
  169.               "RGB*, GRAY*",
  170.               GIMP_PLUGIN,
  171.               nargs, 0,
  172.               args, NULL);
  173. }
  174.  
  175. static void
  176. run (gchar  *name,
  177.      gint    nparams,
  178.      GimpParam  *param,
  179.      gint   *nreturn_vals,
  180.      GimpParam **return_vals)
  181. {
  182.   static GimpParam values[1];
  183.   GimpDrawable *drawable;
  184.   GimpRunModeType run_mode;
  185.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  186.  
  187.   run_mode = param[0].data.d_int32;
  188.  
  189.   /*  Get the specified drawable  */
  190.   drawable = gimp_drawable_get (param[2].data.d_drawable);
  191.  
  192.   *nreturn_vals = 1;
  193.   *return_vals = values;
  194.  
  195.   values[0].type = GIMP_PDB_STATUS;
  196.   values[0].data.d_status = status;
  197.  
  198.   switch (run_mode)
  199.     {
  200.     case GIMP_RUN_INTERACTIVE:
  201.       INIT_I18N_UI();
  202.       /*  Possibly retrieve data  */
  203.       gimp_get_data ("plug_in_edge", &evals);
  204.  
  205.       /*  First acquire information with a dialog  */
  206.       if (! edge_dialog (drawable))
  207.     return;
  208.       break;
  209.  
  210.     case GIMP_RUN_NONINTERACTIVE:
  211.       /*  Make sure all the arguments are there!  */
  212.       if (nparams != 5)
  213.     status = GIMP_PDB_CALLING_ERROR;
  214.       if (status == GIMP_PDB_SUCCESS)
  215.     {
  216.       evals.amount   = param[3].data.d_float;
  217.       evals.wrapmode = param[4].data.d_int32;
  218.     }
  219.       INIT_I18N();
  220.       break;
  221.  
  222.     case GIMP_RUN_WITH_LAST_VALS:
  223.       /*  Possibly retrieve data  */
  224.       gimp_get_data ("plug_in_edge", &evals);
  225.       INIT_I18N();
  226.       break;
  227.  
  228.     default:
  229.       break;
  230.     }
  231.  
  232.   /* make sure the drawable exist and is not indexed */
  233.   if (gimp_drawable_is_rgb (drawable->id) ||
  234.       gimp_drawable_is_gray (drawable->id))
  235.     {
  236.       gimp_progress_init (_("Edge Detection..."));
  237.  
  238.       /*  set the tile cache size  */
  239.       gimp_tile_cache_ntiles (TILE_CACHE_SIZE);
  240.  
  241.       /*  run the edge effect  */
  242.       edge (drawable);
  243.  
  244.       if (run_mode != GIMP_RUN_NONINTERACTIVE)
  245.     gimp_displays_flush ();
  246.  
  247.       /*  Store data  */
  248.       if (run_mode == GIMP_RUN_INTERACTIVE)
  249.     gimp_set_data ("plug_in_edge", &evals, sizeof (EdgeVals));
  250.     }
  251.   else
  252.      {
  253.       /* gimp_message ("edge: cannot operate on indexed color images"); */
  254.       status = GIMP_PDB_EXECUTION_ERROR;
  255.     }
  256.  
  257.   values[0].data.d_status = status;
  258.  
  259.   gimp_drawable_detach (drawable);
  260. }
  261.  
  262. /*********************************************************************
  263.  
  264.    TileBuf Util Routines:   Util routines for getting arbitrary pixel
  265.    CAUTION -- the tile is read only !!
  266.  
  267.  **********************************************************************/
  268.  
  269. static void
  270. init_tile_buf (TileBuf   *buf,
  271.            GimpDrawable *drawable)
  272. {
  273.   buf->tile = NULL;
  274.   buf->col = 0;
  275.   buf->row = 0;
  276.   if (gimp_drawable_is_rgb (drawable->id))
  277.     buf->bpp = 3;
  278.   else
  279.     buf->bpp = 1;
  280.   buf->tile_width = gimp_tile_width();
  281.   buf->tile_height = gimp_tile_height();
  282.   buf->drawable = drawable;
  283.   buf->drawable_width = gimp_drawable_width(drawable->id);
  284.   buf->drawable_height = gimp_drawable_height(drawable->id);
  285. }
  286.  
  287. static void
  288. get_tile_pixel (TileBuf *buf,
  289.         gint     x,
  290.         gint     y,
  291.         guchar  *pixel,
  292.         gint     wrapmode)
  293. {
  294.   gint b;
  295.   gint offx, offy;
  296.   gint row, col;
  297.   guchar *ptr;
  298.  
  299.   if (x < 0 || x >= buf->drawable_width ||
  300.       y < 0 || y >= buf->drawable_height)
  301.     switch (wrapmode)
  302.       {
  303.       case WRAP:
  304.     if (x < 0 || x >= buf->drawable_width)
  305.       {
  306.         x %= buf->drawable_width;
  307.         if (x < 0)
  308.           x += buf->drawable_width;
  309.       }
  310.     if (y < 0 || y >= buf->drawable_height)
  311.       {
  312.         y %= buf->drawable_height;
  313.         if (y < 0)
  314.           y += buf->drawable_height;
  315.       }
  316.     break;
  317.       case SMEAR:
  318.     if (x < 0)
  319.       x = 0;
  320.     if (x >= buf->drawable_width)
  321.       x = buf->drawable_width - 1;
  322.     if (y < 0)
  323.       y = 0;
  324.     if (y >= buf->drawable_height)
  325.       y = buf->drawable_height - 1;
  326.     break;
  327.       case BLACK:
  328.     if (x < 0 || x >= buf->drawable_width || 
  329.         y < 0 || y >= buf->drawable_height)
  330.       {
  331.         for (b = 0; b < buf->bpp; b++)
  332.           pixel[b] = 0;
  333.         return;
  334.       }
  335.     break;
  336.       default:
  337.     return;
  338.       }
  339.  
  340.   col = x / buf->tile_width;
  341.   offx = x % buf->tile_width;
  342.   row = y / buf->tile_height;
  343.   offy = y % buf->tile_height;
  344.  
  345.   /* retrieve tile */
  346.   if (!buf->tile || col != buf->col || row != buf->row)
  347.     {
  348.       if(buf->tile)
  349.     gimp_tile_unref (buf->tile, FALSE);
  350.       buf->col = col;
  351.       buf->row = row;
  352.       buf->tile = gimp_drawable_get_tile (buf->drawable, FALSE, row, col);
  353.       gimp_tile_ref (buf->tile);
  354.     }
  355.  
  356.   /* retrieve target pixel */
  357.   ptr = buf->tile->data + (offy * buf->tile->ewidth + offx) * buf->tile->bpp;
  358.   for(b = 0; b < buf->bpp; b++)
  359.     pixel[b] = ptr[b];
  360. }
  361.  
  362. static void
  363. end_tile_buf (TileBuf *buf)
  364. {
  365.   if (buf->tile)
  366.     gimp_tile_unref (buf->tile, FALSE);
  367. }
  368.  
  369. /**********************************************************************
  370.    TileBuf Util Routines End
  371.  **********************************************************************/
  372.  
  373. static long
  374. long_sqrt (long n)
  375. {
  376. #define lsqrt_max4pow (1UL << 30)
  377.   /* lsqrt_max4pow is the (machine-specific) largest power of 4 that can
  378.    * be represented in an unsigned long.
  379.    *
  380.    * Compute the integer square root of the integer argument n
  381.    * Method is to divide n by x computing the quotient x and remainder r
  382.    * Notice that the divisor x is changing as the quotient x changes
  383.    * 
  384.    * Instead of shifting the dividend/remainder left, we shift the
  385.    * quotient/divisor right. The binary point starts at the extreme
  386.    * left, and shifts two bits at a time to the extreme right.
  387.    * 
  388.    * The residue contains n-x^2. (Within these comments, the ^ operator
  389.    * signifies exponentiation rather than exclusive or. Also, the /
  390.    * operator returns fractions, rather than truncating, so 1/4 means
  391.    * one fourth, not zero.)
  392.    * 
  393.    * Since (x + 1/2)^2 == x^2 + x + 1/4,
  394.    * n - (x + 1/2)^2 == (n - x^2) - (x + 1/4)
  395.    * Thus, we can increase x by 1/2 if we decrease (n-x^2) by (x+1/4)
  396.    */
  397.  
  398.   gulong residue;        /* n - x^2  */
  399.   gulong root;           /* x + 1/4  */
  400.   gulong half;           /* 1/2      */
  401.  
  402.   residue = n;           /* n - (x = 0)^2, with suitable alignment */
  403.  
  404.   /*
  405.    * if the correct answer fits in two bits, pull it out of a magic hat
  406.    */
  407.   if (residue <= 12)
  408.     return (0x03FFEA94 >> (residue *= 2)) & 3;
  409.  
  410.   root = lsqrt_max4pow;         /* x + 1/4, shifted all the way left */
  411.   /* half = root + root; 1/2, shifted likewise */
  412.  
  413.   /* 
  414.    * Unwind iterations corresponding to leading zero bits 
  415.    */
  416.   while (root > residue)
  417.     root >>= 2;
  418.  
  419.   /*
  420.    * Unwind the iteration corresponding to the first one bit
  421.    * Operations have been rearranged and combined for efficiency
  422.    * Initialization of half is folded into this iteration
  423.    */
  424.   residue -= root;              /* Decrease (n-x^2) by (0+1/4)             */
  425.   half = root >> 2;             /* 1/4, with binary point shifted right 2  */
  426.   root += half;                 /* x=1. (root is now (x=1)+1/4.)           */
  427.   half += half;                 /* 1/2, properly aligned                   */
  428.  
  429.   /*
  430.    * Normal loop (there is at least one iteration remaining)
  431.    */
  432.   do
  433.     {
  434.       if (root <= residue)      /* Whenever we can,                          */
  435.         {
  436.           residue -= root;      /* decrease (n-x^2) by (x+1/4)               */
  437.           root += half;         /* increase x by 1/2                         */
  438.         }
  439.       half >>= 2;               /* Shift binary point 2 places right          */
  440.       root -= half;             /* x{ +1/2 } +1/4 - 1/8 == x { +1/2 } 1/8     */
  441.       root >>= 1;               /* 2x{ +1 } +1/4, shifted right 2 places      */
  442.     }
  443.   while (half);                 /* When 1/2 == 0, bin. point is at far right  */
  444.  
  445.   /* 
  446.    * round up if (x+1/2)^2 < n
  447.    */
  448.   if (root < residue)
  449.     ++root;
  450.  
  451.   /* 
  452.    * Guaranteed to be correctly rounded (or truncated)
  453.    */
  454.   return root;
  455. }
  456.  
  457. /********************************************************/
  458. /*              Edge Detection main                     */
  459. /********************************************************/
  460.  
  461. static void
  462. edge (GimpDrawable *drawable)
  463. {
  464.   /*
  465.    * this function is too long, so I must split this into a few
  466.    * functions later ...  -- taka
  467.    */
  468.   GimpPixelRgn src_rgn, dest_rgn;
  469.   gpointer pr;
  470.   TileBuf buf;
  471.   guchar *srcrow, *src;
  472.   guchar *destrow, *dest;
  473.   guchar pix00[3], pix01[3], pix02[3];
  474.   guchar pix10[3],/*pix11[3],*/ pix12[3];
  475.   guchar pix20[3], pix21[3], pix22[3];
  476.   glong width, height;
  477.   gint alpha, has_alpha, chan;
  478.   gint x, y;
  479.   gint x1, y1, x2, y2;
  480.   glong sum1, sum2;
  481.   glong sum, scale;
  482.   gint maxval;
  483.   gint cur_progress;
  484.   gint max_progress;
  485.   gint wrapmode;
  486.  
  487.   if (evals.amount < 1.0)
  488.     evals.amount = 1.0;
  489.  
  490.   init_tile_buf (&buf, drawable);
  491.  
  492.   gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2);
  493.  
  494.   width = gimp_drawable_width (drawable->id);
  495.   height = gimp_drawable_height (drawable->id);
  496.   alpha = gimp_drawable_bpp (drawable->id);
  497.   has_alpha = gimp_drawable_has_alpha (drawable->id);
  498.   if (has_alpha)
  499.     alpha--;
  500.  
  501.   maxval = 255;
  502.   scale = (10 << 16) / evals.amount;
  503.   wrapmode = evals.wrapmode;
  504.  
  505.   cur_progress = 0;
  506.   max_progress = (x2 - x1) * (y2 - y1);
  507.  
  508.   gimp_pixel_rgn_init (&src_rgn, drawable, x1, y1, x2-x1, y2-y1, FALSE, FALSE);
  509.   gimp_pixel_rgn_init (&dest_rgn, drawable, x1, y1, x2-x1, y2-y1, TRUE, TRUE);
  510.  
  511.   for (pr = gimp_pixel_rgns_register (2, &src_rgn, &dest_rgn); 
  512.        pr != NULL;
  513.        pr = gimp_pixel_rgns_process (pr))
  514.     {
  515.       srcrow = src_rgn.data;
  516.       destrow = dest_rgn.data;
  517.       for (y = dest_rgn.y;
  518.         y < (dest_rgn.y + dest_rgn.h);
  519.         y++, srcrow += src_rgn.rowstride, destrow += dest_rgn.rowstride)
  520.     {
  521.       src = srcrow;
  522.       dest = destrow;
  523.       for (x = dest_rgn.x;
  524.            x < (dest_rgn.x + dest_rgn.w);
  525.            x++,  src += src_rgn.bpp, dest += dest_rgn.bpp)
  526.         {
  527.           if(dest_rgn.x < x &&  x < dest_rgn.x + dest_rgn.w - 1 &&
  528.          dest_rgn.y < y &&  y < dest_rgn.y + dest_rgn.h - 1)
  529.         {
  530.           /*
  531.           ** 3x3 kernel is inside of the tile -- do fast
  532.           ** version
  533.           */
  534.           for (chan = 0; chan < alpha; chan++)
  535.             {
  536.               /*
  537.                * PIX(1,1) is the current pixel, so
  538.                * e.g. PIX(0,0) means 1 above and 1 left pixel.
  539.                *
  540.                * There were casting to `long' in GIMP 0.54
  541.                * edge code, but I think `guchar' should be
  542.                * extended to `int' with minus operators, so
  543.                * there's no need to cast to `long'. Both sum1
  544.                * and sum2 will be between -4*255 to 4*255
  545.                *
  546.                *    -- taka
  547.                */
  548. #define PIX(X,Y)  src[ (Y-1)*(int)src_rgn.rowstride + (X-1)*(int)src_rgn.bpp + chan ]
  549.               /* make convolution */
  550.               sum1 = (PIX(2,0) - PIX(0,0)) +
  551.              2 * (PIX(2,1) - PIX(0,1)) +
  552.                  (PIX(2,2) - PIX(2,0));
  553.               sum2 = (PIX(0,2) - PIX(0,0)) +
  554.              2 * (PIX(1,2) - PIX(1,0)) +
  555.                  (PIX(2,2) - PIX(2,0));
  556. #undef  PIX
  557.               /* common job ... */
  558.               sum = long_sqrt ((long) sum1 * sum1 + (long) sum2 * sum2);
  559.               sum = (sum * scale) >> 16;    /* arbitrary scaling factor */
  560.               if (sum > maxval)
  561.             sum = maxval;
  562.               dest[chan] = sum;
  563.             }
  564.         }
  565.           else
  566.         {
  567.           /*
  568.           ** The kernel is not inside of the tile -- do slow
  569.           ** version
  570.           */
  571.           /*
  572.            * When the kernel intersects the boundary of the
  573.            * image, get_tile_pixel() will (should) do the
  574.            * right work with `wrapmode'.
  575.            */
  576.           get_tile_pixel (&buf, x-1, y-1, pix00, wrapmode);
  577.           get_tile_pixel (&buf, x  , y-1, pix10, wrapmode);
  578.           get_tile_pixel (&buf, x+1, y-1, pix20, wrapmode);
  579.           get_tile_pixel (&buf, x-1, y  , pix01, wrapmode);
  580.           get_tile_pixel (&buf, x+1, y  , pix21, wrapmode);
  581.           get_tile_pixel (&buf, x-1, y+1, pix02, wrapmode);
  582.           get_tile_pixel (&buf, x  , y+1, pix12, wrapmode);
  583.           get_tile_pixel (&buf, x+1, y+1, pix22, wrapmode);
  584.  
  585.           for (chan = 0; chan < alpha; chan++)
  586.             {
  587.               /* make convolution */
  588.               sum1 = (pix20[chan] - pix00[chan]) +
  589.              2 * (pix21[chan] - pix01[chan]) +
  590.                  (pix22[chan] - pix20[chan]);
  591.               sum2 = (pix02[chan] - pix00[chan]) +
  592.              2 * (pix12[chan] - pix10[chan]) +
  593.                  (pix22[chan] - pix20[chan]);
  594.               /* common job ... */
  595.               sum = long_sqrt ((long) sum1 * sum1 + (long) sum2 * sum2);
  596.               sum = (sum * scale) >> 16;  /* arbitrary scaling factor */
  597.               if (sum > maxval) sum = maxval;
  598.               dest[chan] = sum;
  599.             }
  600.         }
  601.           if (has_alpha)
  602.         dest[alpha] = src[alpha];
  603.         }
  604.         }
  605.       cur_progress += dest_rgn.w * dest_rgn.h;
  606.       gimp_progress_update ((double) cur_progress / (double) max_progress);
  607.     }
  608.  
  609.   end_tile_buf (&buf);
  610.   gimp_drawable_flush (drawable);
  611.   gimp_drawable_merge_shadow (drawable->id, TRUE);
  612.   gimp_drawable_update (drawable->id, x1, y1, (x2 - x1), (y2 - y1));
  613. }
  614.  
  615. /*******************************************************/
  616. /*                    Dialog                           */
  617. /*******************************************************/
  618.  
  619. static void
  620. edge_ok_callback (GtkWidget *widget,
  621.           gpointer   data)
  622. {
  623.   eint.run = TRUE;
  624.  
  625.   gtk_widget_destroy (GTK_WIDGET (data));
  626. }
  627.  
  628. static gint
  629. edge_dialog (GimpDrawable *drawable)
  630. {
  631.   GtkWidget *dlg;
  632.   GtkWidget *frame;
  633.   GtkWidget *table;
  634.   GtkWidget *hbox;
  635.   GtkWidget *toggle;
  636.   GtkObject *scale_data;
  637.   GSList *group = NULL;
  638.  
  639.   gint    use_wrap  = (evals.wrapmode == WRAP);
  640.   gint    use_smear = (evals.wrapmode == SMEAR);
  641.   gint    use_black = (evals.wrapmode == BLACK);
  642.  
  643.   gimp_ui_init ("edge", FALSE);
  644.  
  645.   dlg = gimp_dialog_new (_("Edge Detection"), "edge",
  646.              gimp_standard_help_func, "filters/edge.html",
  647.              GTK_WIN_POS_MOUSE,
  648.              FALSE, TRUE, FALSE,
  649.  
  650.              _("OK"), edge_ok_callback,
  651.              NULL, NULL, NULL, TRUE, FALSE,
  652.              _("Cancel"), gtk_widget_destroy,
  653.              NULL, 1, NULL, FALSE, TRUE,
  654.  
  655.              NULL);
  656.  
  657.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  658.               GTK_SIGNAL_FUNC (gtk_main_quit),
  659.               NULL);
  660.  
  661.   /*  parameter settings  */
  662.   frame = gtk_frame_new (_("Parameter Settings"));
  663.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  664.   gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
  665.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
  666.  
  667.   table = gtk_table_new (2, 3, FALSE);
  668.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  669.   gtk_table_set_row_spacings (GTK_TABLE (table), 4);
  670.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  671.   gtk_container_add (GTK_CONTAINER (frame), table);
  672.  
  673.   /*  Label, scale, entry for evals.amount  */
  674.   scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
  675.                      _("Amount:"), 100, 0,
  676.                      evals.amount, 1.0, 10.0, 0.1, 1.0, 1,
  677.                      TRUE, 0, 0,
  678.                      NULL, NULL);
  679.  
  680.   gtk_signal_connect (GTK_OBJECT (scale_data), "value_changed",
  681.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  682.               &evals.amount);
  683.  
  684.   /*  Radio buttons WRAP, SMEAR, BLACK  */
  685.  
  686.   hbox = gtk_hbox_new (FALSE, 4);
  687.   gtk_table_attach (GTK_TABLE (table), hbox, 0, 3, 1, 2,
  688.             GTK_FILL, GTK_FILL, 0, 0);
  689.  
  690.   toggle = gtk_radio_button_new_with_label (group, _("Wrap"));
  691.   group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
  692.   gtk_box_pack_start (GTK_BOX (hbox), toggle, TRUE, TRUE, 0);
  693.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  694.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  695.               &use_wrap);
  696.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), use_wrap);
  697.   gtk_widget_show (toggle);
  698.  
  699.   toggle = gtk_radio_button_new_with_label (group, _("Smear"));
  700.   group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
  701.   gtk_box_pack_start (GTK_BOX (hbox), toggle, TRUE, TRUE, 0);
  702.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  703.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  704.               &use_smear);
  705.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), use_smear);
  706.   gtk_widget_show (toggle);
  707.  
  708.   toggle = gtk_radio_button_new_with_label (group, _("Black"));
  709.   group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
  710.   gtk_box_pack_start (GTK_BOX (hbox), toggle, TRUE, TRUE, 0);
  711.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  712.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  713.               &use_black);
  714.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), use_black);
  715.   gtk_widget_show (toggle);
  716.   gtk_widget_show (hbox);
  717.  
  718.   gtk_widget_show (table);
  719.   gtk_widget_show (frame);
  720.   gtk_widget_show (dlg);
  721.  
  722.   gtk_main ();
  723.   gdk_flush ();
  724.  
  725.   if (use_wrap)
  726.     evals.wrapmode = WRAP;
  727.   else if (use_smear)
  728.     evals.wrapmode = SMEAR;
  729.   else if (use_black)
  730.     evals.wrapmode = BLACK;
  731.  
  732.   return eint.run;
  733. }
  734.