1 /****************************************************************************
2  * This is a plugin for GIMP v 0.99.8 or later.  Documentation is
3  * available at http://www.rru.com/~meo/gimp/ .
4  *
5  * Copyright (C) 1997-98 Miles O'Neal  <meo@rru.com>  http://www.rru.com/~meo/
6  * Blur code Copyright (C) 1995 Spencer Kimball and Peter Mattis
7  * GUI based on GTK code from:
8  *    alienmap (Copyright (C) 1996, 1997 Daniel Cotting)
9  *    plasma   (Copyright (C) 1996 Stephen Norris),
10  *    oilify   (Copyright (C) 1996 Torsten Martinsen),
11  *    ripple   (Copyright (C) 1997 Brian Degenhardt) and
12  *    whirl    (Copyright (C) 1997 Federico Mena Quintero).
13  *
14  * This program is free software: you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 3 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
26  *
27  ****************************************************************************/
28 
29 /****************************************************************************
30  * Blur:
31  *
32  * blur version 2.1 (10 June 2004 WES)
33  * history
34  *     2.1 - 10 June 2004 WES
35  *         removed dialog along with randomization and repeat options
36  *     2.0 -  1 May 1998 MEO
37  *         based on randomize 1.7
38  *
39  * Please send any patches or suggestions to the author: meo@rru.com .
40  *
41  * Blur applies a 3x3 blurring convolution kernel to the specified drawable.
42  *
43  * For each pixel in the selection or image,
44  * whether to change the pixel is decided by picking a
45  * random number, weighted by the user's "randomization" percentage.
46  * If the random number is in range, the pixel is modified.  For
47  * blurring, an average is determined from the current and adjacent
48  * pixels. *(Except for the random factor, the blur code came
49  * straight from the original S&P blur plug-in.)*
50  *
51  * This works only with RGB and grayscale images.
52  *
53  ****************************************************************************/
54 
55 #include "config.h"
56 
57 #include <string.h>
58 
59 #include <libgimp/gimp.h>
60 
61 #include "libgimp/stdplugins-intl.h"
62 
63 
64 /*********************************
65  *
66  *  PLUGIN-SPECIFIC CONSTANTS
67  *
68  ********************************/
69 
70 #define PLUG_IN_PROC "plug-in-blur"
71 
72 /*********************************
73  *
74  *  LOCAL FUNCTIONS
75  *
76  ********************************/
77 
78 static void query (void);
79 static void run   (const gchar      *name,
80                    gint              nparams,
81                    const GimpParam  *param,
82                    gint             *nreturn_vals,
83                    GimpParam       **return_vals);
84 
85 const GimpPlugInInfo PLUG_IN_INFO =
86 {
87   NULL,  /* init_proc  */
88   NULL,  /* quit_proc  */
89   query, /* query_proc */
90   run,   /* run_proc   */
91 };
92 
93 static void         blur             (GimpDrawable *drawable);
94 
95 static inline void  blur_prepare_row (GimpPixelRgn *pixel_rgn,
96                                       guchar       *data,
97                                       gint          x,
98                                       gint          y,
99                                       gint          w);
100 
101 /************************************ Guts ***********************************/
102 
MAIN()103 MAIN ()
104 
105 /*********************************
106  *
107  *  query() - query_proc
108  *
109  *      called by GIMP to learn about this plug-in
110  *
111  ********************************/
112 
113 static void
114 query (void)
115 {
116   static const GimpParamDef args[] =
117   {
118     { GIMP_PDB_INT32,    "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
119     { GIMP_PDB_IMAGE,    "image",    "Input image (unused)"         },
120     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable"               },
121   };
122 
123   gimp_install_procedure (PLUG_IN_PROC,
124                           N_("Simple blur, fast but not very strong"),
125                           "This plug-in blurs the specified drawable, using "
126                           "a 3x3 blur. Indexed images are not supported.",
127                           "Miles O'Neal  <meo@rru.com>",
128                           "Miles O'Neal, Spencer Kimball, Peter Mattis, "
129                           "Torsten Martinsen, Brian Degenhardt, "
130                           "Federico Mena Quintero, Stephen Norris, "
131                           "Daniel Cotting",
132                           "1995-1998",
133                           N_("_Blur"),
134                           "RGB*, GRAY*",
135                           GIMP_PLUGIN,
136                           G_N_ELEMENTS (args), 0,
137                           args, NULL);
138 }
139 
140 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)141 run (const gchar      *name,
142      gint              nparams,
143      const GimpParam  *param,
144      gint             *nreturn_vals,
145      GimpParam       **return_vals)
146 {
147   GimpDrawable      *drawable;
148   GimpRunMode        run_mode;
149   GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
150   static GimpParam   values[1];
151 
152   INIT_I18N ();
153 
154   values[0].type          = GIMP_PDB_STATUS;
155   values[0].data.d_status = status;
156 
157   *nreturn_vals = 1;
158   *return_vals  = values;
159 
160   if (strcmp (name, PLUG_IN_PROC) != 0 || nparams < 3)
161     {
162       values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
163       return;
164     }
165 
166   run_mode = param[0].data.d_int32;
167   drawable = gimp_drawable_get (param[2].data.d_drawable);
168 
169   /*
170    *  Make sure the drawable type is appropriate.
171    */
172   if (gimp_drawable_is_rgb (drawable->drawable_id) ||
173       gimp_drawable_is_gray (drawable->drawable_id))
174     {
175       gimp_progress_init (_("Blurring"));
176       gimp_tile_cache_ntiles (2 * (drawable->width / gimp_tile_width () + 1));
177 
178       blur (drawable);
179 
180       if (run_mode != GIMP_RUN_NONINTERACTIVE)
181         {
182           gimp_displays_flush ();
183         }
184     }
185   else
186     {
187       status = GIMP_PDB_EXECUTION_ERROR;
188     }
189 
190   values[0].data.d_status = status;
191   gimp_drawable_detach (drawable);
192 }
193 
194 
195 /*********************************
196  *
197  *  blur_prepare_row()
198  *
199  *  Get a row of pixels.  If the requested row
200  *  is off the edge, clone the edge row.
201  *
202  ********************************/
203 
204 static inline void
blur_prepare_row(GimpPixelRgn * pixel_rgn,guchar * data,gint x,gint y,gint w)205 blur_prepare_row (GimpPixelRgn *pixel_rgn,
206                   guchar       *data,
207                   gint          x,
208                   gint          y,
209                   gint          w)
210 {
211   gint b;
212 
213   y = CLAMP (y, 0, pixel_rgn->h - 1);
214 
215   gimp_pixel_rgn_get_row (pixel_rgn, data, x, y, w);
216 
217   /*
218    *  Fill in edge pixels
219    */
220   for (b = 0; b < pixel_rgn->bpp; b++)
221     data[-(gint)pixel_rgn->bpp + b] = data[b];
222 
223   for (b = 0; b < pixel_rgn->bpp; b++)
224     data[w * pixel_rgn->bpp + b] = data[(w - 1) * pixel_rgn->bpp + b];
225 }
226 
227 /*********************************
228  *
229  *  blur()
230  *
231  *  Actually mess with the image.
232  *
233  ********************************/
234 
235 static void
blur(GimpDrawable * drawable)236 blur (GimpDrawable *drawable)
237 {
238   GimpPixelRgn  srcPR, destPR;
239   gint          width, height;
240   gint          bytes;
241   guchar       *dest, *d;
242   guchar       *prev_row, *pr;
243   guchar       *cur_row, *cr;
244   guchar       *next_row, *nr;
245   guchar       *tmp;
246   gint          row, col;
247   gint          x1, y1, x2, y2;
248   gint          ind;
249   gboolean      has_alpha;
250 
251   if (! gimp_drawable_mask_intersect (drawable->drawable_id,
252                                       &x1, &y1, &width, &height))
253     return;
254 
255   x2 = x1 + width;
256   y2 = y1 + height;
257 
258   /*
259    *  Get the size of the input image. (This will/must be the same
260    *  as the size of the output image.  Also get alpha info.
261    */
262   width = drawable->width;
263   height = drawable->height;
264   bytes = drawable->bpp;
265   has_alpha = gimp_drawable_has_alpha (drawable->drawable_id);
266   /*
267    *  allocate row buffers
268    */
269   prev_row = g_new (guchar, (x2 - x1 + 2) * bytes);
270   cur_row  = g_new (guchar, (x2 - x1 + 2) * bytes);
271   next_row = g_new (guchar, (x2 - x1 + 2) * bytes);
272   dest     = g_new (guchar, (x2 - x1) * bytes);
273 
274   /*
275    *  initialize the pixel regions
276    */
277   gimp_pixel_rgn_init (&srcPR, drawable, 0, 0, width, height, FALSE, FALSE);
278   gimp_pixel_rgn_init (&destPR, drawable, 0, 0, width, height, TRUE, TRUE);
279 
280   pr = prev_row + bytes;
281   cr = cur_row + bytes;
282   nr = next_row + bytes;
283 
284   /*
285    *  prepare the first row and previous row
286    */
287   blur_prepare_row (&srcPR, pr, x1, y1 - 1, (x2 - x1));
288   blur_prepare_row (&srcPR, cr, x1, y1, (x2 - x1));
289 
290   /*
291    *  loop through the rows, applying the selected convolution
292    */
293   for (row = y1; row < y2; row++)
294     {
295       /*  prepare the next row  */
296       blur_prepare_row (&srcPR, nr, x1, row + 1, (x2 - x1));
297 
298       d = dest;
299       ind = 0;
300       for (col = 0; col < (x2 - x1) * bytes; col++)
301         {
302           ind++;
303           if (ind == bytes || !has_alpha)
304             {
305               /*
306                *  If no alpha channel,
307                *   or if there is one and this is it...
308                */
309               *d++ = ((gint) pr[col - bytes] + (gint) pr[col] +
310                       (gint) pr[col + bytes] +
311                       (gint) cr[col - bytes] + (gint) cr[col] +
312                       (gint) cr[col + bytes] +
313                       (gint) nr[col - bytes] + (gint) nr[col] +
314                       (gint) nr[col + bytes] + 4) / 9;
315               ind = 0;
316             }
317           else
318             {
319               /*
320                *  otherwise we have an alpha channel,
321                *   but this is a color channel
322                */
323               *d++ = ROUND(
324                            ((gdouble) (pr[col - bytes] * pr[col - ind])
325                             + (gdouble) (pr[col] * pr[col + bytes - ind])
326                             + (gdouble) (pr[col + bytes] * pr[col + 2*bytes - ind])
327                             + (gdouble) (cr[col - bytes] * cr[col - ind])
328                             + (gdouble) (cr[col] * cr[col + bytes - ind])
329                             + (gdouble) (cr[col + bytes] * cr[col + 2*bytes - ind])
330                             + (gdouble) (nr[col - bytes] * nr[col - ind])
331                             + (gdouble) (nr[col] * nr[col + bytes - ind])
332                             + (gdouble) (nr[col + bytes] * nr[col + 2*bytes - ind]))
333                            / ((gdouble) pr[col - ind]
334                               + (gdouble) pr[col + bytes - ind]
335                               + (gdouble) pr[col + 2*bytes - ind]
336                               + (gdouble) cr[col - ind]
337                               + (gdouble) cr[col + bytes - ind]
338                               + (gdouble) cr[col + 2*bytes - ind]
339                               + (gdouble) nr[col - ind]
340                               + (gdouble) nr[col + bytes - ind]
341                               + (gdouble) nr[col + 2*bytes - ind]));
342             }
343         }
344 
345       /*
346        *  Save the modified row, shuffle the row pointers, and every
347        *  so often, update the progress meter.
348        */
349       gimp_pixel_rgn_set_row (&destPR, dest, x1, row, (x2 - x1));
350 
351       tmp = pr;
352       pr = cr;
353       cr = nr;
354       nr = tmp;
355 
356       if ((row % 32) == 0)
357         gimp_progress_update ((gdouble) row / (gdouble) (y2 - y1));
358     }
359 
360   gimp_progress_update (1.0);
361 
362   /*
363    *  update the blurred region
364    */
365   gimp_drawable_flush (drawable);
366   gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
367   gimp_drawable_update (drawable->drawable_id, x1, y1, (x2 - x1), (y2 - y1));
368   /*
369    *  clean up after ourselves.
370    */
371   g_free (prev_row);
372   g_free (cur_row);
373   g_free (next_row);
374   g_free (dest);
375 }
376