1 /* LIC 0.14 -- image filter plug-in for GIMP
2  * Copyright (C) 1996 Tom Bech
3  *
4  * E-mail: tomb@gimp.org
5  * You can contact the original GIMP authors at gimp@xcf.berkeley.edu
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  * In other words, you can't sue me for whatever happens while using this ;)
21  *
22  * Changes (post 0.10):
23  * -> 0.11: Fixed a bug in the convolution kernels (Tom).
24  * -> 0.12: Added Quartic's bilinear interpolation stuff (Tom).
25  * -> 0.13 Changed some UI stuff causing trouble with the 0.60 release, added
26  *         the (GIMP) tags and changed random() calls to rand() (Tom)
27  * -> 0.14 Ported to 0.99.11 (Tom)
28  *
29  * This plug-in implements the Line Integral Convolution (LIC) as described in
30  * Cabral et al. "Imaging vector fields using line integral convolution" in the
31  * Proceedings of ACM SIGGRAPH 93. Publ. by ACM, New York, NY, USA. p. 263-270.
32  * (See http://www8.cs.umu.se/kurser/TDBD13/VT00/extra/p263-cabral.pdf)
33  *
34  * Some of the code is based on code by Steinar Haugen (thanks!), the Perlin
35  * noise function is practically ripped as is :)
36  */
37 
38 #include "config.h"
39 
40 #include <libgimp/gimp.h>
41 #include <libgimp/gimpui.h>
42 
43 #include "libgimp/stdplugins-intl.h"
44 
45 
46 /************/
47 /* Typedefs */
48 /************/
49 
50 #define numx    40              /* Pseudo-random vector grid size */
51 #define numy    40
52 
53 #define PLUG_IN_PROC   "plug-in-lic"
54 #define PLUG_IN_BINARY "van-gogh-lic"
55 #define PLUG_IN_ROLE   "gimp-van-gogh-lic"
56 
57 typedef enum
58 {
59   LIC_HUE,
60   LIC_SATURATION,
61   LIC_BRIGHTNESS
62 } LICEffectChannel;
63 
64 
65 /*****************************/
66 /* Global variables and such */
67 /*****************************/
68 
69 static gdouble G[numx][numy][2];
70 
71 typedef struct
72 {
73   gdouble  filtlen;
74   gdouble  noisemag;
75   gdouble  intsteps;
76   gdouble  minv;
77   gdouble  maxv;
78   gint     effect_channel;
79   gint     effect_operator;
80   gint     effect_convolve;
81   gint32   effect_image_id;
82 } LicValues;
83 
84 static LicValues licvals;
85 
86 static gdouble l      = 10.0;
87 static gdouble dx     =  2.0;
88 static gdouble dy     =  2.0;
89 static gdouble minv   = -2.5;
90 static gdouble maxv   =  2.5;
91 static gdouble isteps = 20.0;
92 
93 static gboolean source_drw_has_alpha = FALSE;
94 
95 static gint    effect_width, effect_height;
96 static gint    border_x, border_y, border_w, border_h;
97 
98 static GtkWidget *dialog;
99 
100 /************************/
101 /* Convenience routines */
102 /************************/
103 
104 static void
peek(GeglBuffer * buffer,gint x,gint y,GimpRGB * color)105 peek (GeglBuffer *buffer,
106       gint        x,
107       gint        y,
108       GimpRGB    *color)
109 {
110   gegl_buffer_sample (buffer, x, y, NULL,
111                       color, babl_format ("R'G'B'A double"),
112                       GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
113 }
114 
115 static void
poke(GeglBuffer * buffer,gint x,gint y,GimpRGB * color)116 poke (GeglBuffer *buffer,
117       gint        x,
118       gint        y,
119       GimpRGB    *color)
120 {
121   gegl_buffer_set (buffer, GEGL_RECTANGLE (x, y, 1, 1), 0,
122                    babl_format ("R'G'B'A double"), color,
123                    GEGL_AUTO_ROWSTRIDE);
124 }
125 
126 static gint
peekmap(const guchar * image,gint x,gint y)127 peekmap (const guchar *image,
128          gint          x,
129          gint          y)
130 {
131   while (x < 0)
132     x += effect_width;
133   x %= effect_width;
134 
135   while (y < 0)
136     y += effect_height;
137   y %= effect_height;
138 
139   return (gint) image[x + effect_width * y];
140 }
141 
142 /*************/
143 /* Main part */
144 /*************/
145 
146 /***************************************************/
147 /* Compute the derivative in the x and y direction */
148 /* We use these convolution kernels:               */
149 /*     |1 0 -1|     |  1   2   1|                  */
150 /* DX: |2 0 -2| DY: |  0   0   0|                  */
151 /*     |1 0 -1|     | -1  -2  -1|                  */
152 /* (It's a variation of the Sobel kernels, really)  */
153 /***************************************************/
154 
155 static gint
gradx(const guchar * image,gint x,gint y)156 gradx (const guchar *image,
157        gint          x,
158        gint          y)
159 {
160   gint val = 0;
161 
162   val = val +     peekmap (image, x-1, y-1);
163   val = val -     peekmap (image, x+1, y-1);
164 
165   val = val + 2 * peekmap (image, x-1, y);
166   val = val - 2 * peekmap (image, x+1, y);
167 
168   val = val +     peekmap (image, x-1, y+1);
169   val = val -     peekmap (image, x+1, y+1);
170 
171   return val;
172 }
173 
174 static gint
grady(const guchar * image,gint x,gint y)175 grady (const guchar *image,
176        gint          x,
177        gint          y)
178 {
179   gint val = 0;
180 
181   val = val +     peekmap (image, x-1, y-1);
182   val = val + 2 * peekmap (image, x,   y-1);
183   val = val +     peekmap (image, x+1, y-1);
184 
185   val = val -     peekmap (image, x-1, y+1);
186   val = val - 2 * peekmap (image, x,   y+1);
187   val = val -     peekmap (image, x+1, y+1);
188 
189   return val;
190 }
191 
192 /************************************/
193 /* A nice 2nd order cubic spline :) */
194 /************************************/
195 
196 static gdouble
cubic(gdouble t)197 cubic (gdouble t)
198 {
199   gdouble at = fabs (t);
200 
201   return (at < 1.0) ? at * at * (2.0 * at - 3.0) + 1.0 : 0.0;
202 }
203 
204 static gdouble
omega(gdouble u,gdouble v,gint i,gint j)205 omega (gdouble u,
206        gdouble v,
207        gint    i,
208        gint    j)
209 {
210   while (i < 0)
211     i += numx;
212 
213   while (j < 0)
214     j += numy;
215 
216   i %= numx;
217   j %= numy;
218 
219   return cubic (u) * cubic (v) * (G[i][j][0]*u + G[i][j][1]*v);
220 }
221 
222 /*************************************************************/
223 /* The noise function (2D variant of Perlins noise function) */
224 /*************************************************************/
225 
226 static gdouble
noise(gdouble x,gdouble y)227 noise (gdouble x,
228        gdouble y)
229 {
230   gint i, sti = (gint) floor (x / dx);
231   gint j, stj = (gint) floor (y / dy);
232 
233   gdouble sum = 0.0;
234 
235   /* Calculate the gdouble sum */
236   /* ======================== */
237 
238   for (i = sti; i <= sti + 1; i++)
239     for (j = stj; j <= stj + 1; j++)
240       sum += omega ((x - (gdouble) i * dx) / dx,
241                     (y - (gdouble) j * dy) / dy,
242                     i, j);
243 
244   return sum;
245 }
246 
247 /*************************************************/
248 /* Generates pseudo-random vectors with length 1 */
249 /*************************************************/
250 
251 static void
generatevectors(void)252 generatevectors (void)
253 {
254   gdouble alpha;
255   gint i, j;
256   GRand *gr;
257 
258   gr = g_rand_new();
259 
260   for (i = 0; i < numx; i++)
261     {
262       for (j = 0; j < numy; j++)
263         {
264           alpha = g_rand_double_range (gr, 0, 2) * G_PI;
265           G[i][j][0] = cos (alpha);
266           G[i][j][1] = sin (alpha);
267         }
268     }
269 
270   g_rand_free (gr);
271 }
272 
273 /* A simple triangle filter */
274 /* ======================== */
275 
276 static gdouble
filter(gdouble u)277 filter (gdouble u)
278 {
279   gdouble f = 1.0 - fabs (u) / l;
280 
281   return (f < 0.0) ? 0.0 : f;
282 }
283 
284 /******************************************************/
285 /* Compute the Line Integral Convolution (LIC) at x,y */
286 /******************************************************/
287 
288 static gdouble
lic_noise(gint x,gint y,gdouble vx,gdouble vy)289 lic_noise (gint    x,
290            gint    y,
291            gdouble vx,
292            gdouble vy)
293 {
294   gdouble i = 0.0;
295   gdouble f1 = 0.0, f2 = 0.0;
296   gdouble u, step = 2.0 * l / isteps;
297   gdouble xx = (gdouble) x, yy = (gdouble) y;
298   gdouble c, s;
299 
300   /* Get vector at x,y */
301   /* ================= */
302 
303   c = vx;
304   s = vy;
305 
306   /* Calculate integral numerically */
307   /* ============================== */
308 
309   f1 = filter (-l) * noise (xx + l * c , yy + l * s);
310 
311   for (u = -l + step; u <= l; u += step)
312     {
313       f2 = filter (u) * noise ( xx - u * c , yy - u * s);
314       i += (f1 + f2) * 0.5 * step;
315       f1 = f2;
316     }
317 
318   i = (i - minv) / (maxv - minv);
319 
320   i = CLAMP (i, 0.0, 1.0);
321 
322   i = (i / 2.0) + 0.5;
323 
324   return i;
325 }
326 
327 static void
getpixel(GeglBuffer * buffer,GimpRGB * p,gdouble u,gdouble v)328 getpixel (GeglBuffer *buffer,
329           GimpRGB    *p,
330           gdouble     u,
331           gdouble     v)
332 {
333   register gint x1, y1, x2, y2;
334   gint width, height;
335   static GimpRGB pp[4];
336 
337   width  = border_w;
338   height = border_h;
339 
340   x1 = (gint)u;
341   y1 = (gint)v;
342 
343   if (x1 < 0)
344     x1 = width - (-x1 % width);
345   else
346     x1 = x1 % width;
347 
348   if (y1 < 0)
349     y1 = height - (-y1 % height);
350   else
351     y1 = y1 % height;
352 
353   x2 = (x1 + 1) % width;
354   y2 = (y1 + 1) % height;
355 
356   peek (buffer, x1, y1, &pp[0]);
357   peek (buffer, x2, y1, &pp[1]);
358   peek (buffer, x1, y2, &pp[2]);
359   peek (buffer, x2, y2, &pp[3]);
360 
361   if (source_drw_has_alpha)
362     *p = gimp_bilinear_rgba (u, v, pp);
363   else
364     *p = gimp_bilinear_rgb (u, v, pp);
365 }
366 
367 static void
lic_image(GeglBuffer * buffer,gint x,gint y,gdouble vx,gdouble vy,GimpRGB * color)368 lic_image (GeglBuffer *buffer,
369            gint        x,
370            gint        y,
371            gdouble     vx,
372            gdouble     vy,
373            GimpRGB    *color)
374 {
375   gdouble u, step = 2.0 * l / isteps;
376   gdouble xx = (gdouble) x, yy = (gdouble) y;
377   gdouble c, s;
378   GimpRGB col = { 0, 0, 0, 0 };
379   GimpRGB col1, col2, col3;
380 
381   /* Get vector at x,y */
382   /* ================= */
383 
384   c = vx;
385   s = vy;
386 
387   /* Calculate integral numerically */
388   /* ============================== */
389 
390   getpixel (buffer, &col1, xx + l * c, yy + l * s);
391 
392   if (source_drw_has_alpha)
393     gimp_rgba_multiply (&col1, filter (-l));
394   else
395     gimp_rgb_multiply (&col1, filter (-l));
396 
397   for (u = -l + step; u <= l; u += step)
398     {
399       getpixel (buffer, &col2, xx - u * c, yy - u * s);
400 
401       if (source_drw_has_alpha)
402         {
403           gimp_rgba_multiply (&col2, filter (u));
404 
405           col3 = col1;
406           gimp_rgba_add (&col3, &col2);
407           gimp_rgba_multiply (&col3, 0.5 * step);
408           gimp_rgba_add (&col, &col3);
409         }
410       else
411         {
412           gimp_rgb_multiply (&col2, filter (u));
413 
414           col3 = col1;
415           gimp_rgb_add (&col3, &col2);
416           gimp_rgb_multiply (&col3, 0.5 * step);
417           gimp_rgb_add (&col, &col3);
418         }
419       col1 = col2;
420     }
421   if (source_drw_has_alpha)
422     gimp_rgba_multiply (&col, 1.0 / l);
423   else
424     gimp_rgb_multiply (&col, 1.0 / l);
425   gimp_rgb_clamp (&col);
426 
427   *color = col;
428 }
429 
430 static guchar *
rgb_to_hsl(gint32 drawable_ID,LICEffectChannel effect_channel)431 rgb_to_hsl (gint32            drawable_ID,
432             LICEffectChannel  effect_channel)
433 {
434   GeglBuffer *buffer;
435   guchar     *themap, data[4];
436   gint        x, y;
437   GimpRGB     color;
438   GimpHSL     color_hsl;
439   gdouble     val = 0.0;
440   glong       maxc, index = 0;
441   GRand      *gr;
442 
443   gr = g_rand_new ();
444 
445   maxc = gimp_drawable_width (drawable_ID) * gimp_drawable_height (drawable_ID);
446 
447   buffer = gimp_drawable_get_buffer (drawable_ID);
448 
449   themap = g_new (guchar, maxc);
450 
451   for (y = 0; y < border_h; y++)
452     {
453       for (x = 0; x < border_w; x++)
454         {
455           data[3] = 255;
456 
457           gegl_buffer_sample (buffer, x, y, NULL,
458                               data, babl_format ("R'G'B'A u8"),
459                               GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
460 
461           gimp_rgba_set_uchar (&color, data[0], data[1], data[2], data[3]);
462           gimp_rgb_to_hsl (&color, &color_hsl);
463 
464           switch (effect_channel)
465             {
466             case LIC_HUE:
467               val = color_hsl.h * 255;
468               break;
469             case LIC_SATURATION:
470               val = color_hsl.s * 255;
471               break;
472             case LIC_BRIGHTNESS:
473               val = color_hsl.l * 255;
474               break;
475             }
476 
477           /* add some random to avoid unstructured areas. */
478           val += g_rand_double_range (gr, -1.0, 1.0);
479 
480           themap[index++] = (guchar) CLAMP0255 (RINT (val));
481         }
482     }
483 
484   g_object_unref (buffer);
485 
486   g_rand_free (gr);
487 
488   return themap;
489 }
490 
491 
492 static void
compute_lic(gint32 drawable_ID,const guchar * scalarfield,gboolean rotate)493 compute_lic (gint32        drawable_ID,
494              const guchar *scalarfield,
495              gboolean      rotate)
496 {
497   GeglBuffer *src_buffer;
498   GeglBuffer *dest_buffer;
499   gint        xcount, ycount;
500   GimpRGB     color;
501   gdouble     vx, vy, tmp;
502 
503   src_buffer  = gimp_drawable_get_buffer (drawable_ID);
504   dest_buffer = gimp_drawable_get_shadow_buffer (drawable_ID);
505 
506   for (ycount = 0; ycount < border_h; ycount++)
507     {
508       for (xcount = 0; xcount < border_w; xcount++)
509         {
510           /* Get derivative at (x,y) and normalize it */
511           /* ============================================================== */
512 
513           vx = gradx (scalarfield, xcount, ycount);
514           vy = grady (scalarfield, xcount, ycount);
515 
516           /* Rotate if needed */
517           if (rotate)
518             {
519               tmp = vy;
520               vy = -vx;
521               vx = tmp;
522             }
523 
524           tmp = sqrt (vx * vx + vy * vy);
525           if (tmp >= 0.000001)
526             {
527               tmp = 1.0 / tmp;
528               vx *= tmp;
529               vy *= tmp;
530             }
531 
532           /* Convolve with the LIC at (x,y) */
533           /* ============================== */
534 
535           if (licvals.effect_convolve == 0)
536             {
537               peek (src_buffer, xcount, ycount, &color);
538 
539               tmp = lic_noise (xcount, ycount, vx, vy);
540 
541               if (source_drw_has_alpha)
542                 gimp_rgba_multiply (&color, tmp);
543               else
544                 gimp_rgb_multiply (&color, tmp);
545             }
546           else
547             {
548               lic_image (src_buffer, xcount, ycount, vx, vy, &color);
549             }
550 
551           poke (dest_buffer, xcount, ycount, &color);
552         }
553 
554       gimp_progress_update ((gfloat) ycount / (gfloat) border_h);
555     }
556 
557   g_object_unref (src_buffer);
558   g_object_unref (dest_buffer);
559 
560   gimp_progress_update (1.0);
561 }
562 
563 static void
compute_image(gint32 drawable_ID)564 compute_image (gint32 drawable_ID)
565 {
566   guchar *scalarfield = NULL;
567 
568   /* Get some useful info on the input drawable */
569   /* ========================================== */
570   if (! gimp_drawable_mask_intersect (drawable_ID,
571                                       &border_x, &border_y,
572                                       &border_w, &border_h))
573     return;
574 
575   gimp_progress_init (_("Van Gogh (LIC)"));
576 
577   if (licvals.effect_convolve == 0)
578     generatevectors ();
579 
580   if (licvals.filtlen < 0.1)
581     licvals.filtlen = 0.1;
582 
583   l = licvals.filtlen;
584   dx = dy = licvals.noisemag;
585   minv = licvals.minv / 10.0;
586   maxv = licvals.maxv / 10.0;
587   isteps = licvals.intsteps;
588 
589   source_drw_has_alpha = gimp_drawable_has_alpha (drawable_ID);
590 
591   effect_width =  gimp_drawable_width  (licvals.effect_image_id);
592   effect_height = gimp_drawable_height (licvals.effect_image_id);
593 
594   switch (licvals.effect_channel)
595     {
596     case 0:
597       scalarfield = rgb_to_hsl (licvals.effect_image_id, LIC_HUE);
598       break;
599     case 1:
600       scalarfield = rgb_to_hsl (licvals.effect_image_id, LIC_SATURATION);
601       break;
602     case 2:
603       scalarfield = rgb_to_hsl (licvals.effect_image_id, LIC_BRIGHTNESS);
604       break;
605     }
606 
607   compute_lic (drawable_ID, scalarfield, licvals.effect_operator);
608 
609   g_free (scalarfield);
610 
611   /* Update image */
612   /* ============ */
613 
614   gimp_drawable_merge_shadow (drawable_ID, TRUE);
615   gimp_drawable_update (drawable_ID, border_x, border_y, border_w, border_h);
616 
617   gimp_displays_flush ();
618 }
619 
620 /**************************/
621 /* Below is only UI stuff */
622 /**************************/
623 
624 static gboolean
effect_image_constrain(gint32 image_id,gint32 drawable_id,gpointer data)625 effect_image_constrain (gint32    image_id,
626                         gint32    drawable_id,
627                         gpointer  data)
628 {
629   return gimp_drawable_is_rgb (drawable_id);
630 }
631 
632 static gboolean
create_main_dialog(void)633 create_main_dialog (void)
634 {
635   GtkWidget *vbox;
636   GtkWidget *hbox;
637   GtkWidget *frame;
638   GtkWidget *table;
639   GtkWidget *combo;
640   GtkObject *scale_data;
641   gint       row;
642   gboolean   run;
643 
644   gimp_ui_init (PLUG_IN_BINARY, TRUE);
645 
646   dialog = gimp_dialog_new (_("Van Gogh (LIC)"), PLUG_IN_ROLE,
647                             NULL, 0,
648                             gimp_standard_help_func, PLUG_IN_PROC,
649 
650                             _("_Cancel"), GTK_RESPONSE_CANCEL,
651                             _("_OK"),     GTK_RESPONSE_OK,
652 
653                             NULL);
654 
655   gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
656                                            GTK_RESPONSE_OK,
657                                            GTK_RESPONSE_CANCEL,
658                                            -1);
659 
660   gimp_window_set_transient (GTK_WINDOW (dialog));
661 
662   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
663   gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
664   gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
665                       vbox, TRUE, TRUE, 0);
666   gtk_widget_show (vbox);
667 
668   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
669   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
670   gtk_widget_show (hbox);
671 
672   frame = gimp_int_radio_group_new (TRUE, _("Effect Channel"),
673                                     G_CALLBACK (gimp_radio_button_update),
674                                     &licvals.effect_channel,
675                                     licvals.effect_channel,
676 
677                                     _("_Hue"),        0, NULL,
678                                     _("_Saturation"), 1, NULL,
679                                     _("_Brightness"), 2, NULL,
680 
681                                     NULL);
682   gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
683   gtk_widget_show (frame);
684 
685   frame = gimp_int_radio_group_new (TRUE, _("Effect Operator"),
686                                     G_CALLBACK (gimp_radio_button_update),
687                                     &licvals.effect_operator,
688                                     licvals.effect_operator,
689 
690                                     _("_Derivative"), 0, NULL,
691                                     _("_Gradient"),   1, NULL,
692 
693                                     NULL);
694   gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
695   gtk_widget_show (frame);
696 
697   frame = gimp_int_radio_group_new (TRUE, _("Convolve"),
698                                     G_CALLBACK (gimp_radio_button_update),
699                                     &licvals.effect_convolve,
700                                     licvals.effect_convolve,
701 
702                                     _("_With white noise"),  0, NULL,
703                                     _("W_ith source image"), 1, NULL,
704 
705                                     NULL);
706   gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
707   gtk_widget_show (frame);
708 
709   /* Effect image menu */
710   table = gtk_table_new (1, 2, FALSE);
711   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
712   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
713   gtk_widget_show (table);
714 
715   combo = gimp_drawable_combo_box_new (effect_image_constrain, NULL);
716   gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
717                               licvals.effect_image_id,
718                               G_CALLBACK (gimp_int_combo_box_get_active),
719                               &licvals.effect_image_id);
720 
721   gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
722                              _("_Effect image:"), 0.0, 0.5, combo, 2, TRUE);
723 
724   table = gtk_table_new (5, 3, FALSE);
725   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
726   gtk_table_set_row_spacings (GTK_TABLE (table), 6);
727   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
728   gtk_widget_show (table);
729 
730   row = 0;
731 
732   scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
733                                      _("_Filter length:"), 0, 6,
734                                      licvals.filtlen, 0.1, 64, 1.0, 8.0, 1,
735                                      TRUE, 0, 0,
736                                      NULL, NULL);
737   g_signal_connect (scale_data, "value-changed",
738                     G_CALLBACK (gimp_double_adjustment_update),
739                     &licvals.filtlen);
740 
741   scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
742                                      _("_Noise magnitude:"), 0, 6,
743                                      licvals.noisemag, 1, 5, 0.1, 1.0, 1,
744                                      TRUE, 0, 0,
745                                      NULL, NULL);
746   g_signal_connect (scale_data, "value-changed",
747                     G_CALLBACK (gimp_double_adjustment_update),
748                     &licvals.noisemag);
749 
750   scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
751                                      _("In_tegration steps:"), 0, 6,
752                                      licvals.intsteps, 1, 40, 1.0, 5.0, 1,
753                                      TRUE, 0, 0,
754                                      NULL, NULL);
755   g_signal_connect (scale_data, "value-changed",
756                     G_CALLBACK (gimp_double_adjustment_update),
757                     &licvals.intsteps);
758 
759   scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
760                                      _("_Minimum value:"), 0, 6,
761                                      licvals.minv, -100, 0, 1, 10, 1,
762                                      TRUE, 0, 0,
763                                      NULL, NULL);
764   g_signal_connect (scale_data, "value-changed",
765                     G_CALLBACK (gimp_double_adjustment_update),
766                     &licvals.minv);
767 
768   scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
769                                      _("M_aximum value:"), 0, 6,
770                                      licvals.maxv, 0, 100, 1, 10, 1,
771                                      TRUE, 0, 0,
772                                      NULL, NULL);
773   g_signal_connect (scale_data, "value-changed",
774                     G_CALLBACK (gimp_double_adjustment_update),
775                     &licvals.maxv);
776 
777   gtk_widget_show (dialog);
778 
779   run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
780 
781   gtk_widget_destroy (dialog);
782 
783   return run;
784 }
785 
786 /*************************************/
787 /* Set parameters to standard values */
788 /*************************************/
789 
790 static void
set_default_settings(void)791 set_default_settings (void)
792 {
793   licvals.filtlen          = 5;
794   licvals.noisemag         = 2;
795   licvals.intsteps         = 25;
796   licvals.minv             = -25;
797   licvals.maxv             = 25;
798   licvals.effect_channel   = 2;
799   licvals.effect_operator  = 1;
800   licvals.effect_convolve  = 1;
801   licvals.effect_image_id  = 0;
802 }
803 
804 static void
query(void)805 query (void)
806 {
807   static const GimpParamDef args[] =
808   {
809     { GIMP_PDB_INT32,    "run-mode", "The run mode { RUN-INTERACTIVE (0) }"    },
810     { GIMP_PDB_IMAGE,    "image",    "Input image"    },
811     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" }
812   };
813 
814   gimp_install_procedure (PLUG_IN_PROC,
815                           N_("Special effects that nobody understands"),
816                           "No help yet",
817                           "Tom Bech & Federico Mena Quintero",
818                           "Tom Bech & Federico Mena Quintero",
819                           "Version 0.14, September 24 1997",
820                           N_("_Van Gogh (LIC)..."),
821                           "RGB*",
822                           GIMP_PLUGIN,
823                           G_N_ELEMENTS (args), 0,
824                           args, NULL);
825 
826   gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Artistic");
827 }
828 
829 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)830 run (const gchar      *name,
831      gint              nparams,
832      const GimpParam  *param,
833      gint             *nreturn_vals,
834      GimpParam       **return_vals)
835 {
836   static GimpParam   values[1];
837   GimpRunMode        run_mode;
838   gint32             drawable_ID;
839   GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
840 
841   INIT_I18N ();
842   gegl_init (NULL, NULL);
843 
844   *nreturn_vals = 1;
845   *return_vals  = values;
846 
847   values[0].type          = GIMP_PDB_STATUS;
848   values[0].data.d_status = status;
849 
850   /* Set default values */
851   /* ================== */
852 
853   set_default_settings ();
854 
855   /* Possibly retrieve data */
856   /* ====================== */
857 
858   gimp_get_data (PLUG_IN_PROC, &licvals);
859 
860   run_mode    = param[0].data.d_int32;
861   drawable_ID = param[2].data.d_drawable;
862 
863   if (status == GIMP_PDB_SUCCESS)
864     {
865       /* Make sure that the drawable is RGBA or RGB color */
866       /* ================================================ */
867 
868       if (gimp_drawable_is_rgb (drawable_ID))
869         {
870           switch (run_mode)
871             {
872             case GIMP_RUN_INTERACTIVE:
873               if (create_main_dialog ())
874                 compute_image (drawable_ID);
875 
876               gimp_set_data (PLUG_IN_PROC, &licvals, sizeof (LicValues));
877               break;
878 
879             case GIMP_RUN_WITH_LAST_VALS:
880               compute_image (drawable_ID);
881               break;
882 
883             default:
884               break;
885             }
886         }
887       else
888         {
889           status = GIMP_PDB_EXECUTION_ERROR;
890         }
891     }
892 
893   values[0].data.d_status = status;
894 }
895 
896 const GimpPlugInInfo PLUG_IN_INFO =
897 {
898   NULL,  /* init_proc  */
899   NULL,  /* quit_proc  */
900   query, /* query_proc */
901   run,   /* run_proc   */
902 };
903 
904 MAIN ()
905