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 / convmatrix.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-12-16  |  25.5 KB  |  1,043 lines

  1. /* Convolution Matrix plug-in for the GIMP -- Version 0.1
  2.  * Copyright (C) 1997 Lauri Alanko <la@iki.fi>
  3.  *
  4.  *
  5.  * This program is free software; you can redistribute it and/or modify
  6.  * it under the terms of the GNU General Public License as published by
  7.  * the Free Software Foundation; either version 2 of the License, or
  8.  * (at your option) any later version.
  9.  *
  10.  * This program is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program; if not, write to the Free Software
  17.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  18.  *
  19.  * The GNU General Public License is also available from
  20.  * http://www.fsf.org/copyleft/gpl.html
  21.  * 
  22.  *
  23.  * CHANGELOG:
  24.  * v0.13    15.12.2000
  25.  *     Made the PDB interface actually work.   (Simon Budig <simon@gimp.org>)
  26.  *
  27.  * v0.12    15.9.1997
  28.  *    Got rid of the unportable snprintf. Also made some _tiny_ GUI fixes.
  29.  *
  30.  * v0.11    20.7.1997
  31.  *    Negative values in the matrix are now abs'ed when used to weight
  32.  *      alpha. Embossing effects should work properly now. Also fixed a
  33.  *      totally idiotic bug with embossing.
  34.  *
  35.  * v0.1     2.7.1997
  36.  *    Initial release. Works... kinda.
  37.  * 
  38.  * 
  39.  * TODO:
  40.  * 
  41.  * - remove channels selector (that's what the channels dialog is for)
  42.  * - remove idiotic slowdowns
  43.  * - clean up code
  44.  * - preview
  45.  * - optimize properly
  46.  * - save & load matrices
  47.  * - spiffy frontend for designing matrices
  48.  * 
  49.  * What else?
  50.  * 
  51.  * 
  52.  */
  53.  
  54. #include "config.h"
  55.  
  56. #include <stdlib.h>
  57. #include <stdio.h>
  58. #include <time.h>
  59. #include <string.h>
  60. #include <sys/types.h>
  61. #ifdef HAVE_UNISTD_H
  62. #include <unistd.h>
  63. #endif
  64.  
  65. #include <gtk/gtk.h>
  66.  
  67. #include <libgimp/gimp.h>
  68. #include <libgimp/gimpui.h>
  69.  
  70. #include "libgimp/stdplugins-intl.h"
  71.  
  72.  
  73. typedef enum
  74. {
  75.   EXTEND,
  76.   WRAP,
  77.   CLEAR,
  78.   MIRROR
  79. } BorderMode;
  80.  
  81. GimpDrawable *drawable;
  82.  
  83. gchar * const channel_labels[] =
  84. {
  85.   N_("Grey"),
  86.   N_("Red"),
  87.   N_("Green"),
  88.   N_("Blue"),
  89.   N_("Alpha")
  90. };
  91.  
  92. gchar * const bmode_labels[] =
  93. {
  94.   N_("Extend"),
  95.   N_("Wrap"),
  96.   N_("Crop")
  97. };
  98.  
  99. /* Declare local functions. */
  100. static void query (void);
  101. static void run   (gchar      *name,
  102.            gint        nparams,
  103.            GimpParam  *param,
  104.            gint       *nreturn_vals,
  105.            GimpParam **return_vals);
  106.  
  107. static gint dialog (void);
  108.  
  109. static void doit         (void);
  110. static void check_config (void);
  111.  
  112. GimpPlugInInfo PLUG_IN_INFO =
  113. {
  114.   NULL,   /* init_proc  */
  115.   NULL,   /* quit_proc  */
  116.   query,  /* query_proc */
  117.   run,    /* run_proc   */
  118. };
  119.  
  120. gint bytes;
  121. gint sx1, sy1, sx2, sy2;
  122. gint run_flag = 0;
  123.  
  124. typedef struct
  125. {
  126.   gfloat     matrix[5][5];
  127.   gfloat     divisor;
  128.   gfloat     offset;
  129.   gint       alpha_alg;
  130.   BorderMode bmode;
  131.   gint       channels[5];
  132.   gint       autoset;
  133. } config;
  134.  
  135. const config default_config =
  136. {
  137.   {
  138.     { 0.0, 0.0, 0.0, 0.0, 0.0 },
  139.     { 0.0, 0.0, 0.0, 0.0, 0.0 },
  140.     { 0.0, 0.0, 1.0, 0.0, 0.0 },
  141.     { 0.0, 0.0, 0.0, 0.0, 0.0 },
  142.     { 0.0, 0.0, 0.0, 0.0, 0.0 }
  143.   },                 /* matrix */
  144.   1,                 /* divisor */
  145.   0,                 /* offset */
  146.   1,                 /* Alpha-handling algorithm */     
  147.   CLEAR,             /* border-mode */
  148.   { 1, 1, 1, 1, 1 }, /* Channels mask */
  149.   0                  /* autoset */
  150. };
  151.  
  152. config my_config;
  153.  
  154. struct
  155. {
  156.   GtkWidget *matrix[5][5];
  157.   GtkWidget *divisor;
  158.   GtkWidget *offset;
  159.   GtkWidget *alpha_alg;
  160.   GtkWidget *bmode[3];
  161.   GtkWidget *channels[5];
  162.   GtkWidget *autoset;
  163.   GtkWidget *ok;
  164. } my_widgets;
  165.  
  166.  
  167. MAIN ()
  168.  
  169. static void
  170. query (void)
  171. {
  172.   static GimpParamDef args[] =
  173.   {
  174.     { GIMP_PDB_INT32,    "run_mode", "Interactive, non-interactive" },
  175.     { GIMP_PDB_IMAGE,    "image",    "Input image (unused)" },
  176.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  177.     { GIMP_PDB_INT32, "argc_matrix", "The number of elements in the following array. Should be always 25." },
  178.     { GIMP_PDB_FLOATARRAY, "matrix", "The 5x5 convolution matrix" },
  179.     { GIMP_PDB_INT32, "alpha_alg", "Enable weighting by alpha channel" },
  180.     { GIMP_PDB_FLOAT, "divisor", "Divisor" },
  181.     { GIMP_PDB_FLOAT, "offset", "Offset" },
  182.  
  183.     { GIMP_PDB_INT32, "argc_channels", "The number of elements in following array. Should be always 5." },
  184.     { GIMP_PDB_INT32ARRAY, "channels", "Mask of the channels to be filtered" },
  185.     { GIMP_PDB_INT32, "bmode", "Mode for treating image borders" }
  186.   };
  187.   static gint nargs = sizeof (args) / sizeof (args[0]);
  188.  
  189.   gimp_install_procedure ("plug_in_convmatrix",
  190.               "A generic 5x5 convolution matrix",
  191.               "",
  192.               "Lauri Alanko",
  193.               "Lauri Alanko",
  194.               "1997",
  195.               N_("<Image>/Filters/Generic/Convolution Matrix..."),
  196.               "RGB*, GRAY*",
  197.               GIMP_PLUGIN,
  198.               nargs, 0,
  199.               args, NULL);
  200. }
  201.  
  202. static void
  203. run (gchar      *name,
  204.      gint        nparams,
  205.      GimpParam  *param,
  206.      gint       *nreturn_vals,
  207.      GimpParam **return_vals)
  208. {
  209.   static GimpParam  values[1];
  210.   GimpRunModeType   run_mode;
  211.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  212.   gint              x, y;
  213.  
  214.   INIT_I18N_UI();
  215.  
  216.   (void) name; /* Shut up warnings about unused parameters. */
  217.  
  218.   *nreturn_vals = 1;
  219.   *return_vals = values;
  220.  
  221.   run_mode = param[0].data.d_int32;
  222.     
  223.   /*  Get the specified drawable  */
  224.   drawable = gimp_drawable_get (param[2].data.d_drawable);
  225.  
  226.   /*  The plug-in is not able to handle images smaller than 3 pixels  */
  227.   if (drawable->width < 3 || drawable->height < 3)
  228.     {
  229.       g_message (_("Convolution Matrix does not work\non layers smaller than 3 pixels."));
  230.       status = GIMP_PDB_EXECUTION_ERROR;
  231.       values[0].type = GIMP_PDB_STATUS;
  232.       values[0].data.d_status = status;
  233.       return;
  234.     }
  235.  
  236.   my_config = default_config;
  237.   if (run_mode == GIMP_RUN_NONINTERACTIVE)
  238.     {
  239.       if (nparams != 11)
  240.     status = GIMP_PDB_CALLING_ERROR;
  241.       else
  242.     {
  243.       if (param[3].data.d_int32 != 25) {
  244.         status = GIMP_PDB_CALLING_ERROR;
  245.       } else {
  246.         for (y = 0; y < 5; y++)
  247.           for (x = 0; x < 5; x++)
  248.         my_config.matrix[x][y]=param[4].data.d_floatarray[y*5+x];
  249.       }
  250.  
  251.       my_config.alpha_alg = param[5].data.d_int32;
  252.       my_config.divisor   = param[6].data.d_float;
  253.       my_config.offset    = param[7].data.d_float;
  254.  
  255.  
  256.       if (param[8].data.d_int32 != 5) {
  257.         status = GIMP_PDB_CALLING_ERROR;
  258.       } else {
  259.         for (y = 0; y < 5; y++)
  260.           my_config.channels[y] = param[9].data.d_int32array[y];
  261.       }
  262.  
  263.       my_config.bmode     = param[10].data.d_int32;
  264.  
  265.       check_config ();
  266.     }
  267.     }
  268.   else
  269.     {
  270.       gimp_get_data ("plug_in_convmatrix", &my_config);
  271.  
  272.       if (run_mode == GIMP_RUN_INTERACTIVE)
  273.     {
  274.       /*  Oh boy. We get to do a dialog box, because we can't really
  275.        *  expect the user to set us up with the right values using gdb.
  276.        */
  277.       check_config ();
  278.       if (!dialog())
  279.         {
  280.           /* The dialog was closed, or something similarly evil happened. */
  281.           status = GIMP_PDB_EXECUTION_ERROR;
  282.         }
  283.     }
  284.     }
  285.  
  286.   if (status == GIMP_PDB_SUCCESS)
  287.     {
  288.       /*  Make sure that the drawable is gray or RGB color  */
  289.       if (gimp_drawable_is_rgb(drawable->id) ||
  290.       gimp_drawable_is_gray(drawable->id))
  291.     {
  292.       gimp_progress_init (_("Applying convolution"));
  293.       gimp_tile_cache_ntiles (2 * (drawable->width /
  294.                        gimp_tile_width () + 1));
  295.  
  296.       doit ();
  297.  
  298.       if (run_mode != GIMP_RUN_NONINTERACTIVE)
  299.         gimp_displays_flush ();
  300.  
  301.       if (run_mode == GIMP_RUN_INTERACTIVE)
  302.         gimp_set_data ("plug_in_convmatrix", &my_config, sizeof (my_config));
  303.     }
  304.       else
  305.     {
  306.       status = GIMP_PDB_EXECUTION_ERROR;
  307.     }
  308.       gimp_drawable_detach (drawable);
  309.     }
  310.  
  311.   values[0].type = GIMP_PDB_STATUS;
  312.   values[0].data.d_status = status;
  313. }
  314.  
  315.  
  316. /*  A generic wrapper to gimp_pixel_rgn_get_row which handles unlimited
  317.  *  wrapping or gives you transparent regions outside the image
  318.  */
  319.  
  320. static void
  321. my_get_row (GimpPixelRgn *PR,
  322.         guchar       *dest,
  323.         gint          x,
  324.         gint          y,
  325.         gint          w)
  326. {
  327.   gint width, height, bytes;
  328.   gint i;
  329.  
  330.   width  = PR->drawable->width;
  331.   height = PR->drawable->height;
  332.   bytes  = PR->drawable->bpp;
  333.  
  334.   /* Y-wrappings */
  335.  
  336.   switch (my_config.bmode)
  337.     {
  338.     case WRAP:
  339.       /* Wrapped, so we get the proper row from the other side */
  340.       while (y < 0) /* This is the _sure_ way to wrap. :) */
  341.     y += height;
  342.       while (y >= height)
  343.     y -= height;
  344.       break;
  345.  
  346.     case CLEAR:
  347.       /* Beyond borders, so set full transparent. */
  348.       if (y < 0 || y >= height)
  349.     {
  350.       memset (dest, 0, w * bytes);
  351.       return; /* Done, so back. */
  352.     }
  353.     case MIRROR:
  354.       /* The border lines are _not_ duplicated in the mirror image */
  355.       /* is this right? */
  356.       while (y < 0 || y >= height)
  357.     {
  358.       if (y < 0)
  359.         y = -y; /* y=-y-1 */
  360.       if (y >= height)
  361.         y = 2 * height - y - 2; /* y=2*height-y-1 */  
  362.     }
  363.       break;
  364.  
  365.     case EXTEND:
  366.       y = CLAMP (y , 0 , height - 1);
  367.       break;
  368.     }
  369.  
  370.   switch (my_config.bmode)
  371.     {
  372.     case CLEAR:
  373.       if (x < 0)
  374.     {
  375.       i = MIN (w, -x);
  376.       memset (dest, 0, i * bytes);
  377.       dest += i * bytes;
  378.       w -= i;
  379.       x += i;
  380.     }
  381.       if (w)
  382.     {
  383.       i = MIN (w, width);
  384.       gimp_pixel_rgn_get_row (PR, dest, x, y, i);
  385.       dest += i * bytes;
  386.       w -= i;
  387.       x += i;
  388.     }
  389.       if (w)
  390.     memset (dest, 0, w * bytes);
  391.       break;
  392.  
  393.     case WRAP:
  394.       while (x < 0)
  395.     x += width;
  396.       i = MIN (w, width - x);
  397.       gimp_pixel_rgn_get_row (PR, dest, x, y, i);
  398.       w -= i;
  399.       dest += i * bytes;
  400.       x = 0;
  401.       while (w)
  402.     {
  403.       i = MIN (w, width);
  404.       gimp_pixel_rgn_get_row (PR, dest, x, y, i);
  405.       w -= i;
  406.       dest += i * bytes;
  407.     }
  408.       break;
  409.  
  410.     case EXTEND:
  411.       if (x < 0)
  412.     {
  413.       gimp_pixel_rgn_get_pixel (PR, dest, 0, y);
  414.       x++;
  415.       w--;
  416.       dest += bytes;
  417.  
  418.       while (x < 0 && w)
  419.         {
  420.           for (i = 0; i < bytes; i++)
  421.         {
  422.           *dest = *(dest - bytes);
  423.           dest++;
  424.         }
  425.           x++;
  426.           w--;
  427.         }
  428.     }
  429.       if (w)
  430.     {
  431.       i = MIN (w, width);
  432.       gimp_pixel_rgn_get_row (PR, dest, x, y, i);
  433.       w -= i;
  434.       dest += i * bytes;
  435.     }
  436.       while (w)
  437.     {
  438.       for (i = 0; i < bytes; i++)
  439.         {
  440.           *dest= *(dest - bytes);
  441.           dest++;
  442.         }
  443.       x++;
  444.       w--;
  445.     }
  446.       break;
  447.  
  448.     case MIRROR: /* Not yet handled */
  449.       break;
  450.     }
  451. }
  452.  
  453. static gfloat
  454. calcmatrix (guchar **srcrow,
  455.         gint     xoff,
  456.         gint     i)
  457. {
  458.   static gfloat matrixsum = 0;
  459.   static gint bytes       = 0;
  460.  
  461.   gfloat sum      = 0;
  462.   gfloat alphasum = 0;
  463.   gfloat temp;
  464.   gint   x, y;
  465.  
  466.   if (!bytes)
  467.     {
  468.       bytes = drawable->bpp;
  469.       for (y = 0; y < 5; y++)
  470.     for (x = 0; x < 5; x++)
  471.       {
  472.         temp = my_config.matrix[x][y];
  473.         matrixsum += ABS (temp);
  474.       }
  475.     }
  476.   for (y = 0; y < 5; y++)
  477.     for (x = 0; x < 5; x++)
  478.       {
  479.     temp = my_config.matrix[x][y];
  480.     if (i != (bytes - 1) && my_config.alpha_alg == 1)
  481.       {
  482.         temp *= srcrow[y][xoff + x * bytes +bytes - 1 - i];
  483.         alphasum += ABS (temp);
  484.       }
  485.     temp *= srcrow[y][xoff + x * bytes];
  486.     sum += temp;
  487.       }
  488.   sum /= my_config.divisor;
  489.   if (i != (bytes - 1) && my_config.alpha_alg == 1)
  490.     {
  491.       if (alphasum != 0)
  492.     sum = sum * matrixsum / alphasum;
  493.       else
  494.     sum = 0;
  495.       /* sum = srcrow[2][xoff + 2 * bytes] * my_config.matrix[2][2];*/
  496.     }
  497.   sum += my_config.offset;
  498.  
  499.   return sum;
  500. }
  501. static void
  502. doit (void)
  503. {
  504.   GimpPixelRgn  srcPR, destPR;
  505.   gint          width, height, row, col;
  506.   gint          w, h, i;
  507.   guchar       *destrow[3];
  508.   guchar       *srcrow[5];
  509.   guchar       *temprow;
  510.   gfloat        sum;
  511.   gint          xoff;
  512.   gint          chanmask[4];
  513.  
  514.   /* Get the input area. This is the bounding box of the selection in
  515.    *  the image (or the entire image if there is no selection). Only
  516.    *  operating on the input area is simply an optimization. It doesn't
  517.    *  need to be done for correct operation. (It simply makes it go
  518.    *  faster, since fewer pixels need to be operated on).
  519.    */
  520.   gimp_drawable_mask_bounds (drawable->id, &sx1, &sy1, &sx2, &sy2);
  521.   w = sx2 - sx1;
  522.   h = sy2 - sy1;
  523.  
  524.   /* Get the size of the input image. (This will/must be the same
  525.    *  as the size of the output image.
  526.    */
  527.   width  = drawable->width;
  528.   height = drawable->height;
  529.   bytes  = drawable->bpp;
  530.  
  531.   if (gimp_drawable_is_rgb (drawable->id))
  532.     for (i = 0; i <3; i++)
  533.       chanmask[i] = my_config.channels[i + 1];
  534.   else /* Grayscale */
  535.     chanmask[0] = my_config.channels[0];
  536.  
  537.   if (gimp_drawable_has_alpha (drawable->id))
  538.     chanmask[bytes - 1] = my_config.channels[4];
  539.  
  540.   for (i = 0; i < 5; i++)
  541.     srcrow[i] = g_new (guchar, (w + 4) * bytes);
  542.   for (i = 0; i < 3; i++)
  543.     destrow[i]= g_new (guchar, w * bytes);
  544.  
  545.   /*  initialize the pixel regions  */
  546.   gimp_pixel_rgn_init (&srcPR, drawable,
  547.                sx1 - 2, sy1 - 2, w + 4, h + 4, FALSE, FALSE);
  548.   gimp_pixel_rgn_init (&destPR, drawable, sx1, sy1, w, h, TRUE, TRUE);
  549.  
  550.   /* initialize source arrays */
  551.   for (i = 0; i < 5; i++)
  552.     my_get_row (&srcPR, srcrow[i], sx1 - 2, sy1 + i - 2, w + 4);
  553.  
  554.   for (row = sy1; row < sy2; row++)
  555.     {
  556.       xoff = 0;
  557.  
  558.       for (col = sx1; col < sx2; col++)
  559.     for (i = 0; i < bytes; i++)
  560.       {
  561.         if (chanmask[i] <= 0)
  562.           sum = srcrow[2][xoff + 2 * bytes];
  563.         else
  564.           sum = calcmatrix(srcrow, xoff, i);
  565.  
  566.         destrow[2][xoff]= (guchar) CLAMP (sum, 0, 255);
  567.         xoff++;
  568.       }
  569.  
  570.       if (row > sy1 + 1)
  571.     gimp_pixel_rgn_set_row (&destPR, destrow[0], sx1, row - 2, w);
  572.  
  573.       temprow = destrow[0];
  574.       destrow[0] = destrow[1];
  575.       destrow[1] = destrow[2];
  576.       destrow[2] = temprow;
  577.       temprow = srcrow[0];
  578.  
  579.       for (i = 0; i < 4; i++)
  580.     srcrow[i] = srcrow[i + 1];
  581.  
  582.       srcrow[4] = temprow;
  583.       my_get_row (&srcPR, srcrow[4], sx1 - 2, row + 3, w + 4);
  584.  
  585.       gimp_progress_update ((double) (row - sy1) / h);
  586.     }
  587.  
  588.   /* put the final rows in the buffer in place */
  589.   if (h < 3)
  590.     gimp_pixel_rgn_set_row (&destPR, destrow[2], sx1, row - 3, w);
  591.   if (h > 1)
  592.     gimp_pixel_rgn_set_row (&destPR, destrow[0], sx1, row - 2, w);
  593.   if (h > 2)
  594.     gimp_pixel_rgn_set_row (&destPR, destrow[1], sx1, row - 1, w);
  595.  
  596.   /*  update the timred region  */
  597.   gimp_drawable_flush (drawable);
  598.   gimp_drawable_merge_shadow (drawable->id, TRUE);
  599.   gimp_drawable_update (drawable->id, sx1, sy1, sx2 - sx1, sy2 - sy1);
  600. }
  601.  
  602. /***************************************************
  603.  * GUI stuff
  604.  */
  605.  
  606. static void
  607. fprint (gfloat  f,
  608.     gchar  *buffer)
  609. {
  610.   gint i, t;
  611.  
  612.   sprintf (buffer, "%.7f", f);
  613.  
  614.   for (t = 0; buffer[t] != '.'; t++);
  615.  
  616.   i = t + 1;
  617.  
  618.   while (buffer[i] != '\0')
  619.     {
  620.       if (buffer[i] != '0')
  621.     t = i + 1;
  622.  
  623.       i++;
  624.     }
  625.  
  626.   buffer[t] = '\0';
  627. }
  628.  
  629. static void
  630. redraw_matrix (void)
  631. {
  632.   gint x,y;
  633.   gchar buffer[12];
  634.  
  635.   for (y = 0; y < 5; y++)
  636.     for (x = 0; x < 5; x++)
  637.       {
  638.     fprint (my_config.matrix[x][y], buffer);
  639.  
  640.     gtk_entry_set_text (GTK_ENTRY (my_widgets.matrix[x][y]), buffer);
  641.       }
  642. }
  643.  
  644. static void
  645. redraw_channels (void)
  646. {
  647.   gint i;
  648.  
  649.   for (i = 0; i < 5; i++)
  650.     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (my_widgets.channels[i]),
  651.                   my_config.channels[i] > 0);
  652. }
  653.  
  654. static void
  655. redraw_autoset (void)
  656. {
  657.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (my_widgets.autoset),
  658.                 my_config.autoset);
  659. }
  660.  
  661. static void
  662. redraw_alpha_alg (void)
  663. {
  664.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (my_widgets.alpha_alg),
  665.                 my_config.alpha_alg > 0);
  666. }
  667.  
  668. static void
  669. redraw_off_and_div (void)
  670. {
  671.   gchar buffer[12];
  672.  
  673.   fprint (my_config.divisor, buffer);
  674.   gtk_entry_set_text (GTK_ENTRY (my_widgets.divisor), buffer);
  675.   fprint (my_config.offset, buffer);
  676.   gtk_entry_set_text (GTK_ENTRY (my_widgets.offset), buffer);
  677. }
  678.  
  679. static void
  680. redraw_bmode (void)
  681. {
  682.   gtk_toggle_button_set_active
  683.     (GTK_TOGGLE_BUTTON (my_widgets.bmode[my_config.bmode]), TRUE);
  684. }
  685.  
  686. static void
  687. redraw_all (void)
  688. {
  689.   redraw_matrix ();
  690.   redraw_off_and_div ();
  691.   redraw_autoset ();
  692.   redraw_alpha_alg ();
  693.   redraw_bmode ();
  694.   redraw_channels ();
  695. }
  696.  
  697. static void
  698. check_matrix (void)
  699. {
  700.   gint      x, y;
  701.   gboolean  valid = FALSE;
  702.   gfloat    sum   = 0.0;
  703.  
  704.   for (y = 0; y < 5; y++)
  705.     for (x = 0; x < 5; x++)
  706.       {
  707.     sum += my_config.matrix[x][y];
  708.     if (my_config.matrix[x][y] != 0.0)
  709.       valid = TRUE;
  710.       }
  711.  
  712.   if (my_config.autoset)
  713.     {
  714.       if (sum > 0)
  715.     {
  716.       my_config.offset = 0;
  717.       my_config.divisor = sum;
  718.     }
  719.       else if (sum < 0)
  720.     {
  721.       my_config.offset = 255;
  722.       my_config.divisor = -sum;
  723.     }
  724.       else
  725.     {
  726.       my_config.offset = 128;
  727.       /* The sum is 0, so this is probably some sort of
  728.        * embossing filter. Should divisor be autoset to 1
  729.        * or left undefined, ie. for the user to define? */
  730.       my_config.divisor = 1;
  731.     }
  732.       redraw_off_and_div ();
  733.     }
  734.   /* gtk_widget_set_sensitive(my_widgets.ok,valid); */
  735. }
  736.  
  737. static void
  738. ok_callback (GtkWidget *widget,
  739.          gpointer   data)
  740. {
  741.   run_flag = TRUE;
  742.  
  743.   gtk_widget_destroy (GTK_WIDGET (data));
  744. }
  745.  
  746. /* Checks that the configuration is valid for the image type */
  747. static void
  748. check_config (void)
  749. {
  750.   gint i;
  751.  
  752.   for (i = 0; i < 5; i++)
  753.     if (my_config.channels[i] < 0)
  754.       my_config.channels[i] = 0;
  755.  
  756.   if (gimp_drawable_is_rgb (drawable->id))
  757.     my_config.channels[0] = -1;
  758.   else if (gimp_drawable_is_gray (drawable->id))
  759.     for (i = 1; i < 4; i++)
  760.       my_config.channels[i] = -1;
  761.  
  762.   if (!gimp_drawable_has_alpha (drawable->id))
  763.     {
  764.       my_config.channels[4] = -1;
  765.       my_config.alpha_alg   = -1;
  766.       my_config.bmode       = EXTEND;
  767.     }
  768. }
  769.  
  770. static void
  771. defaults_callback (GtkWidget *widget,
  772.            gpointer   data)
  773. {
  774.   my_config = default_config;
  775.   check_config ();
  776.   redraw_all ();
  777. }
  778.  
  779. static void
  780. entry_callback (GtkWidget *widget,
  781.         gpointer   data)
  782. {
  783.   gfloat *value = (gfloat *) data;
  784.  
  785.   *value = atof (gtk_entry_get_text (GTK_ENTRY (widget)));
  786.  
  787. #if 0
  788.   check_matrix ();
  789. #else
  790.   if (widget == my_widgets.divisor)
  791.     gtk_widget_set_sensitive (GTK_WIDGET (my_widgets.ok), (*value != 0.0));
  792.   else if (widget != my_widgets.offset)
  793.     check_matrix ();
  794. #endif    
  795. }
  796.  
  797. static void
  798. my_toggle_callback (GtkWidget *widget,
  799.             gpointer   data)
  800. {
  801.   gint val = GTK_TOGGLE_BUTTON (widget)->active;
  802.  
  803.   if (val)
  804.     *(gint *) data = TRUE;
  805.   else
  806.     *(gint *) data = FALSE;
  807.  
  808.   if (widget == my_widgets.alpha_alg)
  809.     {
  810.       gtk_widget_set_sensitive (my_widgets.bmode[CLEAR], val);
  811.       if (val == 0 && my_config.bmode == CLEAR)
  812.     {
  813.       my_config.bmode = EXTEND;
  814.       redraw_bmode ();
  815.     }
  816.     }
  817.   else if (widget == my_widgets.autoset)
  818.     {
  819.       gtk_widget_set_sensitive (my_widgets.divisor, !val);
  820.       gtk_widget_set_sensitive (my_widgets.offset, !val);
  821.       check_matrix ();
  822.     }
  823. }
  824.  
  825. static void
  826. my_bmode_callback (GtkWidget *widget,
  827.            gpointer   data)
  828. {
  829.   my_config.bmode = (int) data - 1;
  830. }
  831.  
  832. static gint
  833. dialog (void)
  834. {
  835.   GtkWidget *dlg;
  836.   GtkWidget *main_hbox;
  837.   GtkWidget *table;
  838.   GtkWidget *label;
  839.   GtkWidget *entry;
  840.   GtkWidget *button;
  841.   GtkWidget *box;
  842.   GtkWidget *inbox;
  843.   GtkWidget *yetanotherbox;
  844.   GtkWidget *frame;
  845.   gchar      buffer[32];
  846.   gint       x, y, i;
  847.   GSList    *group;
  848.  
  849.   gimp_ui_init ("convmatrix", FALSE);
  850.  
  851.   dlg = gimp_dialog_new (_("Convolution Matrix"), "convmatrix",
  852.              gimp_standard_help_func, "filters/convmatrix.html",
  853.              GTK_WIN_POS_MOUSE,
  854.              FALSE, TRUE, FALSE,
  855.  
  856.              _("OK"), ok_callback,
  857.              NULL, NULL, &my_widgets.ok, TRUE, FALSE,
  858.              _("Reset"), defaults_callback,
  859.              NULL, 1, NULL, FALSE, FALSE,
  860.              _("Cancel"), gtk_widget_destroy,
  861.              NULL, 1, NULL, FALSE, TRUE,
  862.  
  863.              NULL);
  864.  
  865.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  866.               GTK_SIGNAL_FUNC (gtk_main_quit),
  867.               NULL);
  868.  
  869.   main_hbox = gtk_hbox_new (FALSE, 4);
  870.   gtk_container_set_border_width (GTK_CONTAINER (main_hbox), 6);
  871.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), main_hbox,
  872.               TRUE, TRUE, 0);
  873.  
  874.   yetanotherbox = gtk_vbox_new (FALSE, 4);
  875.   gtk_box_pack_start (GTK_BOX (main_hbox), yetanotherbox, FALSE, FALSE, 0);
  876.  
  877.   frame = gtk_frame_new (_("Matrix"));
  878.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  879.   gtk_box_pack_start (GTK_BOX (yetanotherbox), frame, FALSE, FALSE, 0);
  880.  
  881.   inbox = gtk_vbox_new (FALSE, 6);
  882.   gtk_container_set_border_width (GTK_CONTAINER (inbox), 4);
  883.   gtk_container_add (GTK_CONTAINER (frame), inbox);
  884.  
  885.   table = gtk_table_new (5, 5, FALSE);
  886.   gtk_table_set_row_spacings (GTK_TABLE (table), 4);
  887.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  888.   gtk_box_pack_start (GTK_BOX (inbox), table, FALSE, FALSE, 0);
  889.  
  890.   for (y = 0; y < 5; y++)
  891.     for (x = 0; x < 5; x++)
  892.       {
  893.     my_widgets.matrix[x][y] = entry = gtk_entry_new ();
  894.     gtk_widget_set_usize (entry, 40, 0);
  895.     gtk_table_attach (GTK_TABLE (table), entry, x, x+1, y, y+1,
  896.               GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
  897.     gtk_entry_set_text (GTK_ENTRY (entry), buffer);
  898.     gtk_signal_connect (GTK_OBJECT (entry), "changed",
  899.                 GTK_SIGNAL_FUNC (entry_callback),
  900.                 &my_config.matrix[x][y]);
  901.     gtk_widget_show (entry);
  902.       }
  903.  
  904.   gtk_widget_show (table);
  905.  
  906.   box = gtk_hbox_new (FALSE, 6);
  907.   gtk_box_pack_start (GTK_BOX (inbox), box, FALSE, FALSE, 0);
  908.  
  909.   table = gtk_table_new (1, 2, FALSE);
  910.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  911.   gtk_box_pack_start (GTK_BOX (box), table, TRUE, FALSE, 0);
  912.  
  913.   label = gtk_label_new (_("Divisor:"));
  914.   gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  915.   gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, 0, 1);
  916.   gtk_widget_show (label);
  917.  
  918.   my_widgets.divisor = entry = gtk_entry_new ();
  919.   gtk_widget_set_usize (entry, 40, 0);
  920.   gtk_table_attach_defaults (GTK_TABLE (table), entry, 1, 2, 0, 1);
  921.   gtk_signal_connect (GTK_OBJECT (entry), "changed",
  922.               GTK_SIGNAL_FUNC (entry_callback),
  923.               &my_config.divisor);
  924.   gtk_widget_show (entry);
  925.  
  926.   gtk_widget_show (table);
  927.  
  928.   table = gtk_table_new (1, 2, FALSE);
  929.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  930.   gtk_box_pack_start (GTK_BOX (box), table, TRUE, FALSE, 0);
  931.  
  932.   label = gtk_label_new (_("Offset:"));
  933.   gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  934.   gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, 0, 1);
  935.   gtk_widget_show (label);
  936.  
  937.   my_widgets.offset = entry = gtk_entry_new ();
  938.   gtk_widget_set_usize (entry, 40, 0);
  939.   gtk_table_attach_defaults (GTK_TABLE (table), entry, 1, 2, 0, 1);
  940.   gtk_signal_connect (GTK_OBJECT (entry), "changed",
  941.               (GtkSignalFunc) entry_callback,
  942.               &my_config.offset);
  943.   gtk_widget_show (entry);
  944.  
  945.   gtk_widget_show (table);
  946.  
  947.   gtk_widget_show (box);
  948.  
  949.   gtk_widget_show (inbox);
  950.   gtk_widget_show (frame);
  951.  
  952.   box = gtk_vbox_new (FALSE, 2);
  953.   gtk_box_pack_start (GTK_BOX (yetanotherbox), box, FALSE, FALSE, 0);
  954.  
  955.   my_widgets.autoset = button =
  956.     gtk_check_button_new_with_label (_("Automatic"));
  957.   gtk_box_pack_start (GTK_BOX (box), button, TRUE, FALSE, 0);
  958.   gtk_signal_connect (GTK_OBJECT (button), "toggled",
  959.               (GtkSignalFunc) my_toggle_callback,
  960.               &my_config.autoset);
  961.   gtk_widget_show (button);
  962.  
  963.   my_widgets.alpha_alg = button =
  964.     gtk_check_button_new_with_label (_("Alpha-weighting"));
  965.   if (my_config.alpha_alg == -1)
  966.     gtk_widget_set_sensitive (button, FALSE);
  967.   gtk_box_pack_start (GTK_BOX (box), button, TRUE, FALSE, 0);
  968.   gtk_signal_connect (GTK_OBJECT (button), "toggled",
  969.               (GtkSignalFunc)my_toggle_callback,
  970.               &my_config.alpha_alg);
  971.   gtk_widget_show (button);
  972.  
  973.   gtk_widget_show (box);
  974.   gtk_widget_show (yetanotherbox);
  975.  
  976.   inbox = gtk_vbox_new (FALSE, 4);
  977.   gtk_box_pack_start (GTK_BOX (main_hbox), inbox, FALSE, FALSE, 0);
  978.  
  979.   frame = gtk_frame_new (_("Border"));
  980.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  981.   gtk_box_pack_start (GTK_BOX (inbox), frame, FALSE, FALSE, 0);
  982.  
  983.   box = gtk_vbox_new (FALSE, 1);
  984.   gtk_container_set_border_width (GTK_CONTAINER (box), 2);
  985.   gtk_container_add (GTK_CONTAINER (frame), box);
  986.  
  987.   group = NULL;
  988.  
  989.   for (i = 0; i < 3; i++)
  990.     {
  991.       my_widgets.bmode[i] = button =
  992.     gtk_radio_button_new_with_label (group, gettext (bmode_labels[i]));
  993.       group = gtk_radio_button_group (GTK_RADIO_BUTTON (button));
  994.       gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
  995.       gtk_widget_show (button);
  996.       gtk_signal_connect (GTK_OBJECT (button), "toggled",
  997.               (GtkSignalFunc) my_bmode_callback,
  998.               (gpointer) (i + 1));
  999.     }
  1000.  
  1001.   gtk_widget_show (box);
  1002.   gtk_widget_show (frame);
  1003.  
  1004.   frame=gtk_frame_new (_("Channels"));
  1005.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  1006.   gtk_box_pack_start (GTK_BOX (inbox), frame, FALSE, FALSE, 0);
  1007.  
  1008.   box = gtk_vbox_new (FALSE, 1);
  1009.   gtk_container_set_border_width (GTK_CONTAINER (box), 2);
  1010.   gtk_container_add (GTK_CONTAINER (frame), box);
  1011.  
  1012.   for (i = 0; i < 5; i++)
  1013.     {
  1014.       my_widgets.channels[i] = button =
  1015.     gtk_check_button_new_with_label (gettext (channel_labels[i]));
  1016.       if (my_config.channels[i] < 0)
  1017.     gtk_widget_set_sensitive (button, FALSE);
  1018.       gtk_signal_connect (GTK_OBJECT (button), "toggled",
  1019.               (GtkSignalFunc)my_toggle_callback,
  1020.               &my_config.channels[i]);
  1021.       gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
  1022.       gtk_widget_show (button);
  1023.     }
  1024.  
  1025.   gtk_widget_show(box);
  1026.   gtk_widget_show (frame);
  1027.  
  1028.   gtk_widget_show (inbox);
  1029.  
  1030.   gtk_widget_show (main_hbox);
  1031.  
  1032.   gtk_widget_show (dlg);
  1033.   redraw_all ();
  1034.  
  1035.   gtk_widget_set_sensitive (my_widgets.bmode[CLEAR], 
  1036.                 (my_config.alpha_alg > 0));
  1037.  
  1038.   gtk_main ();
  1039.   gdk_flush ();
  1040.  
  1041.   return run_flag;
  1042. }
  1043.