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 / mblur.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-08-24  |  20.2 KB  |  837 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * Motion Blur plug-in for GIMP 0.99
  5.  * Copyright (C) 1997 Daniel Skarda (0rfelyus@atrey.karlin.mff.cuni.cz)
  6.  * 
  7.  * This plug-in is port of Motion Blur plug-in for GIMP 0.54 by Thorsten Martinsen
  8.  *     Copyright (C) 1996 Torsten Martinsen <torsten@danbbs.dk>
  9.  *     Bresenham algorithm stuff hacked from HP2xx written by Heinz W. Werntges
  10.  *     Changes for version 1.11/1.12 Copyright (C) 1996 Federico Mena Quintero
  11.  *     quartic@polloux.fciencias.unam.mx
  12.  *
  13.  * I also used some code from Whirl and Pinch plug-in by Federico Mena Quintero
  14.  *     (federico@nuclecu.unam.mx)
  15.  *
  16.  * This program is free software; you can redistribute it and/or modify
  17.  * it under the terms of the GNU General Public License as published by
  18.  * the Free Software Foundation; either version 2 of the License, or
  19.  * (at your option) any later version.
  20.  *
  21.  * This program is distributed in the hope that it will be useful,
  22.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  23.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  24.  * GNU General Public License for more details.
  25.  *
  26.  * You should have received a copy of the GNU General Public License
  27.  * along with this program; if not, write to the Free Software
  28.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  29.  */
  30.  
  31. /* Version 1.2
  32.  *
  33.  *     Everything is new - no changes
  34.  *
  35.  * TODO:
  36.  *     Bilinear interpolation from original mblur for 0.54
  37.  *     Speed all things up
  38.  *        ? better caching scheme
  39.  *        - while bluring along long trajektory do not averrage all 
  40.  *           pixels but averrage only few samples
  41.  *     Function for weight of samples along trajectory
  42.  *     Preview
  43.  *     Support paths in GiMP 1.1 :-)
  44.  *     Smash all bugs :-)
  45.  */
  46.  
  47. #include "config.h"
  48.  
  49. #include <stdlib.h>
  50. #ifdef HAVE_UNISTD_H
  51. #include <unistd.h>
  52. #endif
  53.  
  54. #include <gtk/gtk.h>
  55.  
  56. #include <libgimp/gimp.h>
  57. #include <libgimp/gimpui.h>
  58.  
  59. #include "libgimp/stdplugins-intl.h"
  60.  
  61.  
  62. #define PLUG_IN_NAME     "plug_in_mblur"
  63. #define PLUG_IN_VERSION    "Sep 1997, 1.2"
  64.  
  65. #define     MBLUR_LINEAR 0
  66. #define     MBLUR_RADIAL 1
  67. #define     MBLUR_ZOOM     2
  68.  
  69. #define        MBLUR_MAX    MBLUR_ZOOM
  70.  
  71. typedef struct
  72. {
  73.   gint32  mblur_type;
  74.   gint32  length;
  75.   gint32  angle;
  76. } mblur_vals_t;
  77.  
  78. typedef struct
  79. {
  80.   gint       col, row;
  81.   gint       img_width, img_height, img_bpp, img_has_alpha;
  82.   gint       tile_width, tile_height;
  83.   guchar     bg_color[4];
  84.   GimpDrawable *drawable;
  85.   GimpTile     *tile;
  86. } pixel_fetcher_t;
  87.  
  88. /***** Prototypes *****/
  89.  
  90. static void query (void);
  91. static void run   (gchar   *name,
  92.            gint     nparams,
  93.            GimpParam  *param,
  94.            gint    *nreturn_vals,
  95.            GimpParam **return_vals);
  96.  
  97. static pixel_fetcher_t *pixel_fetcher_new          (GimpDrawable *drawable);
  98. static void             pixel_fetcher_set_bg_color (pixel_fetcher_t *pf,
  99.                             guchar r, guchar g,
  100.                             guchar b, guchar a);
  101. static void             pixel_fetcher_get_pixel    (pixel_fetcher_t *pf,
  102.                             int x, int y, guchar *pixel);
  103. static void             pixel_fetcher_destroy       (pixel_fetcher_t *pf);
  104.  
  105. static void        mblur        (void);
  106. static void         mblur_linear (void);
  107. static void             mblur_radial (void);
  108. static void             mblur_zoom   (void);
  109.  
  110. static void       dialog_ok_callback   (GtkWidget *, gpointer);
  111.  
  112. static gboolean   mblur_dialog         (void);
  113.  
  114. /***** Variables *****/
  115.  
  116. GimpPlugInInfo PLUG_IN_INFO =
  117. {
  118.   NULL,  /* init_proc  */
  119.   NULL,  /* quit_proc  */
  120.   query, /* query_proc */
  121.   run    /* run_proc   */
  122. };
  123.  
  124. static mblur_vals_t mbvals =
  125. {
  126.   MBLUR_LINEAR,    /* mblur_type */
  127.   5,        /* length */
  128.   45        /* radius */
  129. };
  130.  
  131. static gboolean mb_run = FALSE;
  132.  
  133. static GimpDrawable *drawable;
  134.  
  135. static gint img_width, img_height, img_bpp, img_has_alpha;
  136. static gint sel_x1, sel_y1, sel_x2, sel_y2;
  137. static gint sel_width, sel_height;
  138.  
  139. static double cen_x, cen_y;
  140.  
  141. /***** Functions *****/
  142.  
  143. MAIN()
  144.  
  145. static void
  146. query (void)
  147. {
  148.   static GimpParamDef args[] =
  149.   {
  150.     { GIMP_PDB_INT32,    "run_mode",  "Interactive, non-interactive" },
  151.     { GIMP_PDB_IMAGE,    "image",     "Input image" },
  152.     { GIMP_PDB_DRAWABLE, "drawable",  "Input drawable" },
  153.     { GIMP_PDB_INT32,     "type",      "Type of motion blur (0 - linear, 1 - radial, 2 - zoom)" },
  154.     { GIMP_PDB_INT32,     "length",    "Length" },
  155.     { GIMP_PDB_INT32,     "angle",     "Angle" }
  156.   };
  157.   static gint nargs = sizeof (args) / sizeof (args[0]);
  158.  
  159.   gimp_install_procedure (PLUG_IN_NAME,
  160.               "Motion blur of image",
  161.               "This plug-in simulates the effect seen when "
  162.               "photographing a moving object at a slow shutter "
  163.               "speed. Done by adding multiple displaced copies.",
  164.               "Torsten Martinsen, Federico Mena Quintero and Daniel Skarda",
  165.               "Torsten Martinsen, Federico Mena Quintero and Daniel Skarda",       
  166.               PLUG_IN_VERSION,
  167.               N_("<Image>/Filters/Blur/Motion Blur..."),
  168.               "RGB*, GRAY*",
  169.               GIMP_PLUGIN,
  170.               nargs, 0,
  171.               args, NULL);
  172. }
  173.  
  174. static void
  175. run (gchar   *name,
  176.      gint     nparams,
  177.      GimpParam  *param,
  178.      gint    *nreturn_vals,
  179.      GimpParam **return_vals)
  180. {
  181.   static GimpParam values[1];
  182.  
  183.   GimpRunModeType run_mode;
  184.   GimpPDBStatusType  status;
  185.  
  186.   status   = GIMP_PDB_SUCCESS;
  187.   run_mode = param[0].data.d_int32;
  188.  
  189.   values[0].type          = GIMP_PDB_STATUS;
  190.   values[0].data.d_status = status;
  191.  
  192.   *nreturn_vals = 1;
  193.   *return_vals  = values;
  194.  
  195.   /* Get the active drawable info */
  196.  
  197.   drawable = gimp_drawable_get (param[2].data.d_drawable);
  198.  
  199.   img_width     = gimp_drawable_width (drawable->id);
  200.   img_height    = gimp_drawable_height (drawable->id);
  201.   img_bpp       = gimp_drawable_bpp (drawable->id);
  202.   img_has_alpha = gimp_drawable_has_alpha (drawable->id);
  203.  
  204.   gimp_drawable_mask_bounds (drawable->id, &sel_x1, &sel_y1, &sel_x2, &sel_y2);
  205.  
  206.   /* Calculate scaling parameters */
  207.  
  208.   sel_width  = sel_x2 - sel_x1;
  209.   sel_height = sel_y2 - sel_y1;
  210.  
  211.   cen_x = (double) (sel_x1 + sel_x2 - 1) / 2.0;
  212.   cen_y = (double) (sel_y1 + sel_y2 - 1) / 2.0;
  213.  
  214.   switch (run_mode)
  215.     {
  216.     case GIMP_RUN_INTERACTIVE:
  217.       INIT_I18N_UI();
  218.  
  219.       /* Possibly retrieve data */
  220.       gimp_get_data (PLUG_IN_NAME, &mbvals);
  221.  
  222.       /* Get information from the dialog */
  223.       if (!mblur_dialog())
  224.     return;
  225.       break;
  226.  
  227.     case GIMP_RUN_NONINTERACTIVE:
  228.       INIT_I18N();
  229.  
  230.       /* Make sure all the arguments are present */
  231.       if (nparams != 6)
  232.     status = GIMP_PDB_CALLING_ERROR;
  233.  
  234.       if (status == GIMP_PDB_SUCCESS)
  235.     {
  236.       mbvals.mblur_type = param[3].data.d_int32;
  237.       mbvals.length     = param[4].data.d_int32;
  238.       mbvals.angle      = param[5].data.d_int32;
  239.     }
  240.  
  241.     if ((mbvals.mblur_type < 0) && (mbvals.mblur_type > MBLUR_ZOOM))
  242.       status= GIMP_PDB_CALLING_ERROR;
  243.     break;
  244.  
  245.     case GIMP_RUN_WITH_LAST_VALS:
  246.       INIT_I18N();
  247.  
  248.       /* Possibly retrieve data */
  249.       gimp_get_data (PLUG_IN_NAME, &mbvals);
  250.       break;
  251.  
  252.     default:
  253.       break;
  254.     }
  255.  
  256.   /* Blur the image */
  257.  
  258.   if ((status == GIMP_PDB_SUCCESS) &&
  259.       (gimp_drawable_is_rgb(drawable->id) ||
  260.        gimp_drawable_is_gray(drawable->id)))
  261.     {
  262.       /* Set the tile cache size */
  263.       gimp_tile_cache_ntiles (2 * (drawable->width +
  264.                    gimp_tile_width () - 1) / gimp_tile_width ());
  265.  
  266.       /* Run! */
  267.       mblur ();
  268.  
  269.       /* If run mode is interactive, flush displays */
  270.       if (run_mode != GIMP_RUN_NONINTERACTIVE)
  271.     gimp_displays_flush ();
  272.  
  273.       /* Store data */
  274.       if (run_mode == GIMP_RUN_INTERACTIVE)
  275.     gimp_set_data (PLUG_IN_NAME, &mbvals, sizeof(mblur_vals_t));
  276.     }
  277.   else if (status == GIMP_PDB_SUCCESS)
  278.     status = GIMP_PDB_EXECUTION_ERROR;
  279.  
  280.   values[0].data.d_status = status;
  281.  
  282.   gimp_drawable_detach (drawable);
  283. }
  284.  
  285. static void 
  286. mblur_linear (void)
  287. {
  288.   GimpPixelRgn    dest_rgn;
  289.   pixel_fetcher_t *pft;
  290.   gpointer    pr;
  291.  
  292.   guchar    *dest, *d;
  293.   guchar    pixel[4], bg_color[4];
  294.   gint32    sum[4];
  295.  
  296.   gint          progress, max_progress;
  297.   gint        c;
  298.  
  299.   int         x, y, i, xx, yy, n;
  300.   int         dx, dy, px, py, swapdir, err, e, s1, s2;
  301.  
  302.   gimp_pixel_rgn_init (&dest_rgn, drawable,
  303.                sel_x1, sel_y1, sel_width, sel_height, TRUE, TRUE);
  304.   
  305.   pft = pixel_fetcher_new (drawable);
  306.  
  307.   gimp_palette_get_background (&bg_color[0], &bg_color[1], &bg_color[2]);
  308.   pixel_fetcher_set_bg_color (pft, bg_color[0], bg_color[1], bg_color[2],
  309.                   (img_has_alpha ? 0 : 255));
  310.  
  311.   progress     = 0;
  312.   max_progress = sel_width * sel_height;
  313.  
  314.   n = mbvals.length;
  315.   px = n * cos (mbvals.angle / 180.0 * G_PI);
  316.   py = n * sin (mbvals.angle / 180.0 * G_PI);
  317.  
  318.   /*
  319.    * Initialization for Bresenham algorithm:
  320.    * dx = abs(x2-x1), s1 = sign(x2-x1)
  321.    * dy = abs(y2-y1), s2 = sign(y2-y1)
  322.    */
  323.   if ((dx = px) != 0)
  324.     {
  325.       if (dx < 0)
  326.     {
  327.       dx = -dx;
  328.       s1 = -1;
  329.     }
  330.       else
  331.     s1 = 1;
  332.     }
  333.   else
  334.     s1 = 0;
  335.  
  336.   if ((dy = py) != 0)
  337.     {
  338.       if (dy < 0)
  339.     {
  340.       dy = -dy;
  341.       s2 = -1;
  342.     }
  343.       else
  344.     s2 = 1;
  345.     }
  346.   else
  347.     s2 = 0;
  348.  
  349.   if (dy > dx)
  350.     {
  351.       swapdir = dx;
  352.       dx = dy;
  353.       dy = swapdir;
  354.       swapdir = 1;
  355.     }
  356.   else
  357.     swapdir = 0;
  358.  
  359.   dy *= 2;
  360.   err = dy - dx;    /* Initial error term    */
  361.   dx *= 2;
  362.  
  363.   for (pr = gimp_pixel_rgns_register (1, &dest_rgn);
  364.        pr != NULL;
  365.        pr = gimp_pixel_rgns_process (pr))
  366.     {
  367.       dest = dest_rgn.data;
  368.  
  369.       for (y = dest_rgn.y; y < (dest_rgn.y + dest_rgn.h); y++)
  370.     {
  371.       d = dest;
  372.  
  373.       for (x = dest_rgn.x; x < (dest_rgn.x + dest_rgn.w); x++)
  374.         {
  375.           xx = x; yy = y; e = err;
  376.           for (c= 0; c < img_bpp; c++)
  377.         sum[c]= 0;
  378.  
  379.           for (i = 0; i < n; )
  380.         {
  381.           pixel_fetcher_get_pixel (pft, xx, yy, pixel);
  382.           for (c= 0; c < img_bpp; c++)
  383.             sum[c]+= pixel[c];
  384.           i++;
  385.  
  386.           while (e >= 0)
  387.             {
  388.               if (swapdir)
  389.             xx += s1;
  390.               else
  391.             yy += s2;
  392.               e -= dx;
  393.             }
  394.           if (swapdir)
  395.             yy += s2;
  396.           else
  397.             xx += s1;
  398.           e += dy;
  399.           if ((xx < sel_x1) || (xx >= sel_x2) ||
  400.               (yy < sel_y1) || (yy >= sel_y2))
  401.             break;
  402.         }
  403.  
  404.           if ( i==0 )
  405.         {
  406.           pixel_fetcher_get_pixel (pft, xx, yy, d); 
  407.         }
  408.           else
  409.         {
  410.           for (c=0; c < img_bpp; c++)
  411.             d[c]= sum[c] / i;
  412.         }
  413.           d+= dest_rgn.bpp;
  414.         }
  415.       dest += dest_rgn.rowstride;
  416.     }
  417.       progress += dest_rgn.w * dest_rgn.h;
  418.       gimp_progress_update ((double) progress / max_progress);
  419.     }
  420.   pixel_fetcher_destroy (pft);
  421. }
  422.  
  423. static void
  424. mblur_radial (void)
  425. {
  426.   GimpPixelRgn    dest_rgn;
  427.   pixel_fetcher_t *pft;
  428.   gpointer    pr;
  429.  
  430.   guchar    *dest, *d;
  431.   guchar    pixel[4], bg_color[4];
  432.   gint32    sum[4];
  433.  
  434.   gint          progress, max_progress, c;
  435.  
  436.   int         x, y, i, n, xr, yr;
  437.   int         count, R, r, w, h, step;
  438.   float     angle, theta, * ct, * st, offset, xx, yy;
  439.  
  440.   /* initialize */
  441.  
  442.   xx = 0.0;
  443.   yy = 0.0;
  444.  
  445.   gimp_pixel_rgn_init (&dest_rgn, drawable,
  446.                sel_x1, sel_y1, sel_width, sel_height, TRUE, TRUE);
  447.   
  448.   pft = pixel_fetcher_new (drawable);
  449.  
  450.   gimp_palette_get_background (&bg_color[0], &bg_color[1], &bg_color[2]);
  451.   pixel_fetcher_set_bg_color (pft, bg_color[0], bg_color[1], bg_color[2],
  452.                   (img_has_alpha ? 0 : 255));
  453.  
  454.   progress     = 0;
  455.   max_progress = sel_width * sel_height;
  456.  
  457.   angle = ((float) mbvals.angle) / 180.0 * G_PI;
  458.   w = MAX (img_width-cen_x, cen_x);
  459.   h = MAX (img_height-cen_y, cen_y);
  460.   R = sqrt (w * w + h * h);
  461.   n = 4 * angle * sqrt (R) + 2;
  462.   theta = angle / ((float) (n - 1));
  463.  
  464.   if (((ct = g_new (float, n)) == NULL) ||
  465.       ((st = g_new (float, n)) == NULL))
  466.     return;
  467.   offset = theta * (n - 1) / 2;
  468.   for (i = 0; i < n; ++i)
  469.     {
  470.       ct[i] = cos (theta * i - offset);
  471.       st[i] = sin (theta * i - offset);
  472.     }
  473.  
  474.   for (pr = gimp_pixel_rgns_register (1, &dest_rgn);
  475.        pr != NULL;
  476.        pr = gimp_pixel_rgns_process (pr))
  477.     {
  478.       dest = dest_rgn.data;
  479.  
  480.       for (y = dest_rgn.y; y < (dest_rgn.y + dest_rgn.h); y++)
  481.     {
  482.       d = dest;
  483.  
  484.       for (x = dest_rgn.x; x < (dest_rgn.x + dest_rgn.w); x++)
  485.         {
  486.           xr = x-cen_x;
  487.           yr = y-cen_y;
  488.           r = sqrt (xr * xr + yr * yr);
  489.           if (r == 0)
  490.         step = 1;
  491.           else if ((step = R / r) == 0)
  492.         step = 1;
  493.           else if (step > n-1)
  494.         step = n-1;
  495.  
  496.           for (c = 0; c < img_bpp; c++)
  497.         sum[c] = 0;
  498.  
  499.           for (i = 0, count = 0; i < n; i += step)
  500.         {
  501.           xx = cen_x + xr * ct[i] - yr * st[i];
  502.           yy = cen_y + xr * st[i] + yr * ct[i];
  503.           if ((yy < sel_y1) || (yy >= sel_y2) ||
  504.               (xx < sel_x1) || (xx >= sel_x2))
  505.             continue;
  506.  
  507.           ++count;
  508.           pixel_fetcher_get_pixel (pft, xx, yy, pixel);
  509.           for (c = 0; c < img_bpp; c++) 
  510.             sum[c] += pixel[c];
  511.         }
  512.  
  513.           if (count == 0)
  514.         {
  515.           pixel_fetcher_get_pixel (pft, xx, yy, d); 
  516.         }
  517.           else
  518.         {
  519.           for (c = 0; c < img_bpp; c++)
  520.             d[c]= sum[c] / count;
  521.         }
  522.           d += dest_rgn.bpp;
  523.         }
  524.       dest += dest_rgn.rowstride;
  525.     }
  526.       progress += dest_rgn.w * dest_rgn.h;
  527.       gimp_progress_update ((double) progress / max_progress);
  528.     }
  529.  
  530.   pixel_fetcher_destroy (pft);
  531.  
  532.   g_free (ct);
  533.   g_free (st);
  534. }
  535.  
  536. static void
  537. mblur_zoom (void)
  538. {
  539.   GimpPixelRgn    dest_rgn;
  540.   pixel_fetcher_t *pft;
  541.   gpointer    pr;
  542.  
  543.   guchar    *dest, *d;
  544.   guchar    pixel[4], bg_color[4];
  545.   gint32    sum[4];
  546.  
  547.   gint          progress, max_progress;
  548.   int         x, y, i, xx, yy, n, c;
  549.   float     f;
  550.     
  551.   /* initialize */
  552.  
  553.   xx = 0.0;
  554.   yy = 0.0;
  555.  
  556.   gimp_pixel_rgn_init (&dest_rgn, drawable,
  557.                sel_x1, sel_y1, sel_width, sel_height, TRUE, TRUE);
  558.  
  559.   pft = pixel_fetcher_new (drawable);
  560.  
  561.   gimp_palette_get_background (&bg_color[0], &bg_color[1], &bg_color[2]);
  562.   pixel_fetcher_set_bg_color (pft, bg_color[0], bg_color[1], bg_color[2],
  563.                   (img_has_alpha ? 0 : 255));
  564.  
  565.   progress     = 0;
  566.   max_progress = sel_width * sel_height;
  567.  
  568.   n = mbvals.length;
  569.   f = 0.02;
  570.  
  571.   for (pr = gimp_pixel_rgns_register (1, &dest_rgn);
  572.        pr != NULL;
  573.        pr = gimp_pixel_rgns_process (pr)) 
  574.     {
  575.       dest = dest_rgn.data;
  576.  
  577.       for (y = dest_rgn.y; y < (dest_rgn.y + dest_rgn.h); y++)
  578.     {
  579.       d = dest;
  580.  
  581.       for (x = dest_rgn.x; x < (dest_rgn.x + dest_rgn.w); x++)
  582.         {
  583.           for (c = 0; c < img_bpp; c++)
  584.         sum[c] = 0;
  585.  
  586.           for (i = 0; i < n; ++i)
  587.         {
  588.           xx = cen_x + (x-cen_x) * (1.0 + f * i);
  589.           yy = cen_y + (y-cen_y) * (1.0 + f * i);
  590.  
  591.           if ((yy < sel_y1) || (yy >= sel_y2) ||
  592.               (xx < sel_x1) || (xx >= sel_x2))
  593.             break;
  594.  
  595.           pixel_fetcher_get_pixel (pft, xx, yy, pixel);
  596.           for (c = 0; c < img_bpp; c++)
  597.             sum[c] += pixel[c];      
  598.         }
  599.  
  600.           if (i == 0)
  601.         {
  602.           pixel_fetcher_get_pixel (pft, xx, yy, d); 
  603.         }
  604.           else
  605.         {
  606.           for (c = 0; c < img_bpp; c++)
  607.             d[c] = sum[c] / i;
  608.         }
  609.           d += dest_rgn.bpp;
  610.         }
  611.       dest += dest_rgn.rowstride;
  612.     }
  613.       progress += dest_rgn.w * dest_rgn.h;
  614.       gimp_progress_update ((double) progress / max_progress);
  615.     }
  616.   pixel_fetcher_destroy (pft);
  617. }
  618.  
  619. static void
  620. mblur (void)
  621. {
  622.   gimp_progress_init (_("Blurring..."));
  623.  
  624.   switch (mbvals.mblur_type)
  625.     {
  626.     case MBLUR_LINEAR:
  627.       mblur_linear ();
  628.       break;
  629.     case MBLUR_RADIAL:
  630.       mblur_radial ();
  631.       break;
  632.     case MBLUR_ZOOM:
  633.       mblur_zoom ();
  634.       break;
  635.     default:
  636.       break;
  637.     }
  638.  
  639.   gimp_drawable_flush (drawable);
  640.   gimp_drawable_merge_shadow (drawable->id, TRUE);
  641.   gimp_drawable_update (drawable->id, sel_x1, sel_y1, sel_width, sel_height);
  642. }
  643.  
  644. /*****************************************
  645.  * pixel_fetcher from whirlpinch plug-in 
  646.  ****************************************/
  647.  
  648. static pixel_fetcher_t *
  649. pixel_fetcher_new (GimpDrawable *drawable)
  650. {
  651.   pixel_fetcher_t *pf;
  652.  
  653.   pf = g_new (pixel_fetcher_t, 1);
  654.  
  655.   pf->col           = -1;
  656.   pf->row           = -1;
  657.   pf->img_width     = gimp_drawable_width (drawable->id);
  658.   pf->img_height    = gimp_drawable_height (drawable->id);
  659.   pf->img_bpp       = gimp_drawable_bpp (drawable->id);
  660.   pf->img_has_alpha = gimp_drawable_has_alpha (drawable->id);
  661.   pf->tile_width    = gimp_tile_width ();
  662.   pf->tile_height   = gimp_tile_height ();
  663.   pf->bg_color[0]   = 0;
  664.   pf->bg_color[1]   = 0;
  665.   pf->bg_color[2]   = 0;
  666.   pf->bg_color[3]   = 0;
  667.  
  668.   pf->drawable    = drawable;
  669.   pf->tile        = NULL;
  670.  
  671.   return pf;
  672. }
  673.  
  674. static void
  675. pixel_fetcher_set_bg_color (pixel_fetcher_t *pf,
  676.                 guchar           r,
  677.                 guchar           g,
  678.                 guchar           b,
  679.                 guchar           a)
  680. {
  681.   pf->bg_color[0] = r;
  682.   pf->bg_color[1] = g;
  683.   pf->bg_color[2] = b;
  684.  
  685.   if (pf->img_has_alpha)
  686.     pf->bg_color[pf->img_bpp - 1] = a;
  687. }
  688.  
  689. static void
  690. pixel_fetcher_get_pixel (pixel_fetcher_t *pf,
  691.              int              x,
  692.              int              y,
  693.              guchar          *pixel)
  694. {
  695.   gint    col, row;
  696.   gint    coloff, rowoff;
  697.   guchar *p;
  698.   int     i;
  699.  
  700.   if ((x < sel_x1) || (x >= sel_x2) ||
  701.       (y < sel_y1) || (y >= sel_y2))
  702.     {
  703.       for (i = 0; i < pf->img_bpp; i++)
  704.     pixel[i] = pf->bg_color[i];
  705.  
  706.       return;
  707.     }
  708.  
  709.   col    = x / pf->tile_width;
  710.   coloff = x % pf->tile_width;
  711.   row    = y / pf->tile_height;
  712.   rowoff = y % pf->tile_height;
  713.  
  714.   if ((col != pf->col) ||
  715.       (row != pf->row) ||
  716.       (pf->tile == NULL))
  717.     {
  718.       if (pf->tile != NULL)
  719.     gimp_tile_unref (pf->tile, FALSE);
  720.  
  721.       pf->tile = gimp_drawable_get_tile (pf->drawable, FALSE, row, col);
  722.       gimp_tile_ref (pf->tile);
  723.  
  724.       pf->col = col;
  725.       pf->row = row;
  726.     }
  727.  
  728.   p = pf->tile->data + pf->img_bpp * (pf->tile->ewidth * rowoff + coloff);
  729.  
  730.   for (i = pf->img_bpp; i; i--)
  731.     *pixel++ = *p++;
  732. }
  733.  
  734. static void
  735. pixel_fetcher_destroy (pixel_fetcher_t *pf)
  736. {
  737.   if (pf->tile != NULL)
  738.     gimp_tile_unref (pf->tile, FALSE);
  739.  
  740.   g_free (pf);
  741. }
  742.  
  743. /****************************************
  744.  *                 UI
  745.  ****************************************/
  746.  
  747. static gboolean
  748. mblur_dialog (void)
  749. {
  750.   GtkWidget *dialog;
  751.   GtkWidget *main_vbox;
  752.   GtkWidget *frame;
  753.   GtkWidget *table;
  754.   GtkObject *adjustment;
  755.  
  756.   gimp_ui_init ("mblur", FALSE);
  757.  
  758.   dialog = gimp_dialog_new (_("Motion Blur"), "mblur",
  759.                 gimp_standard_help_func, "filters/mblur.html",
  760.                 GTK_WIN_POS_MOUSE,
  761.                 FALSE, TRUE, FALSE,
  762.  
  763.                 _("OK"), dialog_ok_callback,
  764.                 NULL, NULL, NULL, TRUE, FALSE,
  765.                 _("Cancel"), gtk_widget_destroy,
  766.                 NULL, 1, NULL, FALSE, TRUE,
  767.  
  768.                 NULL);
  769.  
  770.   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
  771.               GTK_SIGNAL_FUNC (gtk_main_quit),
  772.               NULL);
  773.  
  774.   main_vbox = gtk_vbox_new (FALSE, 4);
  775.   gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 6);
  776.   gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), main_vbox);
  777.   gtk_widget_show (main_vbox);
  778.  
  779.   frame =
  780.     gimp_radio_group_new2 (TRUE, _("Blur Type"),
  781.                gimp_radio_button_update,
  782.                &mbvals.mblur_type, (gpointer) mbvals.mblur_type,
  783.  
  784.                _("Linear"), (gpointer) MBLUR_LINEAR, NULL,
  785.                _("Radial"), (gpointer) MBLUR_RADIAL, NULL,
  786.                _("Zoom"),   (gpointer) MBLUR_ZOOM, NULL,
  787.  
  788.                NULL);
  789.   gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
  790.   gtk_widget_show (frame);
  791.  
  792.   frame = gtk_frame_new (_("Blur Parameters"));
  793.   gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
  794.   gtk_widget_show (frame);
  795.  
  796.   table = gtk_table_new (2, 3, FALSE);
  797.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  798.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  799.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  800.   gtk_container_add (GTK_CONTAINER (frame), table);
  801.   gtk_widget_show (table);
  802.  
  803.   adjustment = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
  804.                      _("Length:"), 150, 0,
  805.                      mbvals.length, 0.0, 256.0, 1.0, 8.0, 0,
  806.                      TRUE, 0, 0,
  807.                      NULL, NULL);
  808.   gtk_signal_connect (adjustment, "value_changed",
  809.               GTK_SIGNAL_FUNC (gimp_int_adjustment_update),
  810.               &mbvals.length);
  811.  
  812.   adjustment = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
  813.                      _("Angle:"), 150, 0,
  814.                      mbvals.angle, 0.0, 360.0, 1.0, 15.0, 0,
  815.                      TRUE, 0, 0,
  816.                      NULL, NULL);
  817.   gtk_signal_connect (adjustment, "value_changed",
  818.               GTK_SIGNAL_FUNC (gimp_int_adjustment_update),
  819.               &mbvals.angle);
  820.  
  821.   gtk_widget_show (dialog);
  822.  
  823.   gtk_main ();
  824.   gdk_flush ();
  825.  
  826.   return mb_run;
  827. }
  828.  
  829. static void
  830. dialog_ok_callback (GtkWidget *widget,
  831.             gpointer   data)
  832. {
  833.   mb_run= TRUE;
  834.  
  835.   gtk_widget_destroy (GTK_WIDGET (data));
  836. }
  837.