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 / gauss_iir.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-10-28  |  24.7 KB  |  906 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * This program is free software; you can redistribute it and/or modify
  5.  * it under the terms of the GNU General Public License as published by
  6.  * the Free Software Foundation; either version 2 of the License, or
  7.  * (at your option) any later version.
  8.  *
  9.  * This program is distributed in the hope that it will be useful,
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  * GNU General Public License for more details.
  13.  *
  14.  * You should have received a copy of the GNU General Public License
  15.  * along with this program; if not, write to the Free Software
  16.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17.  */
  18.  
  19. #include "config.h"
  20.  
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24.  
  25. #include <gtk/gtk.h>
  26.  
  27. #include <libgimp/gimp.h>
  28. #include <libgimp/gimpui.h>
  29.  
  30. #include "libgimp/stdplugins-intl.h"
  31.  
  32.  
  33. typedef struct
  34. {
  35.   gdouble  radius;
  36.   gboolean horizontal;
  37.   gboolean vertical;
  38. } BlurValues;
  39.  
  40. typedef struct
  41. {
  42.   gdouble horizontal;
  43.   gdouble vertical;
  44. } Blur2Values;
  45.  
  46. typedef struct
  47. {
  48.   GtkWidget *size;
  49.   gint       run;
  50. } BlurInterface;
  51.  
  52.  
  53. /* Declare local functions.
  54.  */
  55. static void      query  (void);
  56. static void      run    (gchar      *name,
  57.              gint        nparams,
  58.              GimpParam  *param,
  59.              gint       *nreturn_vals,
  60.              GimpParam **return_vals);
  61.  
  62. static void      gauss_iir         (GimpDrawable *drawable,
  63.                     gdouble       horizontal,
  64.                     gdouble       vertical);
  65.  
  66. /*
  67.  * Gaussian blur interface
  68.  */
  69. static gint      gauss_iir_dialog  (void);
  70. static gint      gauss_iir2_dialog (gint32        image_ID, 
  71.                     GimpDrawable *drawable);
  72.  
  73. /*
  74.  * Gaussian blur helper functions
  75.  */
  76. static void      find_constants    (gdouble  n_p[],
  77.                     gdouble  n_m[],
  78.                     gdouble  d_p[],
  79.                     gdouble  d_m[],
  80.                     gdouble  bd_p[],
  81.                     gdouble  bd_m[],
  82.                     gdouble  std_dev);
  83. static void      transfer_pixels   (gdouble *src1,
  84.                     gdouble *src2,
  85.                     guchar  *dest,
  86.                     gint     bytes,
  87.                     gint     width);
  88.  
  89. static void      gauss_ok_callback (GtkWidget *widget,
  90.                     gpointer   data);
  91.  
  92. GimpPlugInInfo PLUG_IN_INFO =
  93. {
  94.   NULL,  /* init_proc  */
  95.   NULL,  /* quit_proc  */
  96.   query, /* query_proc */
  97.   run,   /* run_proc   */
  98. };
  99.  
  100. static BlurValues bvals =
  101. {
  102.   5.0,  /*  radius           */
  103.   TRUE, /*  horizontal blur  */
  104.   TRUE  /*  vertical blur    */
  105. };
  106.  
  107. static Blur2Values b2vals =
  108. {
  109.   5.0,  /*  x radius  */
  110.   5.0   /*  y radius  */
  111. };
  112.  
  113. static BlurInterface bint =
  114. {
  115.   FALSE  /*  run  */
  116. };
  117.  
  118.  
  119. MAIN ()
  120.  
  121. static void
  122. query (void)
  123. {
  124.   static GimpParamDef args[] =
  125.   {
  126.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  127.     { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
  128.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  129.     { GIMP_PDB_FLOAT, "radius", "Radius of gaussian blur (in pixels > 1.0)" },
  130.     { GIMP_PDB_INT32, "horizontal", "Blur in horizontal direction" },
  131.     { GIMP_PDB_INT32, "vertical", "Blur in vertical direction" }
  132.   };
  133.   static gint nargs = sizeof (args) / sizeof (args[0]);
  134.  
  135.   static GimpParamDef args2[] =
  136.   {
  137.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  138.     { GIMP_PDB_IMAGE, "image", "Input image" },
  139.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  140.     { GIMP_PDB_FLOAT, "horizontal", "Horizontal radius of gaussian blur (in pixels)" },
  141.     { GIMP_PDB_FLOAT, "vertical",   "Vertical radius of gaussian blur (in pixels)" }
  142.   };
  143.   static gint nargs2 = sizeof (args2) / sizeof (args2[0]);
  144.  
  145.   gimp_install_procedure ("plug_in_gauss_iir",
  146.               "Applies a gaussian blur to the specified drawable.",
  147.               "Applies a gaussian blur to the drawable, with "
  148.               "specified radius of affect.  The standard deviation "
  149.               "of the normal distribution used to modify pixel "
  150.               "values is calculated based on the supplied radius.  "
  151.               "Horizontal and vertical blurring can be "
  152.               "independently invoked by specifying only one to "
  153.               "run.  The IIR gaussian blurring works best for "
  154.               "large radius values and for images which are not "
  155.               "computer-generated.  Values for radius less than "
  156.               "1.0 are invalid as they will generate spurious "
  157.               "results.",
  158.               "Spencer Kimball & Peter Mattis",
  159.               "Spencer Kimball & Peter Mattis",
  160.               "1995-1996",
  161.               NULL,
  162.               "RGB*, GRAY*",
  163.               GIMP_PLUGIN,
  164.               nargs, 0,
  165.               args, NULL);
  166.  
  167.   gimp_install_procedure ("plug_in_gauss_iir2",
  168.               "Applies a gaussian blur to the specified drawable.",
  169.               "Applies a gaussian blur to the drawable, with "
  170.               "specified radius of affect.  The standard deviation "
  171.               "of the normal distribution used to modify pixel "
  172.               "values is calculated based on the supplied radius.  "
  173.               "This radius can be specified indepently on for the "
  174.               "horizontal and the vertical direction. The IIR "
  175.               "gaussian blurring works best for large radius "
  176.               "values and for images which are not "
  177.               "computer-generated.  Values for radii less than "
  178.               "1.0 would generate spurious results. Therefore "
  179.               "they are interpreted as 0.0, which means that the "
  180.               "computation for this orientation is skipped.",
  181.               "Spencer Kimball, Peter Mattis & Sven Neumann",
  182.               "Spencer Kimball, Peter Mattis & Sven Neumann",
  183.               "1995-2000",
  184.               N_("<Image>/Filters/Blur/Gaussian Blur (IIR)..."),
  185.               "RGB*, GRAY*",
  186.               GIMP_PLUGIN,
  187.               nargs2, 0,
  188.               args2, NULL);
  189. }
  190.  
  191. static void
  192. run (gchar      *name,
  193.      gint        nparams,
  194.      GimpParam  *param,
  195.      gint       *nreturn_vals,
  196.      GimpParam **return_vals)
  197. {
  198.   static GimpParam values[1];
  199.   gint32 image_ID;
  200.   GimpDrawable *drawable;
  201.   GimpRunModeType run_mode;
  202.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  203.  
  204.   run_mode = param[0].data.d_int32;
  205.  
  206.   *nreturn_vals = 1;
  207.   *return_vals = values;
  208.  
  209.   values[0].type = GIMP_PDB_STATUS;
  210.   values[0].data.d_status = status;
  211.  
  212.   /*  Get the specified image and drawable  */
  213.   image_ID = param[1].data.d_image;
  214.   drawable = gimp_drawable_get (param[2].data.d_drawable);
  215.  
  216.   if (strcmp (name, "plug_in_gauss_iir") == 0)   /* the old-fashioned way of calling it */
  217.     {
  218.       switch (run_mode)
  219.     {
  220.     case GIMP_RUN_INTERACTIVE:
  221.       INIT_I18N_UI();
  222.       /*  Possibly retrieve data  */
  223.       gimp_get_data ("plug_in_gauss_iir", &bvals);
  224.       
  225.       /*  First acquire information with a dialog  */
  226.       if (! gauss_iir_dialog ())
  227.         return;
  228.       break;
  229.       
  230.     case GIMP_RUN_NONINTERACTIVE:
  231.       /*  Make sure all the arguments are there!  */
  232.       if (nparams != 6)
  233.         status = GIMP_PDB_CALLING_ERROR;
  234.       if (status == GIMP_PDB_SUCCESS)
  235.         {
  236.           bvals.radius     = param[3].data.d_float;
  237.           bvals.horizontal = (param[4].data.d_int32) ? TRUE : FALSE;
  238.           bvals.vertical   = (param[5].data.d_int32) ? TRUE : FALSE;
  239.         }
  240.       if (status == GIMP_PDB_SUCCESS && (bvals.radius < 1.0))
  241.         status = GIMP_PDB_CALLING_ERROR;
  242.       INIT_I18N();
  243.       break;
  244.       
  245.     case GIMP_RUN_WITH_LAST_VALS:
  246.       INIT_I18N();
  247.       /*  Possibly retrieve data  */
  248.       gimp_get_data ("plug_in_gauss_iir", &bvals);
  249.       break;
  250.       
  251.     default:
  252.       break;
  253.     }
  254.  
  255.       if (!(bvals.horizontal || bvals.vertical))
  256.     {
  257.       gimp_message ( _("gauss_iir: you must specify either horizontal or vertical (or both)"));
  258.       status = GIMP_PDB_CALLING_ERROR;
  259.     }
  260.     }
  261.   else if (strcmp (name, "plug_in_gauss_iir2") == 0)
  262.     {
  263.       switch (run_mode)
  264.     {      
  265.     case GIMP_RUN_INTERACTIVE:
  266.       INIT_I18N_UI();
  267.       /*  Possibly retrieve data  */
  268.       gimp_get_data ("plug_in_gauss_iir2", &b2vals);
  269.       
  270.       /*  First acquire information with a dialog  */
  271.       if (! gauss_iir2_dialog (image_ID, drawable))
  272.         return;
  273.       break;
  274.     case GIMP_RUN_NONINTERACTIVE:
  275.       INIT_I18N();
  276.       /*  Make sure all the arguments are there!  */
  277.       if (nparams != 5)
  278.         status = GIMP_PDB_CALLING_ERROR;
  279.       if (status == GIMP_PDB_SUCCESS)
  280.         {
  281.           b2vals.horizontal = param[3].data.d_float;
  282.           b2vals.vertical   = param[4].data.d_float;
  283.         }
  284.       if (status == GIMP_PDB_SUCCESS && (b2vals.horizontal < 1.0 && b2vals.vertical < 1.0))
  285.         status = GIMP_PDB_CALLING_ERROR;
  286.       break;
  287.       
  288.     case GIMP_RUN_WITH_LAST_VALS:
  289.       INIT_I18N();
  290.       /*  Possibly retrieve data  */
  291.       gimp_get_data ("plug_in_gauss_iir2", &b2vals);
  292.       break;
  293.       
  294.     default:
  295.       break;
  296.     }
  297.     }
  298.   else
  299.     status = GIMP_PDB_CALLING_ERROR;
  300.  
  301.   if (status == GIMP_PDB_SUCCESS)
  302.     {
  303.       /*  Make sure that the drawable is gray or RGB color  */
  304.       if (gimp_drawable_is_rgb (drawable->id) ||
  305.       gimp_drawable_is_gray (drawable->id))
  306.     {
  307.       gimp_progress_init ( _("IIR Gaussian Blur"));
  308.       
  309.           /*  set the tile cache size so that the gaussian blur works well  */
  310.           gimp_tile_cache_ntiles (2 * (MAX (drawable->width, drawable->height) /
  311.                   gimp_tile_width () + 1));
  312.  
  313.           /*  run the gaussian blur  */
  314.       if (strcmp (name, "plug_in_gauss_iir") == 0)
  315.         {
  316.           gauss_iir (drawable, (bvals.horizontal ? bvals.radius : 0.0), 
  317.                                    (bvals.vertical ? bvals.radius : 0.0));
  318.           
  319.           /*  Store data  */
  320.           if (run_mode == GIMP_RUN_INTERACTIVE)
  321.         gimp_set_data ("plug_in_gauss_iir", &bvals, sizeof (BlurValues));
  322.         } 
  323.       else
  324.         {
  325.           gauss_iir (drawable, b2vals.horizontal, b2vals.vertical);
  326.       
  327.           /*  Store data  */
  328.           if (run_mode == GIMP_RUN_INTERACTIVE)
  329.         gimp_set_data ("plug_in_gauss_iir2", &b2vals, sizeof (Blur2Values));
  330.         }
  331.  
  332.           if (run_mode != GIMP_RUN_NONINTERACTIVE)
  333.         gimp_displays_flush ();
  334.         }
  335.       else
  336.         {
  337.           gimp_message ( "gauss_iir: cannot operate on indexed color images");
  338.           status = GIMP_PDB_EXECUTION_ERROR;
  339.         }
  340.  
  341.       gimp_drawable_detach (drawable);
  342.     }
  343.  
  344.   values[0].data.d_status = status;
  345. }
  346.  
  347. static gint
  348. gauss_iir_dialog (void)
  349. {
  350.   GtkWidget *dlg;
  351.   GtkWidget *label;
  352.   GtkWidget *spinbutton;
  353.   GtkObject *adj;
  354.   GtkWidget *toggle;
  355.   GtkWidget *frame;
  356.   GtkWidget *vbox;
  357.   GtkWidget *hbox;
  358.  
  359.   gimp_ui_init ("gauss_iir", FALSE);
  360.  
  361.   dlg = gimp_dialog_new (_("IIR Gaussian Blur"), "gauss_iir",
  362.              gimp_standard_help_func, "filters/gauss_iir.html",
  363.              GTK_WIN_POS_MOUSE,
  364.              FALSE, TRUE, FALSE,
  365.  
  366.              _("OK"), gauss_ok_callback,
  367.              NULL, NULL, NULL, TRUE, FALSE,
  368.              _("Cancel"), gtk_widget_destroy,
  369.              NULL, 1, NULL, FALSE, TRUE,
  370.  
  371.              NULL);
  372.  
  373.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  374.               GTK_SIGNAL_FUNC (gtk_main_quit),
  375.               NULL);
  376.  
  377.   /*  parameter settings  */
  378.   frame = gtk_frame_new (_("Parameter Settings"));
  379.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  380.   gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
  381.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
  382.  
  383.   vbox = gtk_vbox_new (FALSE, 2);
  384.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
  385.   gtk_container_add (GTK_CONTAINER (frame), vbox);
  386.  
  387.   toggle = gtk_check_button_new_with_label (_("Blur Horizontally"));
  388.   gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
  389.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  390.               (GtkSignalFunc) gimp_toggle_button_update,
  391.               &bvals.horizontal);
  392.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), bvals.horizontal);
  393.   gtk_widget_show (toggle);
  394.  
  395.   toggle = gtk_check_button_new_with_label (_("Blur Vertically"));
  396.   gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
  397.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  398.               (GtkSignalFunc) gimp_toggle_button_update,
  399.               &bvals.vertical);
  400.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), bvals.vertical);
  401.   gtk_widget_show (toggle);
  402.  
  403.   hbox = gtk_hbox_new (FALSE, 4);
  404.   gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
  405.  
  406.   label = gtk_label_new (_("Blur Radius:"));
  407.   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
  408.   gtk_widget_show (label);
  409.  
  410.   spinbutton = gimp_spin_button_new (&adj,
  411.                      bvals.radius, 1.0, GIMP_MAX_IMAGE_SIZE,
  412.                      1.0, 5.0, 0, 1, 2);
  413.   gtk_box_pack_start (GTK_BOX (hbox), spinbutton, TRUE, TRUE, 0);
  414.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  415.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  416.               &bvals.radius);
  417.   gtk_widget_show (spinbutton);
  418.  
  419.   gtk_widget_show (hbox);
  420.   gtk_widget_show (vbox);
  421.   gtk_widget_show (frame);
  422.   gtk_widget_show (dlg);
  423.  
  424.   gtk_main ();
  425.   gdk_flush ();
  426.  
  427.   return bint.run;
  428. }
  429.  
  430.  
  431. static gint
  432. gauss_iir2_dialog (gint32        image_ID,
  433.            GimpDrawable *drawable)
  434. {
  435.   GtkWidget *dlg;
  436.   GtkWidget *frame;
  437.   GtkWidget *size;
  438.   GimpUnit   unit;
  439.   gdouble    xres;
  440.   gdouble    yres;
  441.  
  442.   gimp_ui_init ("gauss_iir2", FALSE);
  443.  
  444.   dlg = gimp_dialog_new (_("IIR Gaussian Blur"), "gauss_iir",
  445.              gimp_standard_help_func, "filters/gauss_iir.html",
  446.              GTK_WIN_POS_MOUSE,
  447.              FALSE, TRUE, FALSE,
  448.  
  449.              _("OK"), gauss_ok_callback,
  450.              NULL, NULL, NULL, TRUE, FALSE,
  451.              _("Cancel"), gtk_widget_destroy,
  452.              NULL, 1, NULL, FALSE, TRUE,
  453.  
  454.              NULL);
  455.  
  456.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  457.               GTK_SIGNAL_FUNC (gtk_main_quit),
  458.               NULL);
  459.  
  460.   /*  parameter settings  */
  461.   frame = gtk_frame_new (_("Blur Radius"));
  462.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  463.   gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
  464.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
  465.  
  466.   /*  Get the image resolution and unit  */
  467.   gimp_image_get_resolution (image_ID, &xres, &yres);
  468.   unit = gimp_image_get_unit (image_ID);
  469.  
  470.   size = gimp_coordinates_new (unit, "%a", TRUE, FALSE, 75, 
  471.                    GIMP_SIZE_ENTRY_UPDATE_SIZE,
  472.  
  473.                    b2vals.horizontal == b2vals.vertical,
  474.                    FALSE,
  475.  
  476.                    _("Horizontal:"), b2vals.horizontal, xres,
  477.                    0, 8 * MAX (drawable->width, drawable->height),
  478.                    0, 0,
  479.  
  480.                    _("Vertical:"), b2vals.vertical, yres,
  481.                    0, 8 * MAX (drawable->width, drawable->height),
  482.                    0, 0);
  483.   gtk_container_set_border_width (GTK_CONTAINER (size), 4);
  484.   gtk_container_add (GTK_CONTAINER (frame), size);
  485.  
  486.   gtk_widget_show (size);
  487.   gtk_widget_show (frame);
  488.   gtk_widget_show (dlg);
  489.  
  490.   bint.size = size;
  491.  
  492.   gtk_main ();
  493.   gdk_flush ();
  494.  
  495.   return bint.run;
  496. }
  497.  
  498. static void
  499. gauss_ok_callback (GtkWidget *widget,
  500.            gpointer   data)
  501. {
  502.   b2vals.horizontal =
  503.     gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (bint.size), 0);
  504.   b2vals.vertical =
  505.     gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (bint.size), 1);
  506.  
  507.   bint.run = TRUE;
  508.  
  509.   gtk_widget_destroy (GTK_WIDGET (data));
  510. }
  511.  
  512.  
  513. /* Convert from separated to premultiplied alpha, on a single scan line. */
  514. static void
  515. multiply_alpha (guchar *buf,
  516.         gint    width,
  517.         gint    bytes)
  518. {
  519.   gint i, j;
  520.   gdouble alpha;
  521.  
  522.   for (i = 0; i < width * bytes; i += bytes)
  523.     {
  524.       alpha = buf[i + bytes - 1] * (1.0 / 255.0);
  525.       for (j = 0; j < bytes - 1; j++)
  526.     buf[i + j] *= alpha;
  527.     }
  528. }
  529.  
  530. /* Convert from premultiplied to separated alpha, on a single scan
  531.    line. */
  532. static void
  533. separate_alpha (guchar *buf,
  534.         gint    width,
  535.         gint    bytes)
  536. {
  537.   gint i, j;
  538.   guchar alpha;
  539.   gdouble recip_alpha;
  540.   gint new_val;
  541.  
  542.   for (i = 0; i < width * bytes; i += bytes)
  543.     {
  544.       alpha = buf[i + bytes - 1];
  545.       if (alpha != 0 && alpha != 255)
  546.     {
  547.       recip_alpha = 255.0 / alpha;
  548.       for (j = 0; j < bytes - 1; j++)
  549.         {
  550.           new_val = buf[i + j] * recip_alpha;
  551.           buf[i + j] = MIN (255, new_val);
  552.         }
  553.     }
  554.     }
  555. }
  556.  
  557. static void
  558. gauss_iir (GimpDrawable *drawable,
  559.        gdouble       horz,
  560.        gdouble       vert)
  561. {
  562.   GimpPixelRgn src_rgn, dest_rgn;
  563.   gint width, height;
  564.   gint bytes;
  565.   gint has_alpha;
  566.   guchar *dest;
  567.   guchar *src, *sp_p, *sp_m;
  568.   gdouble n_p[5], n_m[5];
  569.   gdouble d_p[5], d_m[5];
  570.   gdouble bd_p[5], bd_m[5];
  571.   gdouble *val_p, *val_m, *vp, *vm;
  572.   gint x1, y1, x2, y2;
  573.   gint i, j;
  574.   gint row, col, b;
  575.   gint terms;
  576.   gint progress, max_progress;
  577.   gint initial_p[4];
  578.   gint initial_m[4];
  579.   guchar *guc_tmp1, *guc_tmp2;
  580.   gint *gi_tmp1, *gi_tmp2;
  581.   gdouble std_dev;
  582.  
  583.   gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2);
  584.  
  585.   if (horz < 1.0 && vert < 1.0)
  586.     return;
  587.  
  588.   width = (x2 - x1);
  589.   height = (y2 - y1);
  590.   bytes = drawable->bpp;
  591.   has_alpha = gimp_drawable_has_alpha(drawable->id);
  592.  
  593.   val_p = g_new (gdouble, MAX (width, height) * bytes);
  594.   val_m = g_new (gdouble, MAX (width, height) * bytes);
  595.  
  596.   src =  g_new (guchar, MAX (width, height) * bytes);
  597.   dest = g_new (guchar, MAX (width, height) * bytes);
  598.  
  599.   gimp_pixel_rgn_init (&src_rgn, drawable, 0, 0, drawable->width, drawable->height, FALSE, FALSE);
  600.   gimp_pixel_rgn_init (&dest_rgn, drawable, 0, 0, drawable->width, drawable->height, TRUE, TRUE);
  601.  
  602.   progress = 0;
  603.   max_progress = (horz < 1.0 ) ? 0 : width * height * horz;
  604.   max_progress += (vert < 1.0 ) ? 0 : width * height * vert;
  605.  
  606.   /*  First the vertical pass  */
  607.   if (vert >= 1.0)
  608.     {
  609.       vert = fabs (vert) + 1.0;
  610.       std_dev = sqrt (-(vert * vert) / (2 * log (1.0 / 255.0)));
  611.  
  612.       /*  derive the constants for calculating the gaussian from the std dev  */
  613.       find_constants (n_p, n_m, d_p, d_m, bd_p, bd_m, std_dev);
  614.  
  615.       for (col = 0; col < width; col++)
  616.     {
  617.       memset(val_p, 0, height * bytes * sizeof (gdouble));
  618.       memset(val_m, 0, height * bytes * sizeof (gdouble));
  619.  
  620.       gimp_pixel_rgn_get_col (&src_rgn, src, col + x1, y1, (y2 - y1));
  621.  
  622.       if (has_alpha)
  623.         multiply_alpha (src, height, bytes);
  624.  
  625.       sp_p = src;
  626.       sp_m = src + (height - 1) * bytes;
  627.       vp = val_p;
  628.       vm = val_m + (height - 1) * bytes;
  629.  
  630.       /*  Set up the first vals  */
  631. #ifndef ORIGINAL_READABLE_CODE
  632.       for(guc_tmp1 = sp_p, guc_tmp2 = sp_m,
  633.         gi_tmp1 = initial_p, gi_tmp2 = initial_m;
  634.           (guc_tmp1 - sp_p) < bytes;)
  635.         {
  636.           *gi_tmp1++ = *guc_tmp1++;
  637.           *gi_tmp2++ = *guc_tmp2++;
  638.         }
  639. #else
  640.       for (i = 0; i < bytes; i++)
  641.         {
  642.           initial_p[i] = sp_p[i];
  643.           initial_m[i] = sp_m[i];
  644.         }
  645. #endif
  646.  
  647.       for (row = 0; row < height; row++)
  648.         {
  649.           gdouble *vpptr, *vmptr;
  650.           terms = (row < 4) ? row : 4;
  651.  
  652.           for (b = 0; b < bytes; b++)
  653.         {
  654.           vpptr = vp + b; vmptr = vm + b;
  655.           for (i = 0; i <= terms; i++)
  656.             {
  657.               *vpptr += n_p[i] * sp_p[(-i * bytes) + b] -
  658.             d_p[i] * vp[(-i * bytes) + b];
  659.               *vmptr += n_m[i] * sp_m[(i * bytes) + b] -
  660.             d_m[i] * vm[(i * bytes) + b];
  661.             }
  662.           for (j = i; j <= 4; j++)
  663.             {
  664.               *vpptr += (n_p[j] - bd_p[j]) * initial_p[b];
  665.               *vmptr += (n_m[j] - bd_m[j]) * initial_m[b];
  666.             }
  667.         }
  668.  
  669.           sp_p += bytes;
  670.           sp_m -= bytes;
  671.           vp += bytes;
  672.           vm -= bytes;
  673.         }
  674.  
  675.       transfer_pixels (val_p, val_m, dest, bytes, height);
  676.  
  677.       if (has_alpha && !horz)
  678.         separate_alpha (dest, height, bytes);
  679.  
  680.       gimp_pixel_rgn_set_col (&dest_rgn, dest, col + x1, y1, (y2 - y1));
  681.  
  682.       progress += height * vert;
  683.       if ((col % 5) == 0)
  684.         gimp_progress_update ((double) progress / (double) max_progress);
  685.     }
  686.  
  687.       /*  prepare for the horizontal pass  */
  688.       gimp_pixel_rgn_init (&src_rgn, drawable, 0, 0, drawable->width, drawable->height, FALSE, TRUE);
  689.     }
  690.  
  691.   /*  Now the horizontal pass  */
  692.   if (horz >= 1.0)
  693.     {
  694.       horz = fabs (horz) + 1.0;
  695.  
  696.       if (horz != vert)
  697.     {
  698.       std_dev = sqrt (-(horz * horz) / (2 * log (1.0 / 255.0)));
  699.             
  700.       /*  derive the constants for calculating the gaussian from the std dev  */
  701.       find_constants (n_p, n_m, d_p, d_m, bd_p, bd_m, std_dev);
  702.     }
  703.  
  704.       for (row = 0; row < height; row++)
  705.     {
  706.       memset(val_p, 0, width * bytes * sizeof (gdouble));
  707.       memset(val_m, 0, width * bytes * sizeof (gdouble));
  708.  
  709.       gimp_pixel_rgn_get_row (&src_rgn, src, x1, row + y1, (x2 - x1));
  710.  
  711.       if (has_alpha && !vert)
  712.         multiply_alpha (src, height, bytes);
  713.  
  714.       sp_p = src;
  715.       sp_m = src + (width - 1) * bytes;
  716.       vp = val_p;
  717.       vm = val_m + (width - 1) * bytes;
  718.  
  719.       /*  Set up the first vals  */
  720. #ifndef ORIGINAL_READABLE_CODE
  721.       for(guc_tmp1 = sp_p, guc_tmp2 = sp_m,
  722.         gi_tmp1 = initial_p, gi_tmp2 = initial_m;
  723.           (guc_tmp1 - sp_p) < bytes;)
  724.         {
  725.           *gi_tmp1++ = *guc_tmp1++;
  726.           *gi_tmp2++ = *guc_tmp2++;
  727.         }
  728. #else
  729.       for (i = 0; i < bytes; i++)
  730.         {
  731.           initial_p[i] = sp_p[i];
  732.           initial_m[i] = sp_m[i];
  733.         }
  734. #endif
  735.  
  736.       for (col = 0; col < width; col++)
  737.         {
  738.           gdouble *vpptr, *vmptr;
  739.           terms = (col < 4) ? col : 4;
  740.  
  741.           for (b = 0; b < bytes; b++)
  742.         {
  743.           vpptr = vp + b; vmptr = vm + b;
  744.           for (i = 0; i <= terms; i++)
  745.             {
  746.               *vpptr += n_p[i] * sp_p[(-i * bytes) + b] -
  747.             d_p[i] * vp[(-i * bytes) + b];
  748.               *vmptr += n_m[i] * sp_m[(i * bytes) + b] -
  749.             d_m[i] * vm[(i * bytes) + b];
  750.             }
  751.           for (j = i; j <= 4; j++)
  752.             {
  753.               *vpptr += (n_p[j] - bd_p[j]) * initial_p[b];
  754.               *vmptr += (n_m[j] - bd_m[j]) * initial_m[b];
  755.             }
  756.         }
  757.  
  758.           sp_p += bytes;
  759.           sp_m -= bytes;
  760.           vp += bytes;
  761.           vm -= bytes;
  762.         }
  763.  
  764.       transfer_pixels (val_p, val_m, dest, bytes, width);
  765.  
  766.       if (has_alpha)
  767.         separate_alpha (dest, width, bytes);
  768.  
  769.       gimp_pixel_rgn_set_row (&dest_rgn, dest, x1, row + y1, (x2 - x1));
  770.  
  771.       progress += width * horz;
  772.       if ((row % 5) == 0)
  773.         gimp_progress_update ((double) progress / (double) max_progress);
  774.     }
  775.     }
  776.  
  777.   /*  merge the shadow, update the drawable  */
  778.   gimp_drawable_flush (drawable);
  779.   gimp_drawable_merge_shadow (drawable->id, TRUE);
  780.   gimp_drawable_update (drawable->id, x1, y1, (x2 - x1), (y2 - y1));
  781.  
  782.   /*  free up buffers  */
  783.   g_free (val_p);
  784.   g_free (val_m);
  785.   g_free (src);
  786.   g_free (dest);
  787. }
  788.  
  789. static void
  790. transfer_pixels (gdouble *src1,
  791.          gdouble *src2,
  792.          guchar  *dest,
  793.          gint     bytes,
  794.          gint     width)
  795. {
  796.   gint b;
  797.   gint bend = bytes * width;
  798.   gdouble sum;
  799.  
  800.   for(b = 0; b < bend; b++)
  801.     {
  802.       sum = *src1++ + *src2++;
  803.       if (sum > 255) sum = 255;
  804.       else if(sum < 0) sum = 0;
  805.       
  806.       *dest++ = (guchar) sum;
  807.     }
  808. }
  809.  
  810. static void
  811. find_constants (gdouble n_p[],
  812.         gdouble n_m[],
  813.         gdouble d_p[],
  814.         gdouble d_m[],
  815.         gdouble bd_p[],
  816.         gdouble bd_m[],
  817.         gdouble std_dev)
  818. {
  819.   gint i;
  820.   gdouble constants [8];
  821.   gdouble div;
  822.  
  823.   /*  The constants used in the implemenation of a casual sequence
  824.    *  using a 4th order approximation of the gaussian operator
  825.    */
  826.  
  827.   div = sqrt(2 * G_PI) * std_dev;
  828.   constants [0] = -1.783 / std_dev;
  829.   constants [1] = -1.723 / std_dev;
  830.   constants [2] = 0.6318 / std_dev;
  831.   constants [3] = 1.997  / std_dev;
  832.   constants [4] = 1.6803 / div;
  833.   constants [5] = 3.735 / div;
  834.   constants [6] = -0.6803 / div;
  835.   constants [7] = -0.2598 / div;
  836.  
  837.   n_p [0] = constants[4] + constants[6];
  838.   n_p [1] = exp (constants[1]) *
  839.     (constants[7] * sin (constants[3]) -
  840.      (constants[6] + 2 * constants[4]) * cos (constants[3])) +
  841.        exp (constants[0]) *
  842.      (constants[5] * sin (constants[2]) -
  843.       (2 * constants[6] + constants[4]) * cos (constants[2]));
  844.   n_p [2] = 2 * exp (constants[0] + constants[1]) *
  845.     ((constants[4] + constants[6]) * cos (constants[3]) * cos (constants[2]) -
  846.      constants[5] * cos (constants[3]) * sin (constants[2]) -
  847.      constants[7] * cos (constants[2]) * sin (constants[3])) +
  848.        constants[6] * exp (2 * constants[0]) +
  849.      constants[4] * exp (2 * constants[1]);
  850.   n_p [3] = exp (constants[1] + 2 * constants[0]) *
  851.     (constants[7] * sin (constants[3]) - constants[6] * cos (constants[3])) +
  852.       exp (constants[0] + 2 * constants[1]) *
  853.     (constants[5] * sin (constants[2]) - constants[4] * cos (constants[2]));
  854.   n_p [4] = 0.0;
  855.  
  856.   d_p [0] = 0.0;
  857.   d_p [1] = -2 * exp (constants[1]) * cos (constants[3]) -
  858.     2 * exp (constants[0]) * cos (constants[2]);
  859.   d_p [2] = 4 * cos (constants[3]) * cos (constants[2]) * exp (constants[0] + constants[1]) +
  860.     exp (2 * constants[1]) + exp (2 * constants[0]);
  861.   d_p [3] = -2 * cos (constants[2]) * exp (constants[0] + 2 * constants[1]) -
  862.     2 * cos (constants[3]) * exp (constants[1] + 2 * constants[0]);
  863.   d_p [4] = exp (2 * constants[0] + 2 * constants[1]);
  864.  
  865. #ifndef ORIGINAL_READABLE_CODE
  866.   memcpy(d_m, d_p, 5 * sizeof(gdouble));
  867. #else
  868.   for (i = 0; i <= 4; i++)
  869.     d_m [i] = d_p [i];
  870. #endif
  871.  
  872.   n_m[0] = 0.0;
  873.   for (i = 1; i <= 4; i++)
  874.     n_m [i] = n_p[i] - d_p[i] * n_p[0];
  875.  
  876.   {
  877.     gdouble sum_n_p, sum_n_m, sum_d;
  878.     gdouble a, b;
  879.  
  880.     sum_n_p = 0.0;
  881.     sum_n_m = 0.0;
  882.     sum_d = 0.0;
  883.     for (i = 0; i <= 4; i++)
  884.       {
  885.     sum_n_p += n_p[i];
  886.     sum_n_m += n_m[i];
  887.     sum_d += d_p[i];
  888.       }
  889.  
  890. #ifndef ORIGINAL_READABLE_CODE
  891.     sum_d++;
  892.     a = sum_n_p / sum_d;
  893.     b = sum_n_m / sum_d;
  894. #else
  895.     a = sum_n_p / (1 + sum_d);
  896.     b = sum_n_m / (1 + sum_d);
  897. #endif
  898.  
  899.     for (i = 0; i <= 4; i++)
  900.       {
  901.     bd_p[i] = d_p[i] * a;
  902.     bd_m[i] = d_m[i] * b;
  903.       }
  904.   }
  905. }
  906.