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 / unsharp.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-11-26  |  25.6 KB  |  933 lines

  1. /* $Id: unsharp.c,v 1.18 2000/11/26 12:13:23 neo Exp $
  2.  *
  3.  * unsharp.c 0.10 -- This is a plug-in for the GIMP 1.0
  4.  *  http://www.stdout.org/~winston/gimp/unsharp.html
  5.  *  (now out of date) 
  6.  *
  7.  * Copyright (C) 1999 Winston Chang
  8.  *                    <winstonc@cs.wisc.edu>
  9.  *                    <winston@stdout.org>
  10.  *
  11.  * This program is free software; you can redistribute it and/or modify
  12.  * it under the terms of the GNU General Public License as published by
  13.  * the Free Software Foundation; either version 2 of the License, or
  14.  * (at your option) any later version.
  15.  *
  16.  * This program is distributed in the hope that it will be useful,
  17.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19.  * GNU General Public License for more details.
  20.  *
  21.  * You should have received a copy of the GNU General Public License
  22.  * along with this program; if not, write to the Free Software
  23.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  24.  */
  25.  
  26. #include "config.h"
  27.  
  28. #include <stdlib.h>
  29. #include <string.h>
  30. #include <stdio.h>
  31.  
  32. #include <gtk/gtk.h>
  33.  
  34. #include <libgimp/gimp.h>
  35. #include <libgimp/gimpui.h>
  36.  
  37. #include "libgimp/stdplugins-intl.h"
  38.  
  39.  
  40. #define PLUG_IN_VERSION "0.10"
  41.  
  42. #define SCALE_WIDTH  150
  43.  
  44. /* to show both pretty unoptimized code and ugly optimized code blocks
  45.    There's really no reason to define this, unless you want to see how
  46.    much pointer aritmetic can speed things up.  I find that it is about
  47.    45% faster with the optimized code. */
  48. /* #define READABLE_CODE */
  49.  
  50. /* uncomment this line to get a rough feel of how long the
  51.    plug-in takes to run */
  52. /* #define TIMER */
  53. #ifdef TIMER
  54. #include <sys/time.h>
  55. #include <unistd.h>
  56.  
  57. static void timerstart (void);
  58. static void timerstop  (void);
  59. static struct timeval time_start, time_stop;
  60. #endif
  61.  
  62. typedef struct
  63. {
  64.   gdouble radius;
  65.   gdouble amount;
  66.   gint    threshold;
  67. } UnsharpMaskParams;
  68.  
  69. typedef struct
  70. {
  71.   gint run;
  72. } UnsharpMaskInterface;
  73.  
  74. /* local function prototypes */
  75. static void query (void);
  76. static void run   (gchar   *name,
  77.            gint     nparams,
  78.            GimpParam  *param,
  79.            gint    *nreturn_vals,
  80.            GimpParam **return_vals);
  81.  
  82. static inline void blur_line     (gdouble    *ctable,
  83.                   gdouble    *cmatrix,
  84.                   gint        cmatrix_length,
  85.                   guchar     *cur_col,
  86.                   guchar     *dest_col,
  87.                   gint        y,
  88.                   glong       bytes);
  89. static int gen_convolve_matrix   (gdouble     std_dev,
  90.                   gdouble   **cmatrix);
  91. static gdouble* gen_lookup_table (gdouble*    cmatrix,
  92.                   gint        cmatrix_length);
  93. static void unsharp_region       (GimpPixelRgn   srcPTR,
  94.                   GimpPixelRgn   dstPTR,
  95.                   gint        width,
  96.                   gint        height,
  97.                   gint        bytes,
  98.                   gdouble     radius,
  99.                   gdouble     amount,
  100.                   gint        x1,
  101.                   gint        x2,
  102.                   gint        y1,
  103.                   gint        y2);
  104.  
  105. static void unsharp_mask         (GimpDrawable *drawable,
  106.                   gint       radius,
  107.                   gdouble    amount);
  108.  
  109. static void unsharp_ok_callback  (GtkWidget *widget,
  110.                   gpointer   data);
  111. static gint unsharp_mask_dialog  (void);
  112.  
  113. /* preview shit -- not finished yet */
  114. #undef PREVIEW
  115. #ifdef PREVIEW
  116. static void preview_scroll_callback (void);
  117. static void preview_init            (void);
  118. static void preview_exit            (void);
  119. static void preview_update          (void);
  120.  
  121. static GtkWidget* preview;
  122. static gint       preview_width;    /* Width of preview widget */
  123. static gint       preview_height;   /* Height of preview widget */
  124. static gint       preview_x1;       /* Upper-left X of preview */
  125. static gint       preview_y1;       /* Upper-left Y of preview */
  126. static gint       preview_x2;       /* Lower-right X of preview */
  127. static gint       preview_y2;       /* Lower-right Y of preview */
  128.  
  129. static gint       sel_width;    /* Selection width */
  130. static gint       sel_height;   /* Selection height */
  131.         
  132. static GtkObject *hscroll_data;    /* Horizontal scrollbar data */
  133. static GtkObject *vscroll_data;    /* Vertical scrollbar data */
  134. #endif
  135.  
  136. static gint       run_filter = FALSE;
  137.         
  138.  
  139. /* create a few globals, set default values */
  140. static UnsharpMaskParams unsharp_params =
  141. {
  142.   5.0, /* default radius = 5 */
  143.   0.5, /* default amount = .5 */
  144.   0    /* default threshold = 0 */
  145. };
  146.  
  147. /* static UnsharpMaskInterface umint = { FALSE }; */
  148.  
  149. /* Setting PLUG_IN_INFO */
  150. GimpPlugInInfo PLUG_IN_INFO =
  151. {
  152.   NULL,  /* init_proc  */
  153.   NULL,  /* quit_proc  */
  154.   query, /* query_proc */
  155.   run,   /* run_proc   */
  156. };
  157.  
  158. MAIN ()
  159.  
  160. static void
  161. query (void)
  162. {
  163.   static GimpParamDef args[] =
  164.   {
  165.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  166.     { GIMP_PDB_IMAGE, "image", "(unused)" },
  167.     { GIMP_PDB_DRAWABLE, "drawable", "Drawable to draw on" },
  168.     { GIMP_PDB_FLOAT, "radius", "Radius of gaussian blur (in pixels > 1.0)" },
  169.     { GIMP_PDB_FLOAT, "amount", "Strength of effect" },
  170.     { GIMP_PDB_FLOAT, "threshold", "Threshold" }
  171.   };
  172.   static gint nargs = sizeof (args) / sizeof (args[0]);
  173.     
  174.   /* Install a procedure in the procedure database. */
  175.   gimp_install_procedure ("plug_in_unsharp_mask",
  176.               "An unsharp mask filter",
  177.               "The unsharp mask is a sharpening filter that works "
  178.               "by comparing using the difference of the image and "
  179.               "a blurred version of the image.  It is commonly "
  180.               "used on photographic images, and is provides a much "
  181.               "more pleasing result than the standard sharpen "
  182.               "filter.",
  183.               "Winston Chang <winstonc@cs.wisc.edu>",
  184.               "Winston Chang",
  185.               "1999",
  186.               N_("<Image>/Filters/Enhance/Unsharp Mask..."),
  187.               "GRAY*, RGB*",
  188.               GIMP_PLUGIN,
  189.               nargs, 0,
  190.               args, NULL);
  191. }
  192.  
  193. /* this is the actual function */
  194. static void
  195. run (gchar   *name,
  196.      gint     nparams,
  197.      GimpParam  *param,
  198.      gint    *nreturn_vals,
  199.      GimpParam **return_vals)
  200. {
  201.   static GimpParam values[1];
  202.   GimpDrawable *drawable;
  203.   GimpRunModeType run_mode;
  204.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  205.  
  206. #ifdef TIMER
  207.   timerstart();
  208. #endif
  209.  
  210.   run_mode = param[0].data.d_int32;
  211.  
  212.   values[0].type = GIMP_PDB_STATUS;
  213.   values[0].data.d_status = status;
  214.   *return_vals = values;
  215.   *nreturn_vals = 1;
  216.  
  217.   INIT_I18N_UI(); 
  218.  
  219.   switch (run_mode)
  220.     {
  221.     case GIMP_RUN_INTERACTIVE:
  222.       gimp_get_data ("plug_in_unsharp_mask", &unsharp_params);
  223.       if (! unsharp_mask_dialog ()) return;
  224.       break;
  225.  
  226.     case GIMP_RUN_NONINTERACTIVE:
  227.       if (nparams != 6)
  228.     {
  229.       status = GIMP_PDB_CALLING_ERROR;
  230.     }
  231.       else
  232.     {
  233.       unsharp_params.radius = param[3].data.d_float;
  234.       unsharp_params.amount = param[4].data.d_float; 
  235.       unsharp_params.threshold = param[5].data.d_int32;
  236.  
  237.       /* make sure there are legal values */
  238.       if ((unsharp_params.radius < 0.0) ||
  239.           (unsharp_params.amount<0.0))
  240.         status = GIMP_PDB_CALLING_ERROR;
  241.     }
  242.       break;
  243.  
  244.     case GIMP_RUN_WITH_LAST_VALS:
  245.       gimp_get_data ("plug_in_unsharp_mask", &unsharp_params);
  246.       break;
  247.  
  248.     default:
  249.       break;
  250.     }
  251.  
  252.   if (status == GIMP_PDB_SUCCESS)
  253.     {
  254.       drawable = gimp_drawable_get (param[2].data.d_drawable);
  255.       gimp_tile_cache_ntiles(2 * (drawable->width / gimp_tile_width() + 1));
  256.  
  257.       /* here we go */
  258.       unsharp_mask (drawable, unsharp_params.radius, unsharp_params.amount);
  259.  
  260.       /* values[0].data.d_status = status; */
  261.       gimp_displays_flush ();
  262.  
  263.       /* set data for next use of filter */
  264.       gimp_set_data ("plug_in_unsharp_mask", &unsharp_params,
  265.              sizeof (UnsharpMaskParams));
  266.  
  267.       /*fprintf(stderr, "%f %f\n", unsharp_params.radius, unsharp_params.amount);*/
  268.  
  269.       gimp_drawable_detach(drawable);
  270.       values[0].data.d_status = status;
  271.     }
  272. #ifdef TIMER
  273.   timerstop ();
  274. #endif
  275. }
  276.  
  277. /* -------------------------- Unsharp Mask ------------------------- */
  278. static void
  279. unsharp_mask (GimpDrawable *drawable,
  280.           gint       radius,
  281.           gdouble    amount)
  282. {
  283.   GimpPixelRgn srcPR, destPR;
  284.   glong width, height;
  285.   glong bytes;
  286.   gint  x1, y1, x2, y2;
  287.  
  288.   /* Get the input */
  289.   gimp_drawable_mask_bounds(drawable->id, &x1, &y1, &x2, &y2);
  290.   gimp_progress_init(_("Blurring..."));
  291.  
  292.   width = drawable->width;
  293.   height = drawable->height;
  294.   bytes = drawable->bpp;
  295.  
  296.   /* initialize pixel regions */
  297.   gimp_pixel_rgn_init (&srcPR, drawable, 0, 0, width, height, FALSE, FALSE);
  298.   gimp_pixel_rgn_init (&destPR, drawable, 0, 0, width, height, TRUE, TRUE);
  299.  
  300.   unsharp_region (srcPR, destPR, width, height, bytes, radius, amount,
  301.           x1, x2, y1, y2);
  302.  
  303.   gimp_drawable_flush(drawable);
  304.   gimp_drawable_merge_shadow(drawable->id, TRUE);
  305.   gimp_drawable_update(drawable->id, x1, y1, (x2-x1), (y2-y1));
  306. }
  307.  
  308. /* perform an unsharp mask on the region, given a source region, dest.
  309.    region, width and height of the regions, and corner coordinates of
  310.    a subregion to act upon.  Everything outside the subregion is unaffected.
  311.   */
  312. static void
  313. unsharp_region (GimpPixelRgn srcPR,
  314.         GimpPixelRgn destPR,
  315.         gint      width,
  316.         gint      height,
  317.         gint      bytes,
  318.         gdouble   radius,
  319.         gdouble   amount,
  320.         gint      x1,
  321.         gint      x2,
  322.         gint      y1,
  323.         gint      y2)
  324. {
  325.   guchar  *cur_col;
  326.   guchar  *dest_col;
  327.   guchar  *cur_row;
  328.   guchar  *dest_row;
  329.   gint     x;
  330.   gint     y;
  331.   gdouble *cmatrix = NULL;
  332.   gint     cmatrix_length;
  333.   gdouble *ctable;
  334.  
  335.   gint row, col;  /* these are counters for loops */
  336.  
  337.   /* these are used for the merging step */
  338.   gint threshold;
  339.   gint diff;
  340.   gint value;
  341.   gint u,v;
  342.  
  343.   /* find height and width of subregion to act on */
  344.   x = x2-x1;
  345.   y = y2-y1;
  346.  
  347.   /* generate convolution matrix and make sure it's smaller than each dimension */
  348.   cmatrix_length = gen_convolve_matrix(radius, &cmatrix);
  349.   /* generate lookup table */
  350.   ctable = gen_lookup_table(cmatrix, cmatrix_length);
  351.  
  352.   /*  allocate row buffers  */
  353.   cur_row  = g_new (guchar, x * bytes);
  354.   dest_row = g_new (guchar, x * bytes);
  355.  
  356.   /* find height and width of subregion to act on */
  357.   x = x2-x1;
  358.   y = y2-y1;
  359.  
  360.   /* blank out a region of the destination memory area, I think */
  361.   for (row = 0; row < y; row++)
  362.     {
  363.       gimp_pixel_rgn_get_row(&destPR, dest_row, x1, y1+row, (x2-x1));
  364.       memset(dest_row, 0, x*bytes);
  365.       gimp_pixel_rgn_set_row(&destPR, dest_row, x1, y1+row, (x2-x1));
  366.     }
  367.  
  368.   /* blur the rows */
  369.   for (row = 0; row < y; row++)
  370.     {
  371.       gimp_pixel_rgn_get_row(&srcPR, cur_row, x1, y1+row, x);
  372.       gimp_pixel_rgn_get_row(&destPR, dest_row, x1, y1+row, x);
  373.       blur_line(ctable, cmatrix, cmatrix_length, cur_row, dest_row, x, bytes);
  374.       gimp_pixel_rgn_set_row(&destPR, dest_row, x1, y1+row, x);
  375.  
  376.       if (row%5 == 0)
  377.     gimp_progress_update((gdouble)row/(3*y));
  378.     }
  379.  
  380.   /* allocate column buffers */
  381.   cur_col  = g_new (guchar, y * bytes);
  382.   dest_col = g_new (guchar, y * bytes);
  383.  
  384.   /* blur the cols */
  385.   for (col = 0; col < x; col++)
  386.     {
  387.       gimp_pixel_rgn_get_col(&destPR, cur_col, x1+col, y1, y);
  388.       gimp_pixel_rgn_get_col(&destPR, dest_col, x1+col, y1, y);
  389.       blur_line(ctable, cmatrix, cmatrix_length, cur_col, dest_col, y, bytes);
  390.       gimp_pixel_rgn_set_col(&destPR, dest_col, x1+col, y1, y);
  391.  
  392.       if (col%5 == 0)
  393.     gimp_progress_update((gdouble)col/(3*x) + 0.33);
  394.     }
  395.  
  396.   gimp_progress_init(_("Merging..."));
  397.  
  398.   /* find integer value of threshold */
  399.   threshold = unsharp_params.threshold;
  400.     
  401.   /* merge the source and destination (which currently contains
  402.      the blurred version) images */
  403.   for (row = 0; row < y; row++)
  404.     {
  405.       value = 0;
  406.       /* get source row */
  407.       gimp_pixel_rgn_get_row(&srcPR, cur_row, x1, y1+row, x);
  408.       /* get dest row */
  409.       gimp_pixel_rgn_get_row(&destPR, dest_row, x1, y1+row, x);
  410.       /* combine the two */
  411.       for (u = 0; u < x; u++)
  412.     {
  413.       for (v = 0; v < bytes; v++)
  414.         {
  415.           diff = (cur_row[u*bytes+v] - dest_row[u*bytes+v]);
  416.           /* do tresholding */
  417.           if (abs (2 * diff) < threshold)
  418.         diff = 0;
  419.  
  420.           value = cur_row[u*bytes+v] + amount * diff;
  421.                 
  422.           if (value < 0) dest_row[u*bytes+v] =0;
  423.           else if (value > 255) dest_row[u*bytes+v] = 255;
  424.           else  dest_row[u*bytes+v] = value;
  425.         }
  426.     }
  427.       /* update progress bar every five rows */
  428.       if (row%5 == 0) gimp_progress_update((gdouble)row/(3*y) + 0.67);
  429.       gimp_pixel_rgn_set_row(&destPR, dest_row, x1, y1+row, x);
  430.     }
  431.  
  432.   /* free the memory we took */
  433.   g_free(cur_row);
  434.   g_free(dest_row);
  435.   g_free(cur_col);
  436.   g_free(dest_col);
  437.   g_free(cmatrix);
  438.   g_free(ctable);
  439.  
  440. }
  441.  
  442. /* this function is written as if it is blurring a column at a time,
  443.    even though it can operate on rows, too.  There is no difference
  444.    in the processing of the lines, at least to the blur_line function. */
  445. static inline void
  446. blur_line (gdouble *ctable,
  447.        gdouble *cmatrix,
  448.        gint     cmatrix_length,
  449.        guchar  *cur_col,
  450.        guchar  *dest_col,
  451.        gint     y,
  452.        glong    bytes)
  453. {
  454. #ifdef READABLE_CODE
  455. /* ------------- semi-readable code ------------------- */
  456.   gdouble scale;
  457.   gdouble sum;
  458.   gint i,j;
  459.   gint row;
  460.  
  461.   /* this is to take care cases in which the matrix can go over
  462.    * both edges at once.  It's not efficient, but this can only
  463.    * happen in small pictures anyway.
  464.    */
  465.   if (cmatrix_length > y)
  466.     {
  467.       for (row = 0; row < y ; row++)
  468.     {
  469.       scale = 0;
  470.       /* find the scale factor */
  471.       for (j = 0; j < y ; j++)
  472.         {
  473.           /* if the index is in bounds, add it to the scale counter */
  474.           if ((j + cmatrix_length/2 - row >= 0) &&
  475.           (j + cmatrix_length/2 - row < cmatrix_length))
  476.         scale += cmatrix[j + cmatrix_length/2 - row];
  477.         }
  478.       for (i = 0; i<bytes; i++)
  479.         {
  480.           sum = 0;
  481.           for (j = 0; j < y; j++)
  482.         {
  483.           if ((j >= row - cmatrix_length/2) &&
  484.               (j <= row + cmatrix_length/2))
  485.             sum += cur_col[j*bytes + i] * cmatrix[j];
  486.         }
  487.           dest_col[row*bytes + i] = (guchar) ROUND (sum / scale);
  488.         }
  489.     }
  490.     }
  491.   else
  492.     {  /* when the cmatrix is smaller than row length */
  493.       /* for the edge condition, we only use available info, and scale to one */
  494.       for (row = 0; row < cmatrix_length/2; row++)
  495.     {
  496.       /* find scale factor */
  497.       scale=0;
  498.       for (j = cmatrix_length/2 - row; j<cmatrix_length; j++)
  499.         scale += cmatrix[j];
  500.  
  501.       for (i = 0; i<bytes; i++)
  502.         {
  503.           sum = 0;
  504.           for (j = cmatrix_length/2 - row; j<cmatrix_length; j++)
  505.         {
  506.           sum += cur_col[(row + j-cmatrix_length/2)*bytes + i] *
  507.             cmatrix[j];
  508.         }
  509.           dest_col[row*bytes + i] = (guchar) ROUND (sum / scale);
  510.         }
  511.     }
  512.       /* go through each pixel in each col */
  513.       for (; row < y-cmatrix_length/2; row++)
  514.     {
  515.       for (i = 0; i<bytes; i++)
  516.         {
  517.           sum = 0;
  518.           for (j = 0; j<cmatrix_length; j++)
  519.         {
  520.           sum += cur_col[(row + j-cmatrix_length/2)*bytes + i] * cmatrix[j];
  521.         }
  522.           dest_col[row*bytes + i] = (guchar) ROUND (sum);
  523.         }
  524.     }
  525.       /* for the edge condition , we only use available info, and scale to one */
  526.       for (; row < y; row++)
  527.     {
  528.       /* find scale factor */
  529.       scale=0;
  530.       for (j = 0; j< y-row + cmatrix_length/2; j++)
  531.         scale += cmatrix[j];
  532.  
  533.       for (i = 0; i<bytes; i++)
  534.         {
  535.           sum = 0;
  536.           for (j = 0; j<y-row + cmatrix_length/2; j++)
  537.         {
  538.           sum += cur_col[(row + j-cmatrix_length/2)*bytes + i] * cmatrix[j];
  539.         }
  540.           dest_col[row*bytes + i] = (guchar) ROUND (sum / scale);
  541.         }
  542.     }
  543.     }
  544. #endif
  545. #ifndef READABLE_CODE
  546.   /* --------------- optimized, unreadable code -------------------*/
  547.   gdouble scale;
  548.   gdouble sum;
  549.   gint i=0, j=0;
  550.   gint row;
  551.   gint cmatrix_middle = cmatrix_length/2;
  552.  
  553.   gdouble *cmatrix_p;
  554.   guchar  *cur_col_p;
  555.   guchar  *cur_col_p1;
  556.   guchar  *dest_col_p;
  557.   gdouble *ctable_p;
  558.  
  559.   /* this first block is the same as the non-optimized version --
  560.    * it is only used for very small pictures, so speed isn't a
  561.    * big concern.
  562.    */
  563.   if (cmatrix_length > y)
  564.     {
  565.       for (row = 0; row < y ; row++)
  566.     {
  567.       scale=0;
  568.       /* find the scale factor */
  569.       for (j = 0; j < y ; j++)
  570.         {
  571.           /* if the index is in bounds, add it to the scale counter */
  572.           if ((j + cmatrix_length/2 - row >= 0) &&
  573.           (j + cmatrix_length/2 - row < cmatrix_length))
  574.         scale += cmatrix[j + cmatrix_length/2 - row];
  575.         }
  576.       for (i = 0; i<bytes; i++)
  577.         {
  578.           sum = 0;
  579.           for (j = 0; j < y; j++)
  580.         {
  581.           if ((j >= row - cmatrix_length/2) &&
  582.               (j <= row + cmatrix_length/2))
  583.             sum += cur_col[j*bytes + i] * cmatrix[j];
  584.         }
  585.           dest_col[row*bytes + i] = (guchar) ROUND (sum / scale);
  586.         }
  587.     }
  588.     }
  589.   else
  590.     {
  591.       /* for the edge condition, we only use available info and scale to one */
  592.       for (row = 0; row < cmatrix_middle; row++)
  593.     {
  594.       /* find scale factor */
  595.       scale=0;
  596.       for (j = cmatrix_middle - row; j<cmatrix_length; j++)
  597.         scale += cmatrix[j];
  598.       for (i = 0; i<bytes; i++)
  599.         {
  600.           sum = 0;
  601.           for (j = cmatrix_middle - row; j<cmatrix_length; j++)
  602.         {
  603.           sum += cur_col[(row + j-cmatrix_middle)*bytes + i] * cmatrix[j];
  604.         }
  605.           dest_col[row*bytes + i] = (guchar) ROUND (sum / scale);
  606.         }
  607.     }
  608.       /* go through each pixel in each col */
  609.       dest_col_p = dest_col + row*bytes;
  610.       for (; row < y-cmatrix_middle; row++)
  611.     {
  612.       cur_col_p = (row - cmatrix_middle) * bytes + cur_col;
  613.       for (i = 0; i<bytes; i++)
  614.         {
  615.           sum = 0;
  616.           cmatrix_p = cmatrix;
  617.           cur_col_p1 = cur_col_p;
  618.           ctable_p = ctable;
  619.           for (j = cmatrix_length; j>0; j--)
  620.         {
  621.           sum += *(ctable_p + *cur_col_p1);
  622.           cur_col_p1 += bytes;
  623.           ctable_p += 256;
  624.         }
  625.           cur_col_p++;
  626.           *(dest_col_p++) = ROUND (sum);
  627.         }
  628.     }
  629.     
  630.       /* for the edge condition , we only use available info, and scale to one */
  631.       for (; row < y; row++)
  632.     {
  633.       /* find scale factor */
  634.       scale=0;
  635.       for (j = 0; j< y-row + cmatrix_middle; j++)
  636.         scale += cmatrix[j];
  637.       for (i = 0; i<bytes; i++)
  638.         {
  639.           sum = 0;
  640.           for (j = 0; j<y-row + cmatrix_middle; j++)
  641.         {
  642.           sum += cur_col[(row + j-cmatrix_middle)*bytes + i] * cmatrix[j];
  643.         }
  644.           dest_col[row*bytes + i] = (guchar) ROUND (sum / scale);
  645.         }
  646.     }
  647.     }
  648. #endif
  649. }
  650.  
  651. /* generates a 1-D convolution matrix to be used for each pass of 
  652.  * a two-pass gaussian blur.  Returns the length of the matrix.
  653.  */
  654. static gint
  655. gen_convolve_matrix (gdouble   radius,
  656.              gdouble **cmatrix_p)
  657. {
  658.   gint matrix_length;
  659.   gint matrix_midpoint;
  660.   gdouble* cmatrix;
  661.   gint i,j;
  662.   gdouble std_dev;
  663.   gdouble sum;
  664.     
  665.   /* we want to generate a matrix that goes out a certain radius
  666.    * from the center, so we have to go out ceil(rad-0.5) pixels,
  667.    * inlcuding the center pixel.  Of course, that's only in one direction,
  668.    * so we have to go the same amount in the other direction, but not count
  669.    * the center pixel again.  So we double the previous result and subtract
  670.    * one.
  671.    * The radius parameter that is passed to this function is used as
  672.    * the standard deviation, and the radius of effect is the
  673.    * standard deviation * 2.  It's a little confusing.
  674.    */
  675.   radius = fabs(radius) + 1.0;
  676.     
  677.   std_dev = radius;
  678.   radius = std_dev * 2;
  679.  
  680.   /* go out 'radius' in each direction */
  681.   matrix_length = 2 * ceil(radius-0.5) + 1;
  682.   if (matrix_length <= 0) matrix_length = 1;
  683.   matrix_midpoint = matrix_length/2 + 1;
  684.   *cmatrix_p = g_new (gdouble, matrix_length);
  685.   cmatrix = *cmatrix_p;
  686.  
  687.   /*  Now we fill the matrix by doing a numeric integration approximation
  688.    * from -2*std_dev to 2*std_dev, sampling 50 points per pixel.
  689.    * We do the bottom half, mirror it to the top half, then compute the
  690.    * center point.  Otherwise asymmetric quantization errors will occur.
  691.    *  The formula to integrate is e^-(x^2/2s^2).
  692.    */
  693.  
  694.   /* first we do the top (right) half of matrix */
  695.   for (i = matrix_length/2 + 1; i < matrix_length; i++)
  696.     {
  697.       double base_x = i - floor(matrix_length/2) - 0.5;
  698.       sum = 0;
  699.       for (j = 1; j <= 50; j++)
  700.     {
  701.       if ( base_x+0.02*j <= radius ) 
  702.         sum += exp (-(base_x+0.02*j)*(base_x+0.02*j) / 
  703.             (2*std_dev*std_dev));
  704.     }
  705.       cmatrix[i] = sum/50;
  706.     }
  707.  
  708.   /* mirror the thing to the bottom half */
  709.   for (i=0; i<=matrix_length/2; i++) {
  710.     cmatrix[i] = cmatrix[matrix_length-1-i];
  711.   }
  712.     
  713.   /* find center val -- calculate an odd number of quanta to make it symmetric,
  714.    * even if the center point is weighted slightly higher than others. */
  715.   sum = 0;
  716.   for (j=0; j<=50; j++)
  717.     {
  718.       sum += exp (-(0.5+0.02*j)*(0.5+0.02*j) /
  719.           (2*std_dev*std_dev));
  720.     }
  721.   cmatrix[matrix_length/2] = sum/51;
  722.     
  723.   /* normalize the distribution by scaling the total sum to one */
  724.   sum=0;
  725.   for (i=0; i<matrix_length; i++) sum += cmatrix[i];
  726.   for (i=0; i<matrix_length; i++) cmatrix[i] = cmatrix[i] / sum;
  727.  
  728.   return matrix_length;
  729. }
  730.  
  731. /* ----------------------- gen_lookup_table ----------------------- */
  732. /* generates a lookup table for every possible product of 0-255 and
  733.    each value in the convolution matrix.  The returned array is
  734.    indexed first by matrix position, then by input multiplicand (?)
  735.    value.
  736. */
  737. static gdouble *
  738. gen_lookup_table (gdouble *cmatrix,
  739.           gint     cmatrix_length)
  740. {
  741.   int i, j;
  742.   gdouble* lookup_table = g_new (gdouble, cmatrix_length * 256);
  743.  
  744. #ifdef READABLE_CODE
  745.   for (i=0; i<cmatrix_length; i++)
  746.     {
  747.       for (j=0; j<256; j++)
  748.     {
  749.       lookup_table[i*256 + j] = cmatrix[i] * (gdouble)j;
  750.     }
  751.     }
  752. #endif
  753.  
  754. #ifndef READABLE_CODE
  755.   gdouble* lookup_table_p = lookup_table;
  756.   gdouble* cmatrix_p      = cmatrix;
  757.  
  758.   for (i=0; i<cmatrix_length; i++)
  759.     {
  760.       for (j=0; j<256; j++)
  761.     {
  762.       *(lookup_table_p++) = *cmatrix_p * (gdouble)j;
  763.     }
  764.       cmatrix_p++;
  765.     }
  766. #endif
  767.  
  768.   return lookup_table;
  769. }
  770.  
  771. /* ------------------------ unsharp_mask_dialog ----------------------- */
  772. static gint
  773. unsharp_mask_dialog (void) 
  774. {
  775.   GtkWidget *window;
  776.   GtkWidget *frame;
  777.   GtkWidget *table;
  778.   GtkObject *adj;
  779.  
  780.   gimp_ui_init ("unsharp", TRUE);
  781.  
  782.   window = gimp_dialog_new (_("Unsharp Mask"), "unsharp",
  783.                 gimp_standard_help_func, "filters/unsharp.html",
  784.                 GTK_WIN_POS_MOUSE,
  785.                 FALSE, TRUE, FALSE,
  786.  
  787.                 _("OK"), unsharp_ok_callback,
  788.                 NULL, NULL, NULL, TRUE, FALSE,
  789.                 _("Cancel"), gtk_widget_destroy,
  790.                 NULL, 1, NULL, FALSE, TRUE,
  791.  
  792.                 NULL);
  793.  
  794.   gtk_signal_connect (GTK_OBJECT (window), "destroy",
  795.               GTK_SIGNAL_FUNC (gtk_main_quit),
  796.               NULL);
  797.  
  798.   frame = gtk_frame_new (_("Parameter Settings"));
  799.   gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
  800.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), frame,
  801.               FALSE, FALSE, 0);
  802.   gtk_widget_show (frame);
  803.  
  804.   table = gtk_table_new (3, 3, FALSE);
  805.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  806.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  807.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  808.   gtk_container_add (GTK_CONTAINER (frame), table);
  809.   gtk_widget_show (table);
  810.  
  811.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
  812.                   _("Radius:"), SCALE_WIDTH, 0,
  813.                   unsharp_params.radius, 1.0, 25.0, 0.1, 1.0, 1,
  814.                   TRUE, 0, 0,
  815.                   NULL, NULL);
  816.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  817.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  818.               &unsharp_params.radius);
  819.  
  820.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
  821.                   _("Amount:"), SCALE_WIDTH, 0,
  822.                   unsharp_params.amount, 0.0, 5.0, 0.01, 0.1, 2,
  823.                   TRUE, 0, 0,
  824.                   NULL, NULL);
  825.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  826.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  827.               &unsharp_params.amount);
  828.  
  829.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
  830.                   _("Threshold:"), SCALE_WIDTH, 0,
  831.                   unsharp_params.threshold, 0.0, 255.0, 1.0, 10.0, 0,
  832.                   TRUE, 0, 0,
  833.                   NULL, NULL);
  834.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  835.               GTK_SIGNAL_FUNC (gimp_int_adjustment_update),
  836.               &unsharp_params.threshold);
  837.  
  838.   gtk_widget_show (window);
  839.  
  840.   gtk_main ();
  841.   gdk_flush ();
  842.  
  843.   return run_filter;
  844. }
  845.  
  846.  
  847. /* This doesn't work yet. */
  848. #ifdef PREVIEW
  849. /* 'preview_init()' - Initialize the preview window... */
  850.  
  851. static void
  852. preview_init (void)
  853. {
  854.   int    width;        /* Byte width of the image */
  855.  
  856.   /*
  857.    * Setup for preview filter...
  858.    */
  859.  
  860.   width = preview_width * img_bpp;
  861.  
  862.   preview_src   = g_new (guchar, width * preview_height);
  863.   preview_neg   = g_new (guchar, width * preview_height);
  864.   preview_dst   = g_new (guchar, width * preview_height);
  865.   preview_image = g_new (guchar, preview_width * preview_height * 3);
  866.  
  867.   preview_x1 = sel_x1;
  868.   preview_y1 = sel_y1;
  869.   preview_x2 = preview_x1 + preview_width;
  870.   preview_y2 = preview_y1 + preview_height;
  871. }
  872.  
  873. /*
  874.  * 'preview_scroll_callback()' - Update the preview when a scrollbar is moved.
  875.  */
  876.  
  877. static void
  878. preview_scroll_callback (void)
  879. {
  880.   preview_x1 = sel_x1 + GTK_ADJUSTMENT (hscroll_data)->value;
  881.   preview_y1 = sel_y1 + GTK_ADJUSTMENT (vscroll_data)->value;
  882.   preview_x2 = preview_x1 + MIN(preview_width, sel_width);
  883.   preview_y2 = preview_y1 + MIN(preview_height, sel_height);
  884.  
  885.   preview_update ();
  886. }
  887.  
  888. /*
  889.  * 'preview_update()' - Update the preview window.
  890.  */
  891.  
  892. static void
  893. preview_update (void)
  894. {
  895.  
  896. }
  897. #endif
  898.  
  899. static void
  900. unsharp_ok_callback (GtkWidget *widget,
  901.              gpointer   data)
  902. {
  903.   run_filter = TRUE;
  904.  
  905.   gtk_widget_destroy (GTK_WIDGET (data));
  906. }
  907.  
  908. #ifdef TIMER
  909. static void
  910. timerstart (void)
  911. {
  912.   gettimeofday (&time_start, NULL);
  913. }
  914.  
  915. static void
  916. timerstop (void)
  917. {
  918.   long sec, usec;
  919.     
  920.   gettimeofday(&time_stop,NULL);
  921.   sec = time_stop.tv_sec  - time_start.tv_sec;
  922.   usec = time_stop.tv_usec - time_start.tv_usec;
  923.     
  924.   if (usec < 0)
  925.     {
  926.       sec--;
  927.       usec += 1000000;
  928.     }
  929.     
  930.   fprintf (stderr, "%ld.%ld seconds\n", sec, usec);
  931. }
  932. #endif
  933.