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_rle.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-10-28  |  21.9 KB  |  841 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_rle (GimpDrawable *drawable,
  63.                 gdouble       horizontal,
  64.                 gdouble       vertical);
  65.  
  66. /*
  67.  * Gaussian blur interface
  68.  */
  69. static gint      gauss_rle_dialog  (void);
  70. static gint      gauss_rle2_dialog (gint32        image_ID, 
  71.                     GimpDrawable *drawable);
  72.  
  73. /*
  74.  * Gaussian blur helper functions
  75.  */
  76. static gint *    make_curve        (gdouble    sigma,
  77.                     gint      *length);
  78. static void      run_length_encode (guchar    *src,
  79.                     gint      *dest,
  80.                     gint       bytes,
  81.                     gint       width);
  82.  
  83. static void      gauss_ok_callback (GtkWidget *widget,
  84.                     gpointer   data);
  85.  
  86. GimpPlugInInfo PLUG_IN_INFO =
  87. {
  88.   NULL,  /* init_proc  */
  89.   NULL,  /* quit_proc  */
  90.   query, /* query_proc */
  91.   run,   /* run_proc   */
  92. };
  93.  
  94. static BlurValues bvals =
  95. {
  96.   5.0,  /*  radius           */
  97.   TRUE, /*  horizontal blur  */
  98.   TRUE  /*  vertical blur    */
  99. };
  100.  
  101. static Blur2Values b2vals =
  102. {
  103.   5.0,  /*  x radius  */
  104.   5.0   /*  y radius  */
  105. };
  106.  
  107. static BlurInterface bint =
  108. {
  109.   FALSE  /*  run  */
  110. };
  111.  
  112.  
  113. MAIN ()
  114.  
  115. static void
  116. query (void)
  117. {
  118.   static GimpParamDef args[] =
  119.   {
  120.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  121.     { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
  122.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  123.     { GIMP_PDB_FLOAT, "radius", "Radius of gaussian blur (in pixels > 1.0)" },
  124.     { GIMP_PDB_INT32, "horizontal", "Blur in horizontal direction" },
  125.     { GIMP_PDB_INT32, "vertical", "Blur in vertical direction" }
  126.   };
  127.   static gint nargs = sizeof (args) / sizeof (args[0]);
  128.  
  129.   static GimpParamDef args2[] =
  130.   {
  131.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  132.     { GIMP_PDB_IMAGE, "image", "Input image" },
  133.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  134.     { GIMP_PDB_FLOAT, "horizontal", "Horizontal radius of gaussian blur (in pixels)" },
  135.     { GIMP_PDB_FLOAT, "vertical",   "Vertical radius of gaussian blur (in pixels)" }
  136.   };
  137.   static gint nargs2 = sizeof (args2) / sizeof (args2[0]);
  138.  
  139.   gimp_install_procedure ("plug_in_gauss_rle",
  140.               "Applies a gaussian blur to the specified drawable.",
  141.               "Applies a gaussian blur to the drawable, with "
  142.               "specified radius of affect.  The standard deviation "
  143.               "of the normal distribution used to modify pixel "
  144.               "values is calculated based on the supplied radius.  "
  145.               "Horizontal and vertical blurring can be "
  146.               "independently invoked by specifying only one to "
  147.               "run.  The RLE gaussian blurring performs most "
  148.               "efficiently on computer-generated images or images "
  149.               "with large areas of constant intensity.  Values for "
  150.               "radii less than 1.0 are invalid as they would "
  151.               "generate spurious results.",
  152.               "Spencer Kimball & Peter Mattis",
  153.               "Spencer Kimball & Peter Mattis",
  154.               "1995-1996",
  155.               NULL,
  156.               "RGB*, GRAY*",
  157.               GIMP_PLUGIN,
  158.               nargs, 0,
  159.               args, NULL);
  160.  
  161.   gimp_install_procedure ("plug_in_gauss_rle2",
  162.               "Applies a gaussian blur to the specified drawable.",
  163.               "Applies a gaussian blur to the drawable, with "
  164.               "specified radius of affect.  The standard deviation "
  165.               "of the normal distribution used to modify pixel "
  166.               "values is calculated based on the supplied radius.  "
  167.               "This radius can be specified indepently on for the "
  168.               "horizontal and the vertical direction. The RLE "
  169.               "gaussian blurring performs most efficiently on "
  170.               "computer-generated images or images with large "
  171.               "areas of constant intensity.  Values for radii "
  172.               "less than 1.0 would generate spurious results. "
  173.               "Therefore they are interpreted as 0.0, which means "
  174.               "that the computation for this orientation is "
  175.               "skipped.",
  176.               "Spencer Kimball, Peter Mattis & Sven Neumann",
  177.               "Spencer Kimball, Peter Mattis & Sven Neumann",
  178.               "1995-2000",
  179.               N_("<Image>/Filters/Blur/Gaussian Blur (RLE)..."),
  180.               "RGB*, GRAY*",
  181.               GIMP_PLUGIN,
  182.               nargs2, 0,
  183.               args2, NULL);
  184. }
  185.  
  186. static void
  187. run (gchar      *name,
  188.      gint        nparams,
  189.      GimpParam  *param,
  190.      gint       *nreturn_vals,
  191.      GimpParam **return_vals)
  192. {
  193.   static GimpParam values[1];
  194.   gint32 image_ID;
  195.   GimpDrawable *drawable;
  196.   GimpRunModeType run_mode;
  197.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  198.  
  199.   run_mode = param[0].data.d_int32;
  200.  
  201.   *nreturn_vals = 1;
  202.   *return_vals = values;
  203.  
  204.   values[0].type = GIMP_PDB_STATUS;
  205.   values[0].data.d_status = status;
  206.  
  207.   /*  Get the specified image and drawable  */
  208.   image_ID = param[1].data.d_image;
  209.   drawable = gimp_drawable_get (param[2].data.d_drawable);
  210.  
  211.   if (strcmp (name, "plug_in_gauss_rle") == 0)   /* the old-fashioned way of calling it */
  212.     {
  213.       switch (run_mode)
  214.     {      
  215.     case GIMP_RUN_INTERACTIVE:
  216.       INIT_I18N_UI();
  217.       /*  Possibly retrieve data  */
  218.       gimp_get_data ("plug_in_gauss_rle", &bvals);
  219.  
  220.       /*  First acquire information with a dialog  */
  221.       if (! gauss_rle_dialog ())
  222.         return;
  223.       break;
  224.     case GIMP_RUN_NONINTERACTIVE:
  225.       INIT_I18N();
  226.       /*  Make sure all the arguments are there!  */
  227.       if (nparams != 6)
  228.         status = GIMP_PDB_CALLING_ERROR;
  229.       if (status == GIMP_PDB_SUCCESS)
  230.         {
  231.           bvals.radius = param[3].data.d_float;
  232.           bvals.horizontal = (param[4].data.d_int32) ? TRUE : FALSE;
  233.           bvals.vertical = (param[5].data.d_int32) ? TRUE : FALSE;
  234.         }
  235.       if (status == GIMP_PDB_SUCCESS && (bvals.radius < 1.0))
  236.         status = GIMP_PDB_CALLING_ERROR;
  237.       break;
  238.       
  239.     case GIMP_RUN_WITH_LAST_VALS:
  240.       INIT_I18N();
  241.       /*  Possibly retrieve data  */
  242.       gimp_get_data ("plug_in_gauss_rle", &bvals);
  243.       break;
  244.       
  245.     default:
  246.       break;
  247.     }
  248.       
  249.       if (!(bvals.horizontal || bvals.vertical))
  250.     {
  251.       g_message ( _("gauss_rle: you must specify either horizontal or vertical (or both)"));
  252.       status = GIMP_PDB_CALLING_ERROR;
  253.     }
  254.  
  255.     } 
  256.   else if (strcmp (name, "plug_in_gauss_rle2") == 0)
  257.     {
  258.       switch (run_mode)
  259.     {      
  260.     case GIMP_RUN_INTERACTIVE:
  261.       INIT_I18N_UI();
  262.       /*  Possibly retrieve data  */
  263.       gimp_get_data ("plug_in_gauss_rle2", &b2vals);
  264.       
  265.       /*  First acquire information with a dialog  */
  266.       if (! gauss_rle2_dialog (image_ID, drawable))
  267.         return;
  268.       break;
  269.     case GIMP_RUN_NONINTERACTIVE:
  270.       INIT_I18N();
  271.       /*  Make sure all the arguments are there!  */
  272.       if (nparams != 5)
  273.         status = GIMP_PDB_CALLING_ERROR;
  274.       if (status == GIMP_PDB_SUCCESS)
  275.         {
  276.           b2vals.horizontal = param[3].data.d_float;
  277.           b2vals.vertical   = param[4].data.d_float;
  278.         }
  279.       if (status == GIMP_PDB_SUCCESS && (b2vals.horizontal < 1.0 && b2vals.vertical < 1.0))
  280.         status = GIMP_PDB_CALLING_ERROR;
  281.       break;
  282.       
  283.     case GIMP_RUN_WITH_LAST_VALS:
  284.       INIT_I18N();
  285.       /*  Possibly retrieve data  */
  286.       gimp_get_data ("plug_in_gauss_rle2", &b2vals);
  287.       break;
  288.  
  289.     default:
  290.       break;
  291.     }
  292.     }    
  293.   else
  294.     status = GIMP_PDB_CALLING_ERROR;
  295.  
  296.   if (status == GIMP_PDB_SUCCESS)
  297.     {
  298.       /*  Make sure that the drawable is gray or RGB color  */
  299.       if (gimp_drawable_is_rgb (drawable->id) ||
  300.           gimp_drawable_is_gray (drawable->id))
  301.         {
  302.           gimp_progress_init ( _("RLE Gaussian Blur"));
  303.  
  304.           /*  set the tile cache size so that the gaussian blur works well  */
  305.           gimp_tile_cache_ntiles (2 * (MAX (drawable->width, drawable->height) /
  306.                   gimp_tile_width () + 1));
  307.  
  308.           /*  run the gaussian blur  */
  309.       if (strcmp (name, "plug_in_gauss_rle") == 0)
  310.         {
  311.           gauss_rle (drawable, (bvals.horizontal ? bvals.radius : 0.0), 
  312.                                    (bvals.vertical ? bvals.radius : 0.0));
  313.           
  314.           /*  Store data  */
  315.           if (run_mode == GIMP_RUN_INTERACTIVE)
  316.         gimp_set_data ("plug_in_gauss_rle", &bvals, sizeof (BlurValues));
  317.         } 
  318.       else
  319.         {
  320.           gauss_rle (drawable, b2vals.horizontal, b2vals.vertical);
  321.       
  322.           /*  Store data  */
  323.           if (run_mode == GIMP_RUN_INTERACTIVE)
  324.         gimp_set_data ("plug_in_gauss_rle2", &b2vals, sizeof (Blur2Values));
  325.         }
  326.  
  327.           if (run_mode != GIMP_RUN_NONINTERACTIVE)
  328.         gimp_displays_flush ();
  329.         }
  330.       else
  331.         {
  332.           gimp_message ( "gauss_rle: cannot operate on indexed color images");
  333.           status = GIMP_PDB_EXECUTION_ERROR;
  334.         }
  335.  
  336.     gimp_drawable_detach (drawable);
  337.   }
  338.  
  339.   values[0].data.d_status = status;
  340. }
  341.  
  342. static gint
  343. gauss_rle_dialog (void)
  344. {
  345.   GtkWidget *dlg;
  346.   GtkWidget *label;
  347.   GtkWidget *spinbutton;
  348.   GtkObject *adj;
  349.   GtkWidget *toggle;
  350.   GtkWidget *frame;
  351.   GtkWidget *vbox;
  352.   GtkWidget *hbox;
  353.  
  354.   gimp_ui_init ("gauss_rle", FALSE);
  355.  
  356.   dlg = gimp_dialog_new (_("RLE Gaussian Blur"), "gauss_rle",
  357.              gimp_standard_help_func, "filters/gauss_rle.html",
  358.              GTK_WIN_POS_MOUSE,
  359.              TRUE, FALSE, TRUE,
  360.  
  361.              _("OK"), gauss_ok_callback,
  362.              NULL, NULL, NULL, TRUE, FALSE,
  363.              _("Cancel"), gtk_widget_destroy,
  364.              NULL, 1, NULL, FALSE, TRUE,
  365.  
  366.              NULL);
  367.  
  368.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  369.               GTK_SIGNAL_FUNC (gtk_main_quit),
  370.               NULL);
  371.  
  372.   /*  parameter settings  */
  373.   frame = gtk_frame_new (_("Parameter Settings"));
  374.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  375.   gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
  376.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
  377.  
  378.   vbox = gtk_vbox_new (FALSE, 2);
  379.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
  380.   gtk_container_add (GTK_CONTAINER (frame), vbox);
  381.  
  382.   toggle = gtk_check_button_new_with_label (_("Blur Horizontally"));
  383.   gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
  384.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  385.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  386.               &bvals.horizontal);
  387.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), bvals.horizontal);
  388.   gtk_widget_show (toggle);
  389.  
  390.   toggle = gtk_check_button_new_with_label (_("Blur Vertically"));
  391.   gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
  392.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  393.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  394.               &bvals.vertical);
  395.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), bvals.vertical);
  396.   gtk_widget_show (toggle);
  397.  
  398.   hbox = gtk_hbox_new (FALSE, 4);
  399.   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  400.  
  401.   label = gtk_label_new (_("Blur Radius:"));
  402.   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
  403.   gtk_widget_show (label);
  404.  
  405.   spinbutton = gimp_spin_button_new (&adj,
  406.                      bvals.radius, 1.0, GIMP_MAX_IMAGE_SIZE,
  407.                      1.0, 5.0, 0, 1, 2);
  408.   gtk_box_pack_start (GTK_BOX (hbox), spinbutton, TRUE, TRUE, 0);
  409.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  410.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  411.               &bvals.radius);
  412.   gtk_widget_show (spinbutton);
  413.  
  414.   gtk_widget_show (hbox);
  415.   gtk_widget_show (vbox);
  416.   gtk_widget_show (frame);
  417.   gtk_widget_show (dlg);
  418.  
  419.   gtk_main ();
  420.   gdk_flush ();
  421.  
  422.   return bint.run;
  423. }
  424.  
  425.  
  426. static gint
  427. gauss_rle2_dialog (gint32        image_ID,
  428.            GimpDrawable *drawable)
  429. {
  430.   GtkWidget *dlg;
  431.   GtkWidget *frame;
  432.   GtkWidget *size;
  433.   GimpUnit   unit;
  434.   gdouble    xres;
  435.   gdouble    yres;
  436.  
  437.   gimp_ui_init ("gauss_rle2", FALSE);
  438.  
  439.   dlg = gimp_dialog_new (_("RLE Gaussian Blur"), "gauss_rle",
  440.              gimp_standard_help_func, "filters/gauss_rle.html",
  441.              GTK_WIN_POS_MOUSE,
  442.              FALSE, TRUE, FALSE,
  443.  
  444.              _("OK"), gauss_ok_callback,
  445.              NULL, NULL, NULL, TRUE, FALSE,
  446.              _("Cancel"), gtk_widget_destroy,
  447.              NULL, 1, NULL, FALSE, TRUE,
  448.  
  449.              NULL);
  450.  
  451.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  452.               GTK_SIGNAL_FUNC (gtk_main_quit),
  453.               NULL);
  454.  
  455.   /*  parameter settings  */
  456.   frame = gtk_frame_new (_("Blur Radius"));
  457.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  458.   gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
  459.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
  460.  
  461.   /*  Get the image resolution and unit  */
  462.   gimp_image_get_resolution (image_ID, &xres, &yres);
  463.   unit = gimp_image_get_unit (image_ID);
  464.  
  465.   size = gimp_coordinates_new (unit, "%a", TRUE, FALSE, 75,
  466.                    GIMP_SIZE_ENTRY_UPDATE_SIZE,
  467.  
  468.                    b2vals.horizontal == b2vals.vertical,
  469.                    FALSE,
  470.  
  471.                    _("Horizontal:"), b2vals.horizontal, xres,
  472.                    0, 8 * MAX (drawable->width, drawable->height),
  473.                    0, 0,
  474.  
  475.                    _("Vertical:"), b2vals.vertical, yres,
  476.                    0, 8 * MAX (drawable->width, drawable->height),
  477.                    0, 0);
  478.   gtk_container_set_border_width (GTK_CONTAINER (size), 4);
  479.   gtk_container_add (GTK_CONTAINER (frame), size);
  480.  
  481.   gtk_widget_show (size);
  482.   gtk_widget_show (frame);
  483.   gtk_widget_show (dlg);
  484.  
  485.   bint.size = size;
  486.  
  487.   gtk_main ();
  488.   gdk_flush ();
  489.  
  490.   return bint.run;
  491. }
  492.  
  493. static void
  494. gauss_ok_callback (GtkWidget *widget,
  495.            gpointer   data)
  496. {
  497.   b2vals.horizontal =
  498.     gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (bint.size), 0);
  499.   b2vals.vertical =
  500.     gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (bint.size), 1);
  501.  
  502.   bint.run = TRUE;
  503.  
  504.   gtk_widget_destroy (GTK_WIDGET (data));
  505. }
  506.  
  507. /* Convert from separated to premultiplied alpha, on a single scan line. */
  508. static void
  509. multiply_alpha (guchar *buf,
  510.         gint    width,
  511.         gint    bytes)
  512. {
  513.   gint    i, j;
  514.   gdouble alpha;
  515.  
  516.   for (i = 0; i < width * bytes; i += bytes)
  517.     {
  518.       alpha = buf[i + bytes - 1] * (1.0 / 255.0);
  519.       for (j = 0; j < bytes - 1; j++)
  520.     buf[i + j] *= alpha;
  521.     }
  522. }
  523.  
  524. /* Convert from premultiplied to separated alpha, on a single scan
  525.    line. */
  526. static void
  527. separate_alpha (guchar *buf,
  528.         gint    width,
  529.         gint    bytes)
  530. {
  531.   gint    i, j;
  532.   guchar  alpha;
  533.   gdouble recip_alpha;
  534.   gint    new_val;
  535.  
  536.   for (i = 0; i < width * bytes; i += bytes)
  537.     {
  538.       alpha = buf[i + bytes - 1];
  539.       if (alpha != 0 && alpha != 255)
  540.     {
  541.       recip_alpha = 255.0 / alpha;
  542.       for (j = 0; j < bytes - 1; j++)
  543.         {
  544.           new_val = buf[i + j] * recip_alpha;
  545.           buf[i + j] = MIN (255, new_val);
  546.         }
  547.     }
  548.     }
  549. }
  550.  
  551. static void
  552. gauss_rle (GimpDrawable *drawable,
  553.        gdouble       horz,
  554.        gdouble       vert)
  555. {
  556.   GimpPixelRgn src_rgn, dest_rgn;
  557.   gint    width, height;
  558.   gint    bytes;
  559.   gint    has_alpha;
  560.   guchar *dest, *dp;
  561.   guchar *src, *sp;
  562.   gint   *buf, *bb;
  563.   gint    pixels;
  564.   gint    total = 1;
  565.   gint    x1, y1, x2, y2;
  566.   gint    i, row, col, b;
  567.   gint    start, end;
  568.   gint    progress, max_progress;
  569.   gint   *curve;
  570.   gint   *sum = NULL;
  571.   gint    val;
  572.   gint    length;
  573.   gint    initial_p, initial_m;
  574.   gdouble std_dev;
  575.  
  576.   if (horz < 1.0 && vert < 1.0)
  577.     return;
  578.  
  579.   gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2);
  580.  
  581.   width = (x2 - x1);
  582.   height = (y2 - y1);
  583.   bytes = drawable->bpp;
  584.   has_alpha = gimp_drawable_has_alpha(drawable->id);
  585.  
  586.   buf = g_new (gint, MAX (width, height) * 2);
  587.  
  588.   /*  allocate buffers for source and destination pixels  */
  589.   src = g_new (guchar, MAX (width, height) * bytes);
  590.   dest = g_new (guchar, MAX (width, height) * bytes);
  591.  
  592.   gimp_pixel_rgn_init (&src_rgn, drawable, 0, 0, drawable->width, drawable->height, FALSE, FALSE);
  593.   gimp_pixel_rgn_init (&dest_rgn, drawable, 0, 0, drawable->width, drawable->height, TRUE, TRUE);
  594.  
  595.   progress = 0;
  596.   max_progress = (horz < 1.0 ) ? 0 : width * height * horz;
  597.   max_progress += (vert < 1.0 ) ? 0 : width * height * vert;
  598.  
  599.   /*  First the vertical pass  */
  600.   if (vert >= 1.0)
  601.     {
  602.       vert = fabs (vert) + 1.0;
  603.       std_dev = sqrt (-(vert * vert) / (2 * log (1.0 / 255.0)));
  604.       
  605.       curve = make_curve (std_dev, &length);
  606.       sum = g_new (gint, 2 * length + 1);
  607.  
  608.       sum[0] = 0;
  609.  
  610.       for (i = 1; i <= length*2; i++)
  611.     sum[i] = curve[i-length-1] + sum[i-1];
  612.       sum += length;
  613.       
  614.       total = sum[length] - sum[-length];
  615.  
  616.        for (col = 0; col < width; col++)
  617.     {
  618.       gimp_pixel_rgn_get_col (&src_rgn, src, col + x1, y1, (y2 - y1));
  619.  
  620.       if (has_alpha)
  621.         multiply_alpha (src, height, bytes);
  622.  
  623.       sp = src;
  624.       dp = dest;
  625.  
  626.       for (b = 0; b < bytes; b++)
  627.         {
  628.           initial_p = sp[b];
  629.           initial_m = sp[(height-1) * bytes + b];
  630.  
  631.           /*  Determine a run-length encoded version of the row  */
  632.           run_length_encode (sp + b, buf, bytes, height);
  633.  
  634.           for (row = 0; row < height; row++)
  635.         {
  636.           start = (row < length) ? -row : -length;
  637.           end = (height <= (row + length)) ? (height - row - 1) : length;
  638.  
  639.           val = 0;
  640.           i = start;
  641.           bb = buf + (row + i) * 2;
  642.  
  643.           if (start != -length)
  644.             val += initial_p * (sum[start] - sum[-length]);
  645.  
  646.           while (i < end)
  647.             {
  648.               pixels = bb[0];
  649.               i += pixels;
  650.               if (i > end)
  651.             i = end;
  652.               val += bb[1] * (sum[i] - sum[start]);
  653.               bb += (pixels * 2);
  654.               start = i;
  655.             }
  656.  
  657.           if (end != length)
  658.             val += initial_m * (sum[length] - sum[end]);
  659.  
  660.           dp[row * bytes + b] = val / total;
  661.         }
  662.         }
  663.  
  664.       if (has_alpha && !horz)
  665.         separate_alpha (dest, height, bytes);
  666.  
  667.       gimp_pixel_rgn_set_col (&dest_rgn, dest, col + x1, y1, (y2 - y1));
  668.       progress += height * vert;
  669.       if ((col % 5) == 0)
  670.         gimp_progress_update ((double) progress / (double) max_progress);
  671.     }
  672.  
  673.       /*  prepare for the horizontal pass  */
  674.       gimp_pixel_rgn_init (&src_rgn, drawable, 0, 0, drawable->width, drawable->height, FALSE, TRUE);
  675.     }
  676.  
  677.   /*  Now the horizontal pass  */
  678.   if (horz >= 1.0)
  679.     {
  680.       horz = fabs (horz) + 1.0;
  681.  
  682.       if (horz != vert)
  683.     {
  684.       std_dev = sqrt (-(horz * horz) / (2 * log (1.0 / 255.0)));
  685.       
  686.       curve = make_curve (std_dev, &length);
  687.       sum = g_new (gint, 2 * length + 1);
  688.  
  689.       sum[0] = 0;
  690.  
  691.       for (i = 1; i <= length*2; i++)
  692.         sum[i] = curve[i-length-1] + sum[i-1];
  693.       sum += length;
  694.       
  695.       total = sum[length] - sum[-length];
  696.     }
  697.  
  698.       for (row = 0; row < height; row++)
  699.     {
  700.       gimp_pixel_rgn_get_row (&src_rgn, src, x1, row + y1, (x2 - x1));
  701.  
  702.       if (has_alpha && vert < 1.0)
  703.         multiply_alpha (src, height, bytes);
  704.  
  705.       sp = src;
  706.       dp = dest;
  707.  
  708.       for (b = 0; b < bytes; b++)
  709.         {
  710.           initial_p = sp[b];
  711.           initial_m = sp[(width-1) * bytes + b];
  712.  
  713.           /*  Determine a run-length encoded version of the row  */
  714.           run_length_encode (sp + b, buf, bytes, width);
  715.  
  716.           for (col = 0; col < width; col++)
  717.         {
  718.           start = (col < length) ? -col : -length;
  719.           end = (width <= (col + length)) ? (width - col - 1) : length;
  720.  
  721.           val = 0;
  722.           i = start;
  723.           bb = buf + (col + i) * 2;
  724.  
  725.           if (start != -length)
  726.             val += initial_p * (sum[start] - sum[-length]);
  727.  
  728.           while (i < end)
  729.             {
  730.               pixels = bb[0];
  731.               i += pixels;
  732.               if (i > end)
  733.             i = end;
  734.               val += bb[1] * (sum[i] - sum[start]);
  735.               bb += (pixels * 2);
  736.               start = i;
  737.             }
  738.  
  739.           if (end != length)
  740.             val += initial_m * (sum[length] - sum[end]);
  741.  
  742.           dp[col * bytes + b] = val / total;
  743.         }
  744.         }
  745.  
  746.       if (has_alpha)
  747.         separate_alpha (dest, width, bytes);
  748.  
  749.       gimp_pixel_rgn_set_row (&dest_rgn, dest, x1, row + y1, (x2 - x1));
  750.       progress += width * horz;
  751.       if ((row % 5) == 0)
  752.         gimp_progress_update ((double) progress / (double) max_progress);
  753.     }
  754.     }
  755.  
  756.   /*  merge the shadow, update the drawable  */
  757.   gimp_drawable_flush (drawable);
  758.   gimp_drawable_merge_shadow (drawable->id, TRUE);
  759.   gimp_drawable_update (drawable->id, x1, y1, (x2 - x1), (y2 - y1));
  760.  
  761.   /*  free buffers  */
  762.   g_free (buf);
  763.   g_free (src);
  764.   g_free (dest);
  765. }
  766.  
  767. /*
  768.  * The equations: g(r) = exp (- r^2 / (2 * sigma^2))
  769.  *                   r = sqrt (x^2 + y ^2)
  770.  */
  771.  
  772. static gint *
  773. make_curve (gdouble  sigma,
  774.         gint    *length)
  775. {
  776.   gint *curve;
  777.   gdouble sigma2;
  778.   gdouble l;
  779.   gint temp;
  780.   gint i, n;
  781.  
  782.   sigma2 = 2 * sigma * sigma;
  783.   l = sqrt (-sigma2 * log (1.0 / 255.0));
  784.  
  785.   n = ceil (l) * 2;
  786.   if ((n % 2) == 0)
  787.     n += 1;
  788.  
  789.   curve = g_new (gint, n);
  790.  
  791.   *length = n / 2;
  792.   curve += *length;
  793.   curve[0] = 255;
  794.  
  795.   for (i = 1; i <= *length; i++)
  796.     {
  797.       temp = (gint) (exp (- (i * i) / sigma2) * 255);
  798.       curve[-i] = temp;
  799.       curve[i] = temp;
  800.     }
  801.  
  802.   return curve;
  803. }
  804.  
  805. static void
  806. run_length_encode (guchar *src,
  807.            gint   *dest,
  808.            gint    bytes,
  809.            gint    width)
  810. {
  811.   gint start;
  812.   gint i;
  813.   gint j;
  814.   guchar last;
  815.  
  816.   last = *src;
  817.   src += bytes;
  818.   start = 0;
  819.  
  820.   for (i = 1; i < width; i++)
  821.     {
  822.       if (*src != last)
  823.     {
  824.       for (j = start; j < i; j++)
  825.         {
  826.           *dest++ = (i - j);
  827.           *dest++ = last;
  828.         }
  829.       start = i;
  830.       last = *src;
  831.     }
  832.       src += bytes;
  833.     }
  834.  
  835.   for (j = start; j < i; j++)
  836.     {
  837.       *dest++ = (i - j);
  838.       *dest++ = last;
  839.     }
  840. }
  841.