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