diff --git a/ChangeLog b/ChangeLog index 113a0dd117..b9f2b56789 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,37 @@ +Thu Nov 5 16:42:21 PST 1998 Manish Singh + + * Makefile.am: added MAINTAINERS to EXTRA_DIST + + * configure.in: nicer -Wall, -ansi, etc. adding for CFLAGS with gcc + + * libgimp/parasite*.h + * libgimp/gimpintl.h + * app/Makefile.am + * po/Makefile.in.in + * configure.in: portablity, locale patch (gimp-joke-981028-0) + + * configure.in + * plug-ins/Makefile.am: added lic, mapcolor, and xbm plug-ins + + * app/app_procs.c + * app/menus.c: cosmetic message fixes + + * libgimp/parasite.c: use %p to print pointers + + * plug-ins/dbbrowser/dbbrowser_utils.[ch]: changes for new clist + stuff + + * plug-ins/script-fu/script-fu-enums.h + * plug-ins/script-fu/script-fu-scripts.c + * plug-ins/script-fu/script-fu.c + * plug-ins/script-fu/scripts/carved-logo.scm + * plug-ins/script-fu/scripts/chrome-it.scm + * plug-ins/script-fu/scripts/crystal-logo.scm + * plug-ins/script-fu/scripts/neon-logo.scm + * plug-ins/script-fu/scripts/sota-chrome-logo.scm: applied + gimp-ruth-981103-0, adds a filesel to script-fu and SF-FILENAME + param type. Make some scripts use this. + Thu Nov 5 21:55:46 GMT 1998 Andy Thomas * app/gradient.c @@ -509,6 +543,7 @@ Mon Oct 5 01:08:11 CST 1998 Seth Burgess Many thanks to Marc Lehman for reporting these. More to come. Mon Oct 5 23:56:09 EEST 1998 Lauri Alanko + * po/fr.po: fixed a compile-halting typo Mon Oct 5 16:18:08 EDT 1998 Matthew Wilson diff --git a/Makefile.am b/Makefile.am index 47499dd784..ac013102fe 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,9 +4,32 @@ SUBDIRS = po intl libgimp plug-ins app data bin_SCRIPTS = gimptool -EXTRA_DIST = TODO gtkrc gimp_logo.ppm gimp_splash.ppm gimp1_1_splash.ppm rmshm user_install gimp_tips.txt ps-menurc gimp.1 gimptool.1 gtkrc.forest2 gimp.m4 +EXTRA_DIST = \ + MAINTAINERS \ + TODO \ + gtkrc \ + gimp_logo.ppm \ + gimp_splash.ppm \ + gimp1_1_splash.ppm \ + rmshm \ + user_install \ + gimp_tips.txt \ + ps-menurc \ + gimp.1 \ + gimptool.1 \ + gtkrc.forest2 \ + gimp.m4 -gimpdata_DATA = gimprc gimprc_user gtkrc gimp_logo.ppm gimp_splash.ppm gimp1_1_splash.ppm gimp_tips.txt ps-menurc gtkrc.forest2 +gimpdata_DATA = \ + gimprc \ + gimprc_user \ + gtkrc \ + gimp_logo.ppm \ + gimp_splash.ppm \ + gimp1_1_splash.ppm \ + gimp_tips.txt \ + ps-menurc \ + gtkrc.forest2 gimpdata_SCRIPTS = user_install diff --git a/app/Makefile.am b/app/Makefile.am index fd95acdef0..42bb9f3bf7 100644 --- a/app/Makefile.am +++ b/app/Makefile.am @@ -336,7 +336,8 @@ EXTRA_DIST = \ CPPFLAGS = \ -DLIBDIR=\""$(gimpplugindir)"\" \ -DDATADIR=\""$(gimpdatadir)"\" \ - -DGIMPDIR=\""$(gimpdir)"\" + -DGIMPDIR=\""$(gimpdir)"\" \ + -DLOCALEDIR=\""$(localedir)"\" INCLUDES = \ -I$(top_srcdir) \ diff --git a/app/app_procs.c b/app/app_procs.c index 58924991c7..eb67bbf21a 100644 --- a/app/app_procs.c +++ b/app/app_procs.c @@ -458,7 +458,7 @@ app_init (void) sprintf (filename, "%s/gtkrc", gimp_dir); if ((be_verbose == TRUE) || (no_splash == TRUE)) - g_print (_("parsing %s\n"), filename); + g_print (_("parsing \"%s\"\n"), filename); gtk_rc_parse (filename); } diff --git a/app/gui/menus.c b/app/gui/menus.c index f0c60b19da..68edeb32de 100644 --- a/app/gui/menus.c +++ b/app/gui/menus.c @@ -343,7 +343,7 @@ menus_set_sensitive (char *path, gtk_widget_set_sensitive (widget, sensitive); } if (!ifactory || !widget) - printf (_("Unable to set sensitivity for menu which doesn't exist:\n%s"), path); + g_message (_("Unable to set sensitivity for menu which doesn't exist:\n%s"), path); } void @@ -368,7 +368,7 @@ menus_set_state (char *path, widget = NULL; } if (!ifactory || !widget) - printf (_("Unable to set state for menu which doesn't exist:\n%s"), path); + g_message (_("Unable to set state for menu which doesn't exist:\n%s\n"), path); } void diff --git a/app/menus.c b/app/menus.c index f0c60b19da..68edeb32de 100644 --- a/app/menus.c +++ b/app/menus.c @@ -343,7 +343,7 @@ menus_set_sensitive (char *path, gtk_widget_set_sensitive (widget, sensitive); } if (!ifactory || !widget) - printf (_("Unable to set sensitivity for menu which doesn't exist:\n%s"), path); + g_message (_("Unable to set sensitivity for menu which doesn't exist:\n%s"), path); } void @@ -368,7 +368,7 @@ menus_set_state (char *path, widget = NULL; } if (!ifactory || !widget) - printf (_("Unable to set state for menu which doesn't exist:\n%s"), path); + g_message (_("Unable to set state for menu which doesn't exist:\n%s\n"), path); } void diff --git a/app/menus/menus.c b/app/menus/menus.c index f0c60b19da..68edeb32de 100644 --- a/app/menus/menus.c +++ b/app/menus/menus.c @@ -343,7 +343,7 @@ menus_set_sensitive (char *path, gtk_widget_set_sensitive (widget, sensitive); } if (!ifactory || !widget) - printf (_("Unable to set sensitivity for menu which doesn't exist:\n%s"), path); + g_message (_("Unable to set sensitivity for menu which doesn't exist:\n%s"), path); } void @@ -368,7 +368,7 @@ menus_set_state (char *path, widget = NULL; } if (!ifactory || !widget) - printf (_("Unable to set state for menu which doesn't exist:\n%s"), path); + g_message (_("Unable to set state for menu which doesn't exist:\n%s\n"), path); } void diff --git a/app/widgets/gimpitemfactory.c b/app/widgets/gimpitemfactory.c index f0c60b19da..68edeb32de 100644 --- a/app/widgets/gimpitemfactory.c +++ b/app/widgets/gimpitemfactory.c @@ -343,7 +343,7 @@ menus_set_sensitive (char *path, gtk_widget_set_sensitive (widget, sensitive); } if (!ifactory || !widget) - printf (_("Unable to set sensitivity for menu which doesn't exist:\n%s"), path); + g_message (_("Unable to set sensitivity for menu which doesn't exist:\n%s"), path); } void @@ -368,7 +368,7 @@ menus_set_state (char *path, widget = NULL; } if (!ifactory || !widget) - printf (_("Unable to set state for menu which doesn't exist:\n%s"), path); + g_message (_("Unable to set state for menu which doesn't exist:\n%s\n"), path); } void diff --git a/configure.in b/configure.in index ea1200f092..cac05dcebb 100644 --- a/configure.in +++ b/configure.in @@ -66,40 +66,41 @@ ALL_LINGUAS="fr sv ja it fi ko de" AM_GNU_GETTEXT dnl Check for GTK+ -AM_PATH_GTK(1.1.2,, +AM_PATH_GTK(1.1.3,, AC_MSG_ERROR(Test for GTK failed. See the file 'INSTALL' for help.)) +changequote(,)dnl + dnl Evil stuff to extract GLIB stuff from gtk-config output dnl (we want to make sure it matches with the gtk we're using) -GLIB_CFLAGS=`echo $GTK_CFLAGS | sed 's/^.*\(-I[[^ ]]*glib[[^ ]]* *-I[[^ ]]*\).*$/\1/'` -GLIB_LDFLAGS=`echo $gtk_libs | sed -e 's/^.*-lgdk[[^ ]]* *\(-L[[^ ]]*\).*$/\1/' -e 's/^.* -lgdk[[^ ]]* .*$//'` +GLIB_CFLAGS=`echo $GTK_CFLAGS | sed 's/^.*\(-I[^ ]*glib[^ ]* *-I[^ ]*\).*$/\1/'` +GLIB_LDFLAGS=`echo $gtk_libs | sed -e 's/^.*-lgdk[^ ]* *\(-L[^ ]*\).*$/\1/' -e 's/^.* -lgdk[^ ]* .*$//'` if test -z "$glib_ldflags" ; then - GLIB_LDFLAGS=`echo $GTK_LIBS | sed 's/^ *\(-L[[^ ]]*\) .*$/\1/'` + GLIB_LDFLAGS=`echo $GTK_LIBS | sed 's/^ *\(-L[^ ]*\) .*$/\1/'` fi -GLIB_LIBS="$GLIB_LDFLAGS `echo $GTK_LIBS | sed 's/^.*\(-lglib[[^ ]]*\).*$/\1/'`" +GLIB_LIBS="$GLIB_LDFLAGS `echo $GTK_LIBS | sed 's/^.*\(-lglib[^ ]*\).*$/\1/'`" if eval "test x$GCC = xyes"; then - if echo "$CFLAGS" | grep "\-Wall" > /dev/null 2> /dev/null; then - CFLAGS="$CFLAGS" - else - CFLAGS="$CFLAGS -Wall" - fi + case " $CFLAGS " in + *[\ \ ]-Wall[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -Wall" ;; + esac - if eval "test x$enable_ansi = xyes"; then - if echo "$CFLAGS" | grep "\-ansi" > /dev/null 2> /dev/null; then - CFLAGS="$CFLAGS" - else - CFLAGS="$CFLAGS -ansi" - fi + if test "x$enable_ansi" = "xyes"; then + case " $CFLAGS " in + *[\ \ ]-ansi[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -ansi" ;; + esac - if echo "$CFLAGS" | grep "\-pedantic" > /dev/null 2> /dev/null; then - CFLAGS="$CFLAGS" - else - CFLAGS="$CFLAGS -pedantic" - fi + case " $CFLAGS " in + *[\ \ ]-pedantic[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -pedantic" ;; + esac fi fi +changequote([,])dnl + dnl DU4 native cc currently needs -std1 for ANSI mode (instead of K&R) AC_MSG_CHECKING([for extra flags to get ANSI library prototypes]) gimp_save_LIBS=$LIBS @@ -384,6 +385,7 @@ LIBS="$gimp_save_LIBS" gimpdatadir=$datadir/gimp gimpplugindir=$libdir/$PACKAGE/$GIMP_MAJOR_VERSION.$GIMP_MINOR_VERSION +localedir=$datadir/locale brushdata=`ls -1 $srcdir/data/brushes | grep -v Makefile` gradientdata=`ls -1 $srcdir/data/gradients | grep -v Makefile` @@ -547,6 +549,7 @@ AC_SUBST(GLIB_LIBS) AC_SUBST(gimpdir) AC_SUBST(gimpdatadir) AC_SUBST(gimpplugindir) +AC_SUBST(localedir) AC_SUBST(brushdata) AC_SUBST(gradientdata) AC_SUBST(palettedata) @@ -669,7 +672,9 @@ plug-ins/illusion/Makefile plug-ins/iwarp/Makefile plug-ins/jigsaw/Makefile plug-ins/laplace/Makefile +plug-ins/lic/Makefile plug-ins/mail/Makefile +plug-ins/mapcolor/Makefile plug-ins/max_rgb/Makefile plug-ins/maze/Makefile plug-ins/mblur/Makefile @@ -725,6 +730,7 @@ plug-ins/waterselect/Makefile plug-ins/waves/Makefile plug-ins/whirlpinch/Makefile plug-ins/wind/Makefile +plug-ins/xbm/Makefile plug-ins/xwd/Makefile plug-ins/zealouscrop/Makefile app/Makefile diff --git a/libgimp/gimpintl.h b/libgimp/gimpintl.h index 6434dd6666..604529120c 100644 --- a/libgimp/gimpintl.h +++ b/libgimp/gimpintl.h @@ -1,8 +1,6 @@ #ifndef __GIMPINTL_H__ #define __GIMPINTL_H__ -#define LOCALEDIR "/usr/local/share/locale" - /* Copied from gnome-i18n.h by Tom Tromey */ #ifdef ENABLE_NLS diff --git a/libgimp/gimpparasite.c b/libgimp/gimpparasite.c index 9678547d78..15a4f77373 100644 --- a/libgimp/gimpparasite.c +++ b/libgimp/gimpparasite.c @@ -19,6 +19,8 @@ #include "parasiteP.h" #include "parasite.h" +#include +#include #include #include @@ -29,7 +31,7 @@ static void parasite_print(Parasite *p) printf("(pid %d)attempt to print a null parasite\n", getpid()); return; } - printf("(pid %d), parasite: %X\n", getpid(), p); + printf("(pid %d), parasite: %p\n", getpid(), p); if (p->name) printf("\tname: %s\n", p->name); else @@ -37,7 +39,7 @@ static void parasite_print(Parasite *p) printf("\tflags: %d\n", p->flags); printf("\tsize: %d\n", p->size); if (p->size > 0) - printf("\tdata: %X\n", p->data); + printf("\tdata: %p\n", p->data); } Parasite * diff --git a/libgimp/gimpparasite.h b/libgimp/gimpparasite.h index 92e7dea1c2..e9295cefcb 100644 --- a/libgimp/gimpparasite.h +++ b/libgimp/gimpparasite.h @@ -46,4 +46,4 @@ int parasite_is_persistant (const Parasite *p); } #endif /* __cplusplus */ -#endif _PARASITE_H_ +#endif /* _PARASITE_H_ */ diff --git a/libgimp/gimpparasiteF.h b/libgimp/gimpparasiteF.h index 5bf5775764..32008f5c8c 100644 --- a/libgimp/gimpparasiteF.h +++ b/libgimp/gimpparasiteF.h @@ -22,4 +22,4 @@ typedef struct _Parasite Parasite; -#endif _PARASITEF_H_ +#endif /* _PARASITEF_H_ */ diff --git a/libgimp/gimpparasiteP.h b/libgimp/gimpparasiteP.h index 5045050907..af163e8600 100644 --- a/libgimp/gimpparasiteP.h +++ b/libgimp/gimpparasiteP.h @@ -31,4 +31,4 @@ struct _Parasite * responsible for tracking byte order */ }; -#endif _PARASITEP_H_ +#endif /* _PARASITEP_H_ */ diff --git a/libgimp/gimpprocbrowserdialog.c b/libgimp/gimpprocbrowserdialog.c index 461050f800..a35056331a 100644 --- a/libgimp/gimpprocbrowserdialog.c +++ b/libgimp/gimpprocbrowserdialog.c @@ -82,8 +82,9 @@ gimp_db_browser(void (* apply_callback) ( gchar *selected_proc_name, /* list : list in a scrolled_win */ dbbrowser->clist = gtk_clist_new(1); - gtk_clist_set_policy (GTK_CLIST (dbbrowser->clist), GTK_POLICY_AUTOMATIC, - GTK_POLICY_AUTOMATIC); + dbbrowser->scrolled_win = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (dbbrowser->scrolled_win), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_clist_set_selection_mode (GTK_CLIST (dbbrowser->clist), GTK_SELECTION_BROWSE); @@ -91,9 +92,10 @@ gimp_db_browser(void (* apply_callback) ( gchar *selected_proc_name, gtk_signal_connect (GTK_OBJECT (dbbrowser->clist), "select_row", (GtkSignalFunc) procedure_select_callback, dbbrowser); - gtk_box_pack_start (GTK_BOX (vbox), - dbbrowser->clist, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (vbox), dbbrowser->scrolled_win, TRUE, TRUE, 0); + gtk_container_add (GTK_CONTAINER (dbbrowser->scrolled_win), dbbrowser->clist); gtk_widget_show(dbbrowser->clist); + gtk_widget_show(dbbrowser->scrolled_win); /* search entry */ diff --git a/libgimp/gimpprocview.c b/libgimp/gimpprocview.c index 461050f800..a35056331a 100644 --- a/libgimp/gimpprocview.c +++ b/libgimp/gimpprocview.c @@ -82,8 +82,9 @@ gimp_db_browser(void (* apply_callback) ( gchar *selected_proc_name, /* list : list in a scrolled_win */ dbbrowser->clist = gtk_clist_new(1); - gtk_clist_set_policy (GTK_CLIST (dbbrowser->clist), GTK_POLICY_AUTOMATIC, - GTK_POLICY_AUTOMATIC); + dbbrowser->scrolled_win = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (dbbrowser->scrolled_win), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_clist_set_selection_mode (GTK_CLIST (dbbrowser->clist), GTK_SELECTION_BROWSE); @@ -91,9 +92,10 @@ gimp_db_browser(void (* apply_callback) ( gchar *selected_proc_name, gtk_signal_connect (GTK_OBJECT (dbbrowser->clist), "select_row", (GtkSignalFunc) procedure_select_callback, dbbrowser); - gtk_box_pack_start (GTK_BOX (vbox), - dbbrowser->clist, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (vbox), dbbrowser->scrolled_win, TRUE, TRUE, 0); + gtk_container_add (GTK_CONTAINER (dbbrowser->scrolled_win), dbbrowser->clist); gtk_widget_show(dbbrowser->clist); + gtk_widget_show(dbbrowser->scrolled_win); /* search entry */ diff --git a/libgimp/parasite.c b/libgimp/parasite.c index 9678547d78..15a4f77373 100644 --- a/libgimp/parasite.c +++ b/libgimp/parasite.c @@ -19,6 +19,8 @@ #include "parasiteP.h" #include "parasite.h" +#include +#include #include #include @@ -29,7 +31,7 @@ static void parasite_print(Parasite *p) printf("(pid %d)attempt to print a null parasite\n", getpid()); return; } - printf("(pid %d), parasite: %X\n", getpid(), p); + printf("(pid %d), parasite: %p\n", getpid(), p); if (p->name) printf("\tname: %s\n", p->name); else @@ -37,7 +39,7 @@ static void parasite_print(Parasite *p) printf("\tflags: %d\n", p->flags); printf("\tsize: %d\n", p->size); if (p->size > 0) - printf("\tdata: %X\n", p->data); + printf("\tdata: %p\n", p->data); } Parasite * diff --git a/libgimp/parasite.h b/libgimp/parasite.h index 92e7dea1c2..e9295cefcb 100644 --- a/libgimp/parasite.h +++ b/libgimp/parasite.h @@ -46,4 +46,4 @@ int parasite_is_persistant (const Parasite *p); } #endif /* __cplusplus */ -#endif _PARASITE_H_ +#endif /* _PARASITE_H_ */ diff --git a/libgimp/parasiteF.h b/libgimp/parasiteF.h index 5bf5775764..32008f5c8c 100644 --- a/libgimp/parasiteF.h +++ b/libgimp/parasiteF.h @@ -22,4 +22,4 @@ typedef struct _Parasite Parasite; -#endif _PARASITEF_H_ +#endif /* _PARASITEF_H_ */ diff --git a/libgimp/parasiteP.h b/libgimp/parasiteP.h index 5045050907..af163e8600 100644 --- a/libgimp/parasiteP.h +++ b/libgimp/parasiteP.h @@ -31,4 +31,4 @@ struct _Parasite * responsible for tracking byte order */ }; -#endif _PARASITEP_H_ +#endif /* _PARASITEP_H_ */ diff --git a/libgimpbase/gimpparasite.c b/libgimpbase/gimpparasite.c index 9678547d78..15a4f77373 100644 --- a/libgimpbase/gimpparasite.c +++ b/libgimpbase/gimpparasite.c @@ -19,6 +19,8 @@ #include "parasiteP.h" #include "parasite.h" +#include +#include #include #include @@ -29,7 +31,7 @@ static void parasite_print(Parasite *p) printf("(pid %d)attempt to print a null parasite\n", getpid()); return; } - printf("(pid %d), parasite: %X\n", getpid(), p); + printf("(pid %d), parasite: %p\n", getpid(), p); if (p->name) printf("\tname: %s\n", p->name); else @@ -37,7 +39,7 @@ static void parasite_print(Parasite *p) printf("\tflags: %d\n", p->flags); printf("\tsize: %d\n", p->size); if (p->size > 0) - printf("\tdata: %X\n", p->data); + printf("\tdata: %p\n", p->data); } Parasite * diff --git a/libgimpbase/gimpparasite.h b/libgimpbase/gimpparasite.h index 92e7dea1c2..e9295cefcb 100644 --- a/libgimpbase/gimpparasite.h +++ b/libgimpbase/gimpparasite.h @@ -46,4 +46,4 @@ int parasite_is_persistant (const Parasite *p); } #endif /* __cplusplus */ -#endif _PARASITE_H_ +#endif /* _PARASITE_H_ */ diff --git a/plug-ins/Makefile.am b/plug-ins/Makefile.am index e856236c84..ee1bd6eb84 100644 --- a/plug-ins/Makefile.am +++ b/plug-ins/Makefile.am @@ -79,7 +79,9 @@ SUBDIRS = \ iwarp \ jigsaw \ laplace \ + lic \ mail \ + mapcolor \ max_rgb \ maze \ mblur \ @@ -135,6 +137,7 @@ SUBDIRS = \ waves \ whirlpinch \ wind \ + xbm \ xwd \ zealouscrop diff --git a/plug-ins/common/lic.c b/plug-ins/common/lic.c new file mode 100644 index 0000000000..299618c6b6 --- /dev/null +++ b/plug-ins/common/lic.c @@ -0,0 +1,1433 @@ +/*********************************************************************************/ +/* LIC 0.14 -- image filter plug-in for The Gimp program */ +/* Copyright (C) 1996 Tom Bech */ +/*===============================================================================*/ +/* E-mail: tomb@gimp.org */ +/* You can contact the original The Gimp authors at gimp@xcf.berkeley.edu */ +/*===============================================================================*/ +/* This program is free software; you can redistribute it and/or modify it under */ +/* the terms of the GNU General Public License as published by the Free Software */ +/* Foundation; either version 2 of the License, or (at your option) any later */ +/* version. */ +/*===============================================================================*/ +/* This program is distributed in the hope that it will be useful, but WITHOUT */ +/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS */ +/* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.*/ +/*===============================================================================*/ +/* You should have received a copy of the GNU General Public License along with */ +/* this program; if not, write to the Free Software Foundation, Inc., 675 Mass */ +/* Ave, Cambridge, MA 02139, USA. */ +/*===============================================================================*/ +/* In other words, you can't sue me for whatever happens while using this ;) */ +/*********************************************************************************/ +/* Changes (post 0.10): */ +/* -> 0.11: Fixed a bug in the convolution kernels (Tom). */ +/* -> 0.12: Added Quartic's bilinear interpolation stuff (Tom). */ +/* -> 0.13 Changed some UI stuff causing trouble with the 0.60 release, added */ +/* the (GIMP) tags and changed random() calls to rand() (Tom) */ +/* -> 0.14 Ported to 0.99.11 (Tom) */ +/*********************************************************************************/ +/* This plug-in implements the Line Integral Convolution (LIC) as described in */ +/* Cabral et al. "Imaging vector fields using line integral convolution" in the */ +/* Proceedings of ACM SIGGRAPH 93. Publ. by ACM, New York, NY, USA. p. 263-270. */ +/* Some of the code is based on code by Steinar Haugen (thanks!), the Perlin */ +/* noise function is practically ripped as is :) */ +/*********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +/************/ +/* Typedefs */ +/************/ + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#define CHECKBOUNDS(x,y) (x>=0 && y>=0 && xr=a->r+b->r; + a->g=a->g+b->g; + a->b=a->b+b->b; +} + +void rgb_mul(rgbpixel *a,gdouble b) +{ + a->r=a->r*b; + a->g=a->g*b; + a->b=a->b*b; +} + +void rgb_clamp(rgbpixel *a) +{ + if (a->r>1.0) + a->r=1.0; + + if (a->g>1.0) + a->g=1.0; + + if (a->b>1.0) + a->b=1.0; + + if (a->r<0.0) + a->r=0.0; + + if (a->g<0.0) + a->g=0.0; + + if (a->b<0.0) + a->b=0.0; +} + +void set_color(rgbpixel *a,gdouble r,gdouble g,gdouble b) +{ + a->r=r; a->g=g; a->b=b; +} + +glong xy_to_index(gint x,gint y) +{ + return((glong)in_channels*((glong)x+(glong)y*(glong)width)); +} + +gboolean checkbounds(gint x,gint y) +{ + if (x<0 || y<0 || x>width-1 || y>height-1) + return(FALSE); + + return(TRUE); +} + +rgbpixel peek(gint x,gint y) +{ + static guchar data[4]; + rgbpixel color; + + gimp_pixel_rgn_get_pixel(&source_region,data,x,y); + + color.r=(gdouble)(data[0])/255.0; + color.g=(gdouble)(data[1])/255.0; + color.b=(gdouble)(data[2])/255.0; + + if (input_drawable->bpp==4) + { + if (in_channels==4) + color.a=(gdouble)(data[3])/255.0; + else + color.a=1.0; + } + else + color.a=1.0; + + return(color); +} + +void poke(gint x,gint y,rgbpixel *color) +{ + static guchar data[4]; + + data[0]=(guchar)(color->r*255.0); + data[1]=(guchar)(color->g*255.0); + data[2]=(guchar)(color->b*255.0); + data[3]=(guchar)(color->a*255.0); + + gimp_pixel_rgn_set_pixel(&dest_region,data,x,y); +} + +void pos_to_int(gdouble x,gdouble y,gint *scr_x,gint *scr_y) +{ + *scr_x=(gint)((x*(gdouble)width)); + *scr_y=(gint)((y*(gdouble)height)); +} + +/****************************************/ +/* Allocate memory for temporary images */ +/****************************************/ + +gint image_setup(GDrawable *drawable,gint interactive) +{ + /* Get some useful info on the input drawable */ + /* ========================================== */ + + input_drawable=drawable; + output_drawable=drawable; + + gimp_drawable_mask_bounds (drawable->id, &border_x1, &border_y1, &border_x2, &border_y2); + + width=input_drawable->width; + height=input_drawable->height; + + gimp_pixel_rgn_init (&source_region, input_drawable, 0, 0, width, height, FALSE, FALSE); + + maxcounter=(glong)width*(glong)height; + + /* Assume at least RGB */ + /* =================== */ + + in_channels=3; + if (gimp_drawable_has_alpha(input_drawable->id)==TRUE) + in_channels++; + + if (interactive==TRUE) + { + /* Allocate memory for temp. images */ + /* ================================ */ + } + + return(TRUE); +} + +guchar peekmap(guchar *image,gint x,gint y) +{ + glong index; + + index=(glong)x+(glong)width*(glong)y; + + return(image[index]); +} + +/*************/ +/* Main part */ +/*************/ + +/***************************************************/ +/* Compute the derivative in the x and y direction */ +/* We use these convolution kernels: */ +/* |1 0 -1| | 1 2 1| */ +/* DX: |2 0 -2| DY: | 0 0 0| */ +/* |1 0 -1| | -1 -2 -1| */ +/* (It's a varation of the Sobel kernels, really) */ +/***************************************************/ + +gint gradx(guchar *image,gint x,gint y) +{ + gint val=0; + + if (CHECKBOUNDS(x-1,y-1)==TRUE) + val=val+(gint)peekmap(image,x-1,y-1); + if (CHECKBOUNDS(x+1,y-1)==TRUE) + val=val-(gint)peekmap(image,x+1,y-1); + + if (CHECKBOUNDS(x-1,y)==TRUE) + val=val+2*(gint)peekmap(image,x-1,y); + if (CHECKBOUNDS(x+1,y)==TRUE) + val=val-2*(gint)peekmap(image,x+1,y); + + if (CHECKBOUNDS(x-1,y+1)==TRUE) + val=val+(gint)peekmap(image,x-1,y+1); + if (CHECKBOUNDS(x+1,y+1)==TRUE) + val=val-(gint)peekmap(image,x+1,y+1); + + return(val); +} + +gint grady(guchar *image,gint x,gint y) +{ + gint val=0; + + if (CHECKBOUNDS(x-1,y-1)==TRUE) + val=val+(gint)peekmap(image,x-1,y-1); + if (CHECKBOUNDS(x,y-1)==TRUE) + val=val+2*(gint)peekmap(image,x,y-1); + if (CHECKBOUNDS(x+1,y-1)==TRUE) + val=val+(gint)peekmap(image,x+1,y-1); + + if (CHECKBOUNDS(x-1,y+1)==TRUE) + val=val-(gint)peekmap(image,x-1,y+1); + if (CHECKBOUNDS(x,y+1)==TRUE) + val=val-2*(gint)peekmap(image,x,y+1); + if (CHECKBOUNDS(x+1,y+1)==TRUE) + val=val-(gint)peekmap(image,x+1,y+1); + + return(val); +} + +/************************************/ +/* A nice 2nd order cubic spline :) */ +/************************************/ + +gdouble cubic(gdouble t) +{ + gdouble at=fabs(t); + + if (at<1.0) + return(2.0*at*at*at-3.0*at*at+1.0); + + return(0.0); +} + +gdouble omega(gdouble u,gdouble v,gint i,gint j) +{ + while (i<0) + i+=numx; + + while (j<0) + j+=numy; + + i%=numx; + j%=numy; + + return(cubic(u)*cubic(v)*(G[i][j][0]*u+G[i][j][1]*v)); +} + +/*************************************************************/ +/* The noise function (2D variant of Perlins noise function) */ +/*************************************************************/ + +gdouble noise(gdouble x,gdouble y) +{ + gint i,sti=(gint)floor(x/dx); + gint j,stj=(gint)floor(y/dy); + + gdouble sum=0.0; + + /* Calculate the gdouble sum */ + /* ======================== */ + + for (i=sti; i<=sti+1; i++) + { + for (j=stj; j<=stj+1; j++) + sum+=omega((x-(gdouble)i*dx)/dx,(y-(gdouble)j*dy)/dy,i,j); + } + + return(sum); +} + +/*************************************************/ +/* Generates pseudo-random vectors with length 1 */ +/*************************************************/ + +void generatevectors(void) +{ + gdouble alpha; + gint i,j; + + for (i=0; i1.0) + i=1.0; + + i=(i/2.0)+0.5; + + return(i); +} + +static rgbpixel bilinear(gdouble x, gdouble y, rgbpixel *p) +{ + gdouble m0, m1; + gdouble ix, iy; + rgbpixel v; + + x = fmod(x, 1.0); + y = fmod(y, 1.0); + + if (x < 0) + x += 1.0; + + if (y < 0) + y += 1.0; + + ix = 1.0 - x; + iy = 1.0 - y; + + /* Red */ + /* === */ + + m0 = ix * p[0].r + x * p[1].r; + m1 = ix * p[2].r + x * p[3].r; + + v.r = iy * m0 + y * m1; + + /* Green */ + /* ===== */ + + m0 = ix * p[0].g + x * p[1].g; + m1 = ix * p[2].g + x * p[3].g; + + v.g = iy * m0 + y * m1; + + /* Blue */ + /* ==== */ + + m0 = ix * p[0].b + x * p[1].b; + m1 = ix * p[2].b + x * p[3].b; + + v.b = iy * m0 + y * m1; + + return(v); +} /* bilinear */ + +void getpixel(rgbpixel *p,gdouble u,gdouble v) +{ + register gint x1, y1, x2, y2; + static rgbpixel pp[4]; + + x1 = (gint)u; + y1 = (gint)v; + + if (x1 < 0) + x1 = width - (-x1 % width); + else + x1 = x1 % width; + + if (y1 < 0) + y1 = height - (-y1 % height); + else + y1 = y1 % height; + + x2 = (x1 + 1) % width; + y2 = (y1 + 1) % height; + + pp[0] = peek(x1, y1); + pp[1] = peek(x2, y1); + pp[2] = peek(x1, y2); + pp[3] = peek(x2, y2); + + *p=bilinear(u,v,pp); +} + +void lic_image(gint x,gint y,gdouble vx,gdouble vy,rgbpixel *color) +{ + gdouble u,step=2.0*l/isteps; + gdouble xx=(gdouble)x,yy=(gdouble)y; + gdouble c,s; + rgbpixel col,col1,col2,col3; + + /* Get vector at x,y */ + /* ================= */ + + c = vx; + s = vy; + + /* Calculate integral numerically */ + /* ============================== */ + + col=black; + getpixel(&col1,xx+l*c,yy+l*s); + rgb_mul(&col1,filter(-l)); + + for (u=-l+step; u<=l; u+=step) + { + getpixel(&col2,xx-u*c,yy-u*s); + rgb_mul(&col2,filter(u)); + + col3=col1; + rgb_add(&col3,&col2); + rgb_mul(&col3,0.5*step); + rgb_add(&col,&col3); + + col1=col2; + } + + rgb_mul(&col,1.0/l); + rgb_clamp(&col); + + *color=col; +} + +gdouble maximum(gdouble a,gdouble b,gdouble c) +{ + gdouble max=a; + + if (b>max) + max=b; + if (c>max) + max=c; + + return(max); +} + +gdouble minimum(gdouble a,gdouble b,gdouble c) +{ + gdouble min=a; + + if (br,col->g,col->b); + min=minimum(col->r,col->g,col->b); + + if (max==min) *hue=-1.0; + else + { + delta=max-min; + if (col->r==max) + *hue=(col->g-col->b)/delta; + else if (col->g==max) + *hue=2.0+(col->b-col->r)/delta; + else if (col->b==max) + *hue=4.0+(col->r-col->g)/delta; + + *hue=*hue*60.0; + if (*hue<0.0) + *hue=*hue+360.0; + } +} + +void get_saturation(rgbpixel *col,gdouble *sat) +{ + gdouble max,min,l; + + max=maximum(col->r,col->g,col->b); + min=minimum(col->r,col->g,col->b); + + if (max==min) + *sat=0.0; + else + { + l=(max+min)/2.0; + if (l<=0.5) + *sat=(max-min)/(max+min); + else + *sat=(max-min)/(2.0-max-min); + } +} + +void get_brightness(rgbpixel *col,gdouble *bri) +{ + gdouble max,min; + + max=maximum(col->r,col->g,col->b); + min=minimum(col->r,col->g,col->b); + + *bri=(max+min)/2.0; +} + +void rgb_to_hue(GDrawable *image,guchar **map) +{ + guchar *themap,data[4]; + gint w,h,x,y; + rgbpixel color; + gdouble val; + glong maxc,index=0; + GPixelRgn region; + + w=image->width; + h=image->height; + maxc=(glong)w*(glong)h; + +/* gimp_drawable_mask_bounds (drawable->id, &border_x1, &border_y1, &border_x2, &border_y2); */ + + gimp_pixel_rgn_init (®ion, image, 0, 0, w, h, FALSE, FALSE); + + themap=(guchar *)malloc((size_t)maxc*sizeof(guchar)); + + for (y=0;ywidth; + h=image->height; + maxc=(glong)w*(glong)h; + +/* gimp_drawable_mask_bounds (drawable->id, &border_x1, &border_y1, &border_x2, &border_y2); */ + + gimp_pixel_rgn_init (®ion, image, 0, 0, w, h, FALSE, FALSE); + + themap=(guchar *)malloc((size_t)maxc*sizeof(guchar)); + + for (y=0;ywidth; + h=image->height; + maxc=(glong)w*(glong)h; + +/* gimp_drawable_mask_bounds (drawable->id, &border_x1, &border_y1, &border_x2, &border_y2); */ + + gimp_pixel_rgn_init (®ion, image, 0, 0, w, h, FALSE, FALSE); + + themap=(guchar *)malloc((size_t)maxc*sizeof(guchar)); + + for (y=0;yid, TRUE); + gimp_drawable_update(output_drawable->id, 0,0, width,height); + + if (new_image_id!=-1) + { + gimp_display_new(new_image_id); + gimp_displays_flush(); + gimp_drawable_detach(output_drawable); + } +} + +/**************************/ +/* Below is only UI stuff */ +/**************************/ + +void ok_button_clicked(GtkWidget *widget, gpointer client_data) +{ + gtk_widget_hide((GtkWidget *)client_data); + gdk_flush(); + compute_image(); + gtk_widget_destroy((GtkWidget *)client_data); +} + +void cancel_button_clicked(GtkWidget *widget, gpointer client_data) +{ + gtk_widget_destroy((GtkWidget *)client_data); +} + +void dialog_destroy(GtkWidget *widget, gpointer client_data) +{ + gtk_main_quit(); +} + +void effect_channel_callback(GtkWidget *widget,gpointer client_data) +{ + if (GTK_TOGGLE_BUTTON(widget)->active) + { + licvals.effect_channel=(guchar)((glong)client_data); + } +} + +void effect_operator_callback(GtkWidget *widget,gpointer client_data) +{ + if (GTK_TOGGLE_BUTTON(widget)->active) + { + licvals.effect_operator=(guchar)((glong)client_data); + } +} + +void effect_convolve_callback(GtkWidget *widget,gpointer client_data) +{ + if (GTK_TOGGLE_BUTTON(widget)->active) + licvals.effect_convolve=(guchar)((glong)client_data); +} + +gint effect_image_constrain(gint32 image_id, gint32 drawable_id, gpointer data) +{ + if (drawable_id == -1) + return(TRUE); + + return(gimp_drawable_color(drawable_id)); +} + +void effect_image_callback(gint32 id, gpointer data) +{ + licvals.effect_image_id=id; +} + +void effect_parameter_update(GtkAdjustment *adjustment, gpointer client_data) +{ + *((glong *)client_data)=(gdouble)adjustment->value; +} + +void create_main_dialog(void) +{ + GtkWidget *frame; + GtkWidget *vbox,*hbox,*vbox2,*hbox2; + GtkWidget *label; + GtkWidget *option_menu; + GtkWidget *menu; + GtkWidget *button; + GtkWidget *scale; + GtkObject *scale_data; + GSList *group=NULL; + + /* Dialog */ + + dialog = gtk_dialog_new(); + gtk_window_set_title(GTK_WINDOW(dialog), "Van Gogh (LIC)"); + gtk_window_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE); + gtk_container_border_width(GTK_CONTAINER(dialog), 0); + gtk_signal_connect(GTK_OBJECT(dialog), "destroy", + (GtkSignalFunc)dialog_destroy, (gpointer)dialog); + + hbox = gtk_hbox_new(FALSE,5); + gtk_container_border_width(GTK_CONTAINER(hbox),5); + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox),hbox); + + vbox=gtk_vbox_new(FALSE,0); + gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 5); + + frame = gtk_frame_new("Options"); + gtk_frame_set_shadow_type(GTK_FRAME(frame),GTK_SHADOW_ETCHED_IN); + gtk_container_add(GTK_CONTAINER(vbox),frame); + + hbox2=gtk_hbox_new(FALSE,5); + gtk_container_add(GTK_CONTAINER(frame),hbox2); + + button = gtk_check_button_new_with_label("Create new image"); + if (licvals.create_new_image==TRUE) + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button),TRUE); + gtk_container_add(GTK_CONTAINER(hbox2),button); + gtk_widget_show(button); + + gtk_widget_show(hbox2); + gtk_widget_show(frame); + + frame = gtk_frame_new("Effect channel"); + gtk_frame_set_shadow_type(GTK_FRAME(frame),GTK_SHADOW_ETCHED_IN); + gtk_container_add(GTK_CONTAINER(vbox),frame); + + vbox2=gtk_vbox_new(FALSE,0); + gtk_container_add(GTK_CONTAINER(frame),vbox2); + + button = gtk_radio_button_new_with_label(NULL,"Hue"); + group = gtk_radio_button_group(GTK_RADIO_BUTTON(button)); + if (licvals.effect_channel==0) + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE); + + gtk_signal_connect(GTK_OBJECT(button), "toggled", + (GtkSignalFunc)effect_channel_callback, + (gpointer)0); + + gtk_box_pack_start(GTK_BOX(vbox2), button, FALSE, FALSE, 0); + gtk_widget_show(button); + + button = gtk_radio_button_new_with_label(group,"Saturation"); + if (licvals.effect_channel==1) + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE); + + gtk_signal_connect(GTK_OBJECT(button), "toggled", + (GtkSignalFunc)effect_channel_callback, + (gpointer)1); + + gtk_box_pack_start(GTK_BOX(vbox2), button, FALSE, FALSE, 0); + gtk_widget_show(button); + + button = gtk_radio_button_new_with_label(group,"Brightness"); + if (licvals.effect_channel==2) + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE); + + gtk_signal_connect(GTK_OBJECT(button), "toggled", + (GtkSignalFunc)effect_channel_callback, + (gpointer)2); + + gtk_box_pack_start(GTK_BOX(vbox2), button, FALSE, FALSE, 0); + gtk_widget_show(button); + + gtk_widget_show(vbox2); + gtk_widget_show(frame); + + frame = gtk_frame_new("Effect operator"); + gtk_frame_set_shadow_type(GTK_FRAME(frame),GTK_SHADOW_ETCHED_IN); + gtk_container_add(GTK_CONTAINER(vbox),frame); + + vbox2=gtk_vbox_new(FALSE,0); + gtk_container_add(GTK_CONTAINER(frame),vbox2); + + button = gtk_radio_button_new_with_label(NULL,"Derivative"); + group = gtk_radio_button_group(GTK_RADIO_BUTTON(button)); + if (licvals.effect_operator==0) + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE); + + gtk_signal_connect(GTK_OBJECT(button), "toggled", + (GtkSignalFunc)effect_operator_callback, + (gpointer)0); + + gtk_box_pack_start(GTK_BOX(vbox2), button, FALSE, FALSE, 0); + gtk_widget_show(button); + + button = gtk_radio_button_new_with_label(group,"Gradient"); + if (licvals.effect_operator==1) + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE); + + gtk_signal_connect(GTK_OBJECT(button), "toggled", + (GtkSignalFunc)effect_operator_callback, + (gpointer)1); + + gtk_box_pack_start(GTK_BOX(vbox2), button, FALSE, FALSE, 0); + gtk_widget_show(button); + + gtk_widget_show(vbox2); + gtk_widget_show(frame); + + frame = gtk_frame_new("Convolve"); + gtk_frame_set_shadow_type(GTK_FRAME(frame),GTK_SHADOW_ETCHED_IN); + gtk_container_add(GTK_CONTAINER(vbox),frame); + + vbox2=gtk_vbox_new(FALSE,0); + gtk_container_add(GTK_CONTAINER(frame),vbox2); + + button = gtk_radio_button_new_with_label(NULL,"With white noise"); + group = gtk_radio_button_group(GTK_RADIO_BUTTON(button)); + if (licvals.effect_convolve==0) + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE); + + gtk_signal_connect(GTK_OBJECT(button), "toggled", + (GtkSignalFunc)effect_convolve_callback, + (gpointer)0); + + gtk_box_pack_start(GTK_BOX(vbox2), button, FALSE, FALSE, 0); + gtk_widget_show(button); + + button = gtk_radio_button_new_with_label(group,"With source image"); + if (licvals.effect_convolve==1) + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE); + + gtk_signal_connect(GTK_OBJECT(button), "toggled", + (GtkSignalFunc)effect_convolve_callback, + (gpointer)1); + + gtk_box_pack_start(GTK_BOX(vbox2), button, FALSE, FALSE, 0); + gtk_widget_show(button); + + gtk_widget_show(vbox2); + gtk_widget_show(frame); + + gtk_widget_show(vbox); + + vbox=gtk_vbox_new(FALSE,0); + gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 5); + + frame = gtk_frame_new("Parameters"); + gtk_frame_set_shadow_type(GTK_FRAME(frame),GTK_SHADOW_ETCHED_IN); + gtk_container_add(GTK_CONTAINER(vbox),frame); + + /* Effect image menu */ + + vbox2=gtk_vbox_new(FALSE,0); + gtk_container_add(GTK_CONTAINER(frame),vbox2); + + hbox2=gtk_hbox_new(FALSE,0); + gtk_container_add(GTK_CONTAINER(vbox2),hbox2); + + label = gtk_label_new("Effect image:"); + gtk_container_add(GTK_CONTAINER(hbox2),label); + gtk_widget_show(label); + + option_menu = gtk_option_menu_new(); + + menu = gimp_drawable_menu_new(effect_image_constrain, + effect_image_callback, + NULL, + licvals.effect_image_id); + + gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu); + + gtk_container_add(GTK_CONTAINER(hbox2),option_menu); + gtk_widget_show(option_menu); + + gtk_widget_show(hbox2); + + label = gtk_label_new("Filter length:"); + gtk_misc_set_alignment(GTK_MISC(label), 0.5, 1.0); + gtk_container_add(GTK_CONTAINER(vbox2),label); + gtk_widget_show(label); + + scale_data = gtk_adjustment_new(licvals.filtlen, 0, 64, 1.0, 1.0, 0.0); + gtk_signal_connect(GTK_OBJECT(scale_data), "value_changed", + (GtkSignalFunc)effect_parameter_update, + (gpointer)&licvals.filtlen); + scale = gtk_hscale_new(GTK_ADJUSTMENT(scale_data)); + gtk_range_set_update_policy(GTK_RANGE(scale), GTK_UPDATE_CONTINUOUS); + gtk_container_add(GTK_CONTAINER(vbox2),scale); + gtk_widget_show(scale); + + label = gtk_label_new("Noise magnitude:"); + gtk_misc_set_alignment(GTK_MISC(label), 0.5, 1.0); + gtk_container_add(GTK_CONTAINER(vbox2),label); + gtk_widget_show(label); + + scale_data = gtk_adjustment_new(licvals.noisemag, 1, 5, 1.0, 1.0, 0.0); + gtk_signal_connect(GTK_OBJECT(scale_data), "value_changed", + (GtkSignalFunc)effect_parameter_update, + (gpointer)&licvals.noisemag); + + scale = gtk_hscale_new(GTK_ADJUSTMENT(scale_data)); + gtk_range_set_update_policy(GTK_RANGE(scale), GTK_UPDATE_CONTINUOUS); + gtk_container_add(GTK_CONTAINER(vbox2),scale); + gtk_widget_show(scale); + + label = gtk_label_new("Integration steps:"); + gtk_misc_set_alignment(GTK_MISC(label), 0.5, 1.0); + gtk_container_add(GTK_CONTAINER(vbox2),label); + gtk_widget_show(label); + + scale_data = gtk_adjustment_new(licvals.intsteps, 1, 40, 1.0, 1.0, 0.0); + gtk_signal_connect(GTK_OBJECT(scale_data), "value_changed", + (GtkSignalFunc)effect_parameter_update, + (gpointer)&licvals.intsteps); + + scale = gtk_hscale_new(GTK_ADJUSTMENT(scale_data)); + gtk_range_set_update_policy(GTK_RANGE(scale), GTK_UPDATE_CONTINUOUS); + gtk_container_add(GTK_CONTAINER(vbox2),scale); + gtk_widget_show(scale); + + label = gtk_label_new("Minimum value:"); + gtk_misc_set_alignment(GTK_MISC(label), 0.5, 1.0); + gtk_container_add(GTK_CONTAINER(vbox2),label); + gtk_widget_show(label); + + scale_data = gtk_adjustment_new(licvals.minv, -100, 0, 1.0, 1.0, 0.0); + gtk_signal_connect(GTK_OBJECT(scale_data), "value_changed", + (GtkSignalFunc)effect_parameter_update, + (gpointer)&licvals.minv); + + scale = gtk_hscale_new(GTK_ADJUSTMENT(scale_data)); + gtk_range_set_update_policy(GTK_RANGE(scale), GTK_UPDATE_CONTINUOUS); + gtk_container_add(GTK_CONTAINER(vbox2),scale); + gtk_widget_show(scale); + + label = gtk_label_new("Maximum value:"); + gtk_misc_set_alignment(GTK_MISC(label), 0.5, 1.0); + gtk_container_add(GTK_CONTAINER(vbox2),label); + gtk_widget_show(label); + + scale_data = gtk_adjustment_new(licvals.maxv, 0, 100, 1.0, 1.0, 0.0); + gtk_signal_connect(GTK_OBJECT(scale_data), "value_changed", + (GtkSignalFunc)effect_parameter_update, + (gpointer)&licvals.maxv); + + scale = gtk_hscale_new(GTK_ADJUSTMENT(scale_data)); + gtk_range_set_update_policy(GTK_RANGE(scale), GTK_UPDATE_CONTINUOUS); + gtk_container_add(GTK_CONTAINER(vbox2),scale); + gtk_widget_show(scale); + + gtk_widget_show(vbox2); + gtk_widget_show(frame); + + gtk_widget_show(vbox); + gtk_widget_show(hbox); + + /* Buttons */ + + gtk_container_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), 6); + + button = gtk_button_new_with_label("OK"); + GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT); + gtk_signal_connect(GTK_OBJECT(button), "clicked", + (GtkSignalFunc)ok_button_clicked,(gpointer)dialog); + + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button, TRUE, TRUE, 0); + gtk_widget_grab_default(button); + gtk_widget_show(button); + + button = gtk_button_new_with_label("Cancel"); + GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT); + gtk_signal_connect(GTK_OBJECT(button), "clicked", + (GtkSignalFunc)cancel_button_clicked,(gpointer)dialog); + + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button, TRUE, TRUE, 0); + gtk_widget_show(button); + + /* Done */ + + gtk_widget_show(dialog); +} + +/******************/ +/* Implementation */ +/******************/ + +void lic_interactive (GDrawable *drawable); +void lic_noninteractive (GDrawable *drawable); + +/*************************************/ +/* Set parameters to standard values */ +/*************************************/ + +void set_default_settings(void) +{ + licvals.filtlen=5; + licvals.noisemag=2; + licvals.intsteps=25; + licvals.minv=-25; + licvals.maxv=25; + licvals.create_new_image=TRUE; + licvals.effect_channel=2; + licvals.effect_operator=1; + licvals.effect_convolve=1; + licvals.effect_image_id=0; +} + +MAIN() + +static void query(void) +{ + static GParamDef args[] = + { + { PARAM_INT32, "run_mode", "Interactive" }, + { PARAM_IMAGE, "image", "Input image" }, + { PARAM_DRAWABLE, "drawable", "Input drawable" }, + }; + + static GParamDef *return_vals = NULL; + static gint nargs = sizeof (args) / sizeof (args[0]); + static gint nreturn_vals = 0; + + gimp_install_procedure ("plug_in_lic", + "Creates a Van Gogh effect (Line Integral Convolution)", + "No help yet", + "Tom Bech & Federico Mena Quintero", + "Tom Bech & Federico Mena Quintero", + "Version 0.14, September 24 1997", + "/Filters/Artistic/Van Gogh (LIC)", + "RGB", + PROC_PLUG_IN, + nargs, nreturn_vals, + args, return_vals); +} + +static void run(gchar *name, + gint nparams, + GParam *param, + gint *nreturn_vals, + GParam **return_vals) +{ + static GParam values[1]; + GDrawable *drawable; + GRunModeType run_mode; + GStatusType status = STATUS_SUCCESS; + + run_mode = param[0].data.d_int32; + + *nreturn_vals = 1; + *return_vals = values; + + values[0].type = PARAM_STATUS; + values[0].data.d_status = status; + + /* Set default values */ + /* ================== */ + + set_default_settings(); + + /* Possibly retrieve data */ + /* ====================== */ + + gimp_get_data ("plug_in_lic", &licvals); + + /* Get the specified drawable */ + /* ========================== */ + + drawable = gimp_drawable_get (param[2].data.d_drawable); + + if (status == STATUS_SUCCESS) + { + /* Make sure that the drawable is RGBA or RGB color */ + /* ================================================ */ + + if (gimp_drawable_color(drawable->id)) + { + /* Set the tile cache size */ + /* ======================= */ + + gimp_tile_cache_ntiles(TILE_CACHE_SIZE); + + switch (run_mode) + { + case RUN_INTERACTIVE: + lic_interactive(drawable); + gimp_set_data("plug_in_lic", &licvals, sizeof(LicValues)); + break; + case RUN_WITH_LAST_VALS: + image_setup(drawable,FALSE); + compute_image(); + break; + default: + break; + } + } + else + status = STATUS_EXECUTION_ERROR; + } + + values[0].data.d_status = status; + gimp_drawable_detach (drawable); +} + +GPlugInInfo PLUG_IN_INFO = +{ + NULL, /* init_proc */ + NULL, /* quit_proc */ + query, /* query_proc */ + run, /* run_proc */ +}; + +void lic_interactive(GDrawable *drawable) +{ + gchar **argv; + gint argc; + + argc = 1; + argv = g_new (gchar *, 1); + argv[0] = g_strdup ("lic"); + + gtk_init (&argc, &argv); + gtk_rc_parse(gimp_gtkrc()); + + gdk_set_use_xshm(gimp_use_xshm()); + + /* Create application window */ + /* ========================= */ + + create_main_dialog(); + + /* Prepare images */ + /* ============== */ + + image_setup(drawable,TRUE); + + /* Gtk main event loop */ + /* =================== */ + + gtk_main(); + gdk_flush(); +} + +void lic_noninteractive(GDrawable *drawable) +{ + printf("Noninteractive not yet implemented! Sorry.\n"); +} + diff --git a/plug-ins/common/mapcolor.c b/plug-ins/common/mapcolor.c new file mode 100644 index 0000000000..936e2537c9 --- /dev/null +++ b/plug-ins/common/mapcolor.c @@ -0,0 +1,696 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * mapcolor plugin + * Copyright (C) 1998 Peter Kirchgessner + * (email: pkirchg@aol.com, WWW: http://members.aol.com/pkirchg) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +/* Event history: + * V 1.00, PK, 26-Oct-98: Creation. + */ +#define VERSIO 1.00 +static char dversio[] = "v1.00 26-Oct-98"; +static char ident[] = "@(#) GIMP mapcolor plug-in v1.00 26-Oct-98"; + +#include +#include +#include +#include +#include +#include "gtk/gtk.h" +#include "libgimp/gimp.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define PRV_WIDTH 50 +#define PRV_HEIGHT 20 + +typedef struct +{ + guchar colors[4][3]; + gint32 map_mode; +} PLVALS; + +typedef struct +{ + GtkWidget *activate; /* The button that activates the color sel. dialog */ + GtkWidget *colselect; /* The colour selection dialog itself */ + GtkWidget *preview; /* The colour preview */ + unsigned char color[3];/* The selected colour */ +} CSEL; + + +typedef struct +{ + GtkWidget *dialog; + CSEL csel[4]; + PLVALS *plvals; + gint run; /* run */ +} RunInterface; + +static RunInterface runint = +{ + NULL, /* dialog */ + { + { NULL, NULL, NULL, { 0 } }, /* Color selection dialogs */ + { NULL, NULL, NULL, { 0 } }, + { NULL, NULL, NULL, { 0 } }, + { NULL, NULL, NULL, { 0 } } + }, + NULL, /* plug-in values */ + FALSE /* run */ +}; + + +/* Declare some local functions. + */ +static void query (void); +static void run (char *name, + int nparams, + GParam *param, + int *nreturn_vals, + GParam **return_vals); + +GPlugInInfo PLUG_IN_INFO = +{ + NULL, /* init_proc */ + NULL, /* quit_proc */ + query, /* query_proc */ + run, /* run_proc */ +}; + +static gint dialog (PLVALS *plvals); +static void mapcolor_close_callback (GtkWidget *widget, + gpointer data); +static void mapcolor_ok_callback (GtkWidget *widget, + gpointer data); +static void add_color_button (int csel_index, int left, int top, + GtkWidget *table, PLVALS *plvals); +static void color_select_ok_callback (GtkWidget *widget, + gpointer data); +static void color_select_cancel_callback (GtkWidget *widget, + gpointer data); +static void color_preview_show (GtkWidget *widget, + unsigned char *color); + +static void color_mapping (GDrawable *drawable, PLVALS *plvals); + + +/* The run mode */ +static GRunModeType l_run_mode; + + +MAIN () + + +static void +query (void) + +{ + static GParamDef adjust_args[] = + { + { PARAM_INT32, "run_mode", "Interactive, non-interactive" }, + { PARAM_IMAGE, "image", "Input image (not used)" }, + { PARAM_DRAWABLE, "drawable", "Input drawable to adjust" } + }; + static int nadjust_args = sizeof (adjust_args) / sizeof (adjust_args[0]); + + static GParamDef map_args[] = + { + { PARAM_INT32, "run_mode", "Interactive, non-interactive" }, + { PARAM_IMAGE, "image", "Input image (not used)" }, + { PARAM_DRAWABLE, "drawable", "Input drawable where colors are to map" }, + { PARAM_COLOR, "srccolor_1", "First source color" }, + { PARAM_COLOR, "srccolor_2", "Second source color" }, + { PARAM_COLOR, "dstcolor_1", "First destination color" }, + { PARAM_COLOR, "dstcolor_2", "Second destination color" }, + { PARAM_INT32, "map_mode", "Mapping mode (0: linear, others reserved)" } + }; + static int nmap_args = sizeof (map_args) / sizeof (map_args[0]); + + gimp_install_procedure ("plug_in_color_adjust", + "Adjust current foreground/background color in the\ + drawable to black/white", + "The current foreground color is mapped to black, \ +the current background color is mapped to white.", + "Peter Kirchgessner", + "Peter Kirchgessner", + dversio, + "/Filters/Colors/Adjust Fgrd.-Bkgrd.", + "RGB*", + PROC_PLUG_IN, + nadjust_args, 0, + adjust_args, NULL); + + gimp_install_procedure ("plug_in_color_map", + "Map two source colors to two destination colors. \ +Other colors are mapped by interpolation.", + "Map two source colors to two destination colors. \ +Other colors are mapped by interpolation.", + "Peter Kirchgessner", + "Peter Kirchgessner", + dversio, + "/Filters/Colors/Color Mapping", + "RGB*", + PROC_PLUG_IN, + nmap_args, 0, + map_args, NULL); +} + + +static void +run (char *name, + int nparams, + GParam *param, + int *nreturn_vals, + GParam **return_vals) + +{ + static GParam values[1]; + GRunModeType run_mode; + GDrawable *drawable = NULL; + GStatusType status = STATUS_SUCCESS; + guchar *c = (guchar *)ident; + int j; + static PLVALS plvals = + { + { { 0, 0, 0}, { 255, 255, 255 }, { 0, 0, 0 }, { 255, 255, 255 } }, + 0 + }; + + l_run_mode = run_mode = param[0].data.d_int32; + + *nreturn_vals = 1; + *return_vals = values; + + values[0].type = PARAM_STATUS; + values[0].data.d_status = status; + + while (status == STATUS_SUCCESS) + { + if (nparams < 3) + { + status = STATUS_CALLING_ERROR; + break; + } + + /* Make sure the drawable is RGB color */ + drawable = gimp_drawable_get (param[2].data.d_drawable); + if (!gimp_drawable_color (drawable->id)) + { + gimp_message ("color_adjust/color_map: cannot operate on grey/indexed\ + images"); + status = STATUS_EXECUTION_ERROR; + break; + } + + if (strcmp (name, "plug_in_color_adjust") == 0) + { + if (nparams != 3) /* Make sure all the arguments are there */ + { + status = STATUS_CALLING_ERROR; + break; + } + + c = &(plvals.colors[0][0]); /* First source color */ + gimp_palette_get_foreground (c, c+1, c+2); + c = &(plvals.colors[1][0]); /* Second source color */ + gimp_palette_get_background (c, c+1, c+2); + c = &(plvals.colors[2][0]); /* First destination color */ + c[0] = c[1] = c[2] = 0; /* Foreground mapped to black */ + c = &(plvals.colors[3][0]); /* second destination color */ + c[0] = c[1] = c[2] = 255; /* Background mapped to white */ + plvals.map_mode = 0; + + if (run_mode != RUN_NONINTERACTIVE) + gimp_progress_init ("Adjust Foreground/Background"); + + color_mapping (drawable, &plvals); + break; + } + + if (strcmp (name, "plug_in_color_map") == 0) + { + if (run_mode == RUN_NONINTERACTIVE) + { + if (nparams != 8) /* Make sure all the arguments are there */ + { + status = STATUS_CALLING_ERROR; + break; + } + + for (j = 0; j < 4; j++) + { + plvals.colors[j][0] = param[3+j].data.d_color.red; + plvals.colors[j][1] = param[3+j].data.d_color.green; + plvals.colors[j][2] = param[3+j].data.d_color.blue; + } + plvals.map_mode = param[7].data.d_int32; + } + else if (run_mode == RUN_INTERACTIVE) + { + gimp_get_data (name, &plvals); + + c = &(plvals.colors[0][0]); /* First source color */ + gimp_palette_get_foreground (c, c+1, c+2); + c = &(plvals.colors[1][0]); /* Second source color */ + gimp_palette_get_background (c, c+1, c+2); + + if (!dialog (&plvals)) break; + } + else if (run_mode == RUN_WITH_LAST_VALS) + { + gimp_get_data (name, &plvals); + } + else + { + status = STATUS_CALLING_ERROR; + break; + } + + if (run_mode != RUN_NONINTERACTIVE) + gimp_progress_init ("Mapping colors"); + + color_mapping (drawable, &plvals); + + if (run_mode == RUN_INTERACTIVE) + gimp_set_data (name, &plvals, sizeof (plvals)); + + break; + } + + status = STATUS_EXECUTION_ERROR; + } + + if ((status == STATUS_SUCCESS) && (run_mode != RUN_NONINTERACTIVE)) + gimp_displays_flush (); + + if (drawable != NULL) gimp_drawable_detach (drawable); + values[0].data.d_status = status; +} + + +static gint +dialog (PLVALS *plvals) + +{ + GtkWidget *button; + GtkWidget *dlg; + GtkWidget *hbox; + GtkWidget *table; + guchar *color_cube; + gchar **argv; + gint argc; + + argc = 1; + argv = g_new (gchar *, 1); + argv[0] = g_strdup ("Map colors"); + + gtk_init (&argc, &argv); + gtk_rc_parse (gimp_gtkrc ()); + + gdk_set_use_xshm(gimp_use_xshm()); + + gtk_preview_set_gamma(gimp_gamma()); + gtk_preview_set_install_cmap(gimp_install_cmap()); + color_cube = gimp_color_cube(); + gtk_preview_set_color_cube(color_cube[0], color_cube[1], color_cube[2], + color_cube[3]); + gtk_widget_set_default_visual(gtk_preview_get_visual()); + gtk_widget_set_default_colormap(gtk_preview_get_cmap()); + + runint.dialog = dlg = gtk_dialog_new (); + gtk_window_set_title (GTK_WINDOW (dlg), "Map colors"); + gtk_window_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE); + gtk_signal_connect (GTK_OBJECT (dlg), "destroy", + (GtkSignalFunc) mapcolor_close_callback, + NULL); + + /* Action area */ + button = gtk_button_new_with_label ("OK"); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) mapcolor_ok_callback, dlg); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, + TRUE, TRUE, 0); + gtk_widget_grab_default (button); + gtk_widget_show (button); + + button = gtk_button_new_with_label ("Cancel"); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_signal_connect_object (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) gtk_widget_destroy, + GTK_OBJECT (dlg)); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, + TRUE, TRUE, 0); + gtk_widget_show (button); + + /* parameter settings */ + runint.plvals = plvals; + + hbox = gtk_hbox_new (FALSE, 0); + gtk_container_border_width (GTK_CONTAINER (hbox), 0); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), hbox, TRUE, TRUE, 0); + gtk_widget_show (hbox); + + /* The table keeps the color selections */ + table = gtk_table_new (4, 2, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (table), 5); + gtk_table_set_col_spacings (GTK_TABLE (table), 5); + gtk_box_pack_start (GTK_BOX (hbox), table, TRUE, TRUE, 0); + gtk_widget_show (table); + + add_color_button (0, 0, 1, table, plvals); + add_color_button (1, 0, 2, table, plvals); + add_color_button (2, 2, 1, table, plvals); + add_color_button (3, 2, 2, table, plvals); + + gtk_widget_show (dlg); + + gtk_main (); + gdk_flush (); + + return runint.run; +} + + +static void +color_preview_show (GtkWidget *widget, + unsigned char *rgb) + +{guchar *buf, *bp; + int j, width, height; + + width = PRV_WIDTH; + height = PRV_HEIGHT; + + bp = buf = g_malloc (width*3); + if (buf == NULL) return; + + for (j = 0; j < width; j++) + { + *(bp++) = rgb[0]; + *(bp++) = rgb[1]; + *(bp++) = rgb[2]; + } + for (j = 0; j < height; j++) + gtk_preview_draw_row (GTK_PREVIEW (widget), buf, 0, j, width); + + gtk_widget_draw (widget, NULL); + + g_free (buf); +} + + +static void +color_select_ok_callback (GtkWidget *widget, + gpointer data) + +{gdouble color[3]; + int idx, j; + GtkWidget *dialog; + + idx = (int)data; + if ((dialog = runint.csel[idx].colselect) == NULL) return; + + gtk_color_selection_get_color ( + GTK_COLOR_SELECTION (GTK_COLOR_SELECTION_DIALOG (dialog)->colorsel), + color); + + for (j = 0; j < 3; j++) + runint.csel[idx].color[j] = (unsigned char)(color[j]*255.0); + + color_preview_show (runint.csel[idx].preview, &(runint.csel[idx].color[0])); + + runint.csel[idx].colselect = NULL; + gtk_widget_destroy (dialog); +} + + +static void +color_select_cancel_callback (GtkWidget *widget, + gpointer data) + +{int idx; + GtkWidget *dialog; + + idx = (int)data; + if ((dialog = runint.csel[idx].colselect) == NULL) return; + + runint.csel[idx].colselect = NULL; + gtk_widget_destroy (dialog); +} + + +static void +mapcolor_color_button_callback (GtkWidget *widget, + gpointer data) + +{int idx, j; + GtkColorSelectionDialog *csd; + GtkWidget *dialog; + gdouble colour[3]; + static char *label[4] = { "First source color", "Second source color", + "First destination color", "Second destination color" }; + + idx = (int)data; + + /* Is the colour selection dialog already running ? */ + if (runint.csel[idx].colselect != NULL) return; + + for (j = 0; j < 3; j++) + colour[j] = runint.csel[idx].color[j] / 255.0; + + dialog = runint.csel[idx].colselect = gtk_color_selection_dialog_new ( + label[idx]); + + csd = GTK_COLOR_SELECTION_DIALOG (dialog); + + gtk_widget_destroy (csd->help_button); + + gtk_signal_connect (GTK_OBJECT (dialog), "destroy", + (GtkSignalFunc) color_select_cancel_callback, data); + gtk_signal_connect (GTK_OBJECT (csd->ok_button), "clicked", + (GtkSignalFunc) color_select_ok_callback, data); + gtk_signal_connect (GTK_OBJECT (csd->cancel_button), "clicked", + (GtkSignalFunc) color_select_cancel_callback, data); + + gtk_color_selection_set_color (GTK_COLOR_SELECTION (csd->colorsel), colour); + + gtk_window_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE); + gtk_widget_show (dialog); +} + + +static void +add_color_button (int csel_index, + int left, + int top, + GtkWidget *table, + PLVALS *plvals) + +{ + GtkWidget *label; + GtkWidget *button; + GtkWidget *preview; + GtkWidget *hbox; + + hbox = gtk_hbox_new (FALSE, 0); + gtk_container_border_width (GTK_CONTAINER (hbox), 5); + gtk_table_attach (GTK_TABLE (table), hbox, left, left+1, top, top+1, + GTK_FILL, GTK_FILL, 0, 0); + + label = gtk_label_new ((left == 0) ? "From:" : "To:"); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0); + gtk_widget_show (label); + gtk_widget_show (hbox); + + hbox = gtk_hbox_new (FALSE, 0); + gtk_container_border_width (GTK_CONTAINER (hbox), 5); + gtk_table_attach (GTK_TABLE (table), hbox, left+1, left+2, top, top+1, + GTK_FILL, GTK_FILL, 0, 0); + + button = runint.csel[csel_index].activate = gtk_button_new (); + + memcpy (&(runint.csel[csel_index].color[0]), + &(plvals->colors[csel_index][0]), 3); + preview = runint.csel[csel_index].preview = gtk_preview_new(GTK_PREVIEW_COLOR); + gtk_preview_size (GTK_PREVIEW (preview), PRV_WIDTH, PRV_HEIGHT); + gtk_container_add (GTK_CONTAINER (button), preview); + gtk_widget_show (preview); + + color_preview_show (preview, &(runint.csel[csel_index].color[0])); + + gtk_signal_connect (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) mapcolor_color_button_callback, + (gpointer)csel_index); + gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); + gtk_widget_show (button); + gtk_widget_show (hbox); +} + + +static void +mapcolor_close_callback (GtkWidget *widget, + gpointer data) + +{ + gtk_main_quit (); +} + + +static void +mapcolor_ok_callback (GtkWidget *widget, + gpointer data) + +{int j; + GtkWidget *dialog; + PLVALS *plvals = runint.plvals; + + for (j = 0; j < 4; j++) + memcpy (&(plvals->colors[j][0]), &(runint.csel[j].color[0]), 3); + + /* Destroy color selection dialogs if still running */ + for (j = 0; j < 4; j++) + { + dialog = runint.csel[j].colselect; + if (dialog != NULL) + { + runint.csel[j].colselect = NULL; + gtk_widget_destroy (GTK_WIDGET (dialog)); + } + } + plvals->map_mode = 0; /* Currently always linear mapping */ + + runint.run = TRUE; + gtk_widget_destroy (GTK_WIDGET (data)); +} + + +static void +get_mapping (guchar *src_col1, + guchar *src_col2, + guchar *dst_col1, + guchar *dst_col2, + gint32 map_mode, + guchar *redmap, + guchar *greenmap, + guchar *bluemap) + +{int rgb, i, j, a, as, b, bs; + guchar *colormap[3]; + + /* Currently we always do a linear mapping */ + + colormap[0] = redmap; + colormap[1] = greenmap; + colormap[2] = bluemap; + + switch (map_mode) + { + case 0: + default: + for (rgb = 0; rgb < 3; rgb++) + { + a = src_col1[rgb]; as = dst_col1[rgb]; + b = src_col2[rgb]; bs = dst_col2[rgb]; + for (i = 0; i < 256; i++) + { + j = ((i - a) * (bs - as)) / (b - a) + as; + if (j > 255) j = 255; else if (j < 0) j = 0; + colormap[rgb][i] = j; + } + } + break; + } +} + + +static void +color_mapping (GDrawable *drawable, + PLVALS *plvals) + +{ + int processed, total; + gint x, y, xmin, xmax, ymin, ymax; + unsigned char *src, *dest; + GPixelRgn src_rgn, dest_rgn; + gpointer pr; + double progress; + unsigned char redmap[256], greenmap[256], bluemap[256]; + unsigned char *src_col1 = plvals->colors[0]; + unsigned char *src_col2 = plvals->colors[1]; + unsigned char *dst_col1 = plvals->colors[2]; + unsigned char *dst_col2 = plvals->colors[3]; + + if ( (src_col1[0] == src_col2[0]) + || (src_col1[1] == src_col2[1]) + || (src_col1[2] == src_col2[2])) return; + + gimp_drawable_mask_bounds (drawable->id, &xmin, &ymin, &xmax, &ymax); + if ((ymin == ymax) || (xmin == xmax)) return; + total = (xmax - xmin) * (ymax - ymin); + + gimp_tile_cache_ntiles (2 * (drawable->width / gimp_tile_width () + 1)); + gimp_pixel_rgn_init (&src_rgn, drawable, xmin, ymin, + (xmax - xmin), (ymax - ymin), FALSE, FALSE); + gimp_pixel_rgn_init (&dest_rgn, drawable, xmin, ymin, + (xmax - xmin), (ymax - ymin), TRUE, TRUE); + + pr = gimp_pixel_rgns_register (2, &src_rgn, &dest_rgn); + + get_mapping (src_col1, src_col2, dst_col1, dst_col2, plvals->map_mode, + redmap, greenmap, bluemap); + + processed = 0; + progress = 0.0; + for (; pr != NULL; pr = gimp_pixel_rgns_process (pr)) + { + for (y = 0; y < src_rgn.h; y++) + { + src = src_rgn.data + y * src_rgn.rowstride; + dest = dest_rgn.data + y * dest_rgn.rowstride; + for (x = 0; x < src_rgn.w; x++) + { + dest[0] = redmap[src[0]]; + dest[1] = greenmap[src[1]]; + dest[2] = bluemap[src[2]]; + src += drawable->bpp; + dest += drawable->bpp; + processed++; + } + } + if (l_run_mode != RUN_NONINTERACTIVE) + { + if ((double)processed/(double)total - progress > 0.1) + { + progress = (double)processed/(double)total; + gimp_progress_update (progress); + } + } + } + if (l_run_mode != RUN_NONINTERACTIVE) + gimp_progress_update (1.0); + + gimp_drawable_flush (drawable); + gimp_drawable_merge_shadow (drawable->id, TRUE); + gimp_drawable_update (drawable->id, xmin, ymin, (xmax - xmin), (ymax - ymin)); +} diff --git a/plug-ins/common/xbm.c b/plug-ins/common/xbm.c new file mode 100644 index 0000000000..877a33814c --- /dev/null +++ b/plug-ins/common/xbm.c @@ -0,0 +1,1092 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * X10 and X11 bitmap (XBM) loading and saving file filter for the GIMP. + * XBM code Copyright (C) 1998 Gordon Matzigkeit + * + * The XBM reading and writing code was written from scratch by Gordon + * Matzigkeit based on the XReadBitmapFile(3X11) manual + * page distributed with X11R6 and by staring at valid XBM files. It + * does not contain any code written for other XBM file loaders. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Release 1.0, 1998-02-04, Gordon Matzigkeit : + * - Load and save X10 and X11 bitmaps. + * - Allow the user to specify the C identifier prefix. + * + * TODO: + * - Parsing is very tolerant, and the algorithms are quite hairy, so + * load_image should be carefully tested to make sure there are no XBM's + * that fail. + * - Allow the user to specify a hotspot, and preserve it when loading. + */ + +/* Set this for debugging. */ +/* #define VERBOSE 2 */ + +#include +#include +#include +#include +#include +#include "libgimp/gimp.h" + +/* Wear your GIMP with pride! */ +#define DEFAULT_USE_COMMENT TRUE +#define DEFAULT_COMMENT "Made with GIMP" +#define MAX_COMMENT 72 + +/* C identifier prefix. */ +#define DEFAULT_PREFIX "bitmap" +#define MAX_PREFIX 24 + +/* Whether or not to save as X10 bitmap. */ +#define DEFAULT_X10_FORMAT FALSE + +typedef struct _XBMSaveVals +{ + gchar comment[MAX_COMMENT + 1]; + gint x10_format; + gint x_hot; + gint y_hot; + gchar prefix[MAX_PREFIX + 1]; +} XBMSaveVals; + +static XBMSaveVals xsvals = +{ + DEFAULT_COMMENT, /* comment */ + DEFAULT_X10_FORMAT, /* x10_format */ + -1, /* x_hot */ + -1, /* y_hot */ + DEFAULT_PREFIX, /* prefix */ +}; + + +typedef struct _XBMSaveInterface +{ + gint run; +} XBMSaveInterface; + +static XBMSaveInterface xsint = +{ + FALSE /* run */ +}; + + +/* Declare some local functions. + */ +static void query (void); +static void run (char *name, + int nparams, + GParam *param, + int *nreturn_vals, + GParam **return_vals); +static gint32 load_image (char *filename); +static gint save_image (char *filename, + gint32 image_ID, + gint32 drawable_ID); +static gint save_dialog (gint32 drawable_ID); +static void close_callback (GtkWidget *widget, + gpointer data); +static void save_ok_callback (GtkWidget *widget, + gpointer data); +static void save_toggle_update (GtkWidget *widget, + gpointer data); +static void comment_entry_callback (GtkWidget *widget, + gpointer data); +static void prefix_entry_callback(GtkWidget *widget, + gpointer data); + + +GPlugInInfo PLUG_IN_INFO = +{ + NULL, /* init_proc */ + NULL, /* quit_proc */ + query, /* query_proc */ + run, /* run_proc */ +}; + +MAIN (); + +#ifdef VERBOSE +static int verbose = VERBOSE; +#endif + + +static void +query () +{ + static GParamDef load_args[] = + { + { PARAM_INT32, "run_mode", "Interactive, non-interactive" }, + { PARAM_STRING, "filename", "The name of the file to load" }, + { PARAM_STRING, "raw_filename", "The name entered" }, + }; + static GParamDef load_return_vals[] = + { + { PARAM_IMAGE, "image", "Output image" }, + }; + static int nload_args = sizeof (load_args) / sizeof (load_args[0]); + static int nload_return_vals = sizeof (load_return_vals) / sizeof (load_return_vals[0]); + + + static GParamDef save_args[] = + { + { PARAM_INT32, "run_mode", "Interactive, non-interactive" }, + { PARAM_IMAGE, "image", "Input image" }, + { PARAM_DRAWABLE, "drawable", "Drawable to save" }, + { PARAM_STRING, "filename", "The name of the file to save" }, + { PARAM_STRING, "raw_filename", "The name entered" }, + { PARAM_STRING, "comment", "Image description (maximum 72 bytes)" }, + { PARAM_INT32, "x10", "Save in X10 format" }, + { PARAM_INT32, "x_hot", "X coordinate of hotspot" }, + { PARAM_INT32, "y_hot", "Y coordinate of hotspot" }, + { PARAM_STRING, "prefix", "Identifier prefix [determined from filename]"}, + } ; + static int nsave_args = sizeof (save_args) / sizeof (save_args[0]); + + gimp_install_procedure ("file_xbm_load", + "Load a file in X10 or X11 bitmap (XBM) file format", + "Load a file in X10 or X11 bitmap (XBM) file format. XBM is a lossless format for flat black-and-white (two color indexed) images.", + "Gordon Matzigkeit", + "Gordon Matzigkeit", + "1998", + "/XBM", + NULL, + PROC_PLUG_IN, + nload_args, nload_return_vals, + load_args, load_return_vals); + + + gimp_install_procedure ("file_xbm_save", + "Save a file in X10 or X11 bitmap (XBM) file format", + "Save a file in X10 or X11 bitmap (XBM) file format. XBM is a lossless format for flat black-and-white (two color indexed) images.", + "Gordon Matzigkeit", + "Gordon Matzigkeit", + "1998", + "/XBM", + "INDEXED", + PROC_PLUG_IN, + nsave_args, 0, + save_args, NULL); + + gimp_register_load_handler ("file_xbm_load", "xbm,icon,bitmap", ""); + gimp_register_save_handler ("file_xbm_save", "xbm,icon,bitmap", ""); +} + + +static void +init_prefix (char *filename) +{ + char *p, *prefix; + int len; + + /* Mangle the filename to get the prefix. */ + prefix = strrchr (filename, '/'); + if (prefix) + prefix ++; + else + prefix = filename; + + /* Strip any extension. */ + p = strrchr (prefix, '.'); + if (p && p != prefix) + len = MIN (MAX_PREFIX, p - prefix); + else + len = MAX_PREFIX; + + memset (xsvals.prefix, 0, sizeof (xsvals.prefix)); + strncpy (xsvals.prefix, prefix, len); +} + + +static void +run (char *name, + int nparams, + GParam *param, + int *nreturn_vals, + GParam **return_vals) +{ + static GParam values[2]; + GStatusType status = STATUS_SUCCESS; + GRunModeType run_mode; + gint32 image_ID; + + run_mode = param[0].data.d_int32; + + *nreturn_vals = 1; + *return_vals = values; + values[0].type = PARAM_STATUS; + values[0].data.d_status = STATUS_CALLING_ERROR; + +#ifdef VERBOSE + if (verbose) + printf ("XBM: RUN %s\n", name); +#endif + + if (strcmp (name, "file_xbm_load") == 0) + { + image_ID = load_image (param[1].data.d_string); + + if (image_ID != -1) + { + *nreturn_vals = 2; + values[0].data.d_status = STATUS_SUCCESS; + values[1].type = PARAM_IMAGE; + values[1].data.d_image = image_ID; + } + else + { + values[0].data.d_status = STATUS_EXECUTION_ERROR; + } + } + else if (strcmp (name, "file_xbm_save") == 0) + { + int argc; + char **argv; + + argc = 1; + argv = g_new (gchar *, 1); + argv[0] = g_strdup ("xbm"); + + switch (run_mode) + { + case RUN_INTERACTIVE: + /* Possibly retrieve data */ + gimp_get_data ("file_xbm_save", &xsvals); + + /* Always override the prefix with the filename. */ + init_prefix (param[3].data.d_string); + + /* First acquire information with a dialog */ + if (! save_dialog (param[2].data.d_int32)) + return; + + break; + + case RUN_NONINTERACTIVE: + /* Make sure all the required arguments are there! */ + if (nparams < 5) + status = STATUS_CALLING_ERROR; + if (status == STATUS_SUCCESS) + { + int i = 5; + + if (nparams > i) + { + memset (xsvals.comment, 0, sizeof (xsvals.comment)); + strncpy (xsvals.comment, param[i].data.d_string, + MAX_COMMENT); + } + + i ++; + if (nparams > i) + xsvals.x10_format = (param[i].data.d_int32) ? TRUE : FALSE; + + i += 2; + if (nparams > i) + { + /* They've asked for a hotspot. */ + xsvals.x_hot = param[i - 1].data.d_int32; + xsvals.y_hot = param[i].data.d_int32; + } + + i ++; + if (nparams > i) + { + memset (xsvals.prefix, 0, sizeof (xsvals.prefix)); + strncpy (xsvals.prefix, param[i].data.d_string, + MAX_PREFIX); + } + else + init_prefix (param[3].data.d_string); + + i ++; + /* Too many arguments. */ + if (nparams > i) + status = STATUS_CALLING_ERROR; + } + break; + + case RUN_WITH_LAST_VALS: + /* Possibly retrieve data */ + gimp_get_data ("file_xbm_save", &xsvals); + break; + + default: + break; + } + + *nreturn_vals = 1; + if (save_image (param[3].data.d_string, param[1].data.d_int32, + param[2].data.d_int32)) + { + /* Store xsvals data */ + gimp_set_data ("file_xbm_save", &xsvals, sizeof (xsvals)); + values[0].data.d_status = STATUS_SUCCESS; + } + else + values[0].data.d_status = STATUS_EXECUTION_ERROR; + } +} + + + +/* Return the value of a digit. */ +static gint +getval (int c, int base) +{ + static guchar *digits = "0123456789abcdefABCDEF"; + int val; + + /* Include uppercase hex digits. */ + if (base == 16) + base = 22; + + /* Find a match. */ + for (val = 0; val < base; val ++) + if (c == digits[val]) + return (val < 16) ? val : (val - 6); + return -1; +} + + +/* Same as fgetc, but skip C-style comments and insert whitespace. */ +static gint +cpp_fgetc (FILE *fp) +{ + int comment, c; + + /* FIXME: insert whitespace as advertised. */ + comment = 0; + do + { + c = fgetc (fp); + if (comment) + { + if (c == '*') + /* In a comment, with potential to leave. */ + comment = 1; + else if (comment == 1 && c == '/') + /* Leaving a comment. */ + comment = 0; + else + /* In a comment, with no potential to leave. */ + comment = 2; + } + else + { + /* Not in a comment. */ + if (c == '/') + { + /* Potential to enter a comment. */ + c = fgetc (fp); + if (c == '*') + /* Entered a comment, with no potential to leave. */ + comment = 2; + else + { + /* Just a slash in the open. */ + ungetc (c, fp); + c = '/'; + } + } + } + } + while (comment && c != EOF); + return c; +} + + +/* Match a string with a file. */ +static gint +match (FILE *fp, char *s) +{ + int c; + + do + { + c = fgetc (fp); + if (c == *s) + s ++; + else + break; + } + while (c != EOF && *s); + + if (!*s) + return TRUE; + + if (c != EOF) + ungetc (c, fp); + return FALSE; +} + + +/* Read the next integer from the file, skipping all non-integers. */ +static gint +get_int (FILE *fp) +{ + int digval, base, val, c; + + do + c = cpp_fgetc (fp); + while (c != EOF && !isdigit (c)); + + if (c == EOF) + return 0; + + /* Check for the base. */ + if (c == '0') + { + c = fgetc (fp); + if (c == 'x' || c == 'X') + { + c = fgetc (fp); + base = 16; + } + else if (isdigit (c)) + base = 8; + else + { + ungetc (c, fp); + return 0; + } + } + else + base = 10; + + val = 0; + for (;;) + { + digval = getval (c, base); + if (digval == -1) + { + ungetc (c, fp); + break; + } + val *= base; + val += digval; + c = fgetc (fp); + } + + return val; +} + + +static gint +load_image (char *filename) +{ + FILE *fp; + gint32 image_ID, layer_ID; + + GPixelRgn pixel_rgn; + GDrawable *drawable; + guchar *data; + int width, height, intbits; + int c, i, j, k; + int tileheight, rowoffset; + + char *name_buf; + + guchar cmap[] = + { + 0x00, 0x00, 0x00, /* black */ + 0xff, 0xff, 0xff /* white */ + }; + + fp = fopen (filename, "rb"); + if (!fp) + { + printf ("XBM: cannot open \"%s\"\n", filename); + return -1; + } + + name_buf = g_malloc (strlen (filename) + 11); + sprintf (name_buf, "Loading %s:", filename); + gimp_progress_init (name_buf); + g_free (name_buf); + + /* Loosely parse the header */ + intbits = height = width = 0; + c = ' '; + do + { + + if (isspace (c)) + { + if (match (fp, "char")) + { + c = fgetc (fp); + if (isspace (c)) + { + intbits = 8; + continue; + } + } + else if (match (fp, "short")) + { + c = fgetc (fp); + if (isspace (c)) + { + intbits = 16; + continue; + } + } + } + + if (c == '_') + { + if (match (fp, "width")) + { + c = fgetc (fp); + if (isspace (c)) + { + width = get_int (fp); + continue; + } + } + else if (match (fp, "height")) + { + c = fgetc (fp); + if (isspace (c)) + { + height = get_int (fp); + continue; + } + } + } + + c = cpp_fgetc (fp); + } + while (c != '{' && c != EOF); + + if (c == EOF) + { + printf ("XBM: cannot read header (ftell == %ld)\n", ftell (fp)); + return -1; + } + + if (width == 0) + { + printf ("XBM: no image width specified\n"); + return -1; + } + + if (height == 0) + { + printf ("XBM: no image height specified\n"); + return -1; + } + + if (intbits == 0) + { + printf ("XBM: no image data type specified\n"); + return -1; + } + + image_ID = gimp_image_new (width, height, INDEXED); + gimp_image_set_filename (image_ID, filename); + + /* Set a black-and-white colormap. */ + gimp_image_set_cmap (image_ID, cmap, 2); + + layer_ID = gimp_layer_new (image_ID, + "Background", + width, height, + INDEXED_IMAGE, + 100, + NORMAL_MODE); + gimp_image_add_layer (image_ID, layer_ID, 0); + + drawable = gimp_drawable_get (layer_ID); + + /* Prepare the pixel region. */ + gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, width, height, TRUE, FALSE); + + /* Allocate the data. */ + tileheight = gimp_tile_height (); + data = (guchar *) g_malloc (width * tileheight); + + for (i = 0; i < height; i += tileheight) + { + tileheight = MIN (tileheight, height - i); + +#ifdef VERBOSE + if (verbose > 1) + printf ("XBM: reading %dx(%d+%d) pixel region\n", width, i, + tileheight); +#endif + + /* Parse the data from the file */ + for (j = 0; j < tileheight; j ++) + { + /* Read each row. */ + rowoffset = j * width; + for (k = 0; k < width; k ++) + { + /* Expand each integer into INTBITS pixels. */ + if (k % intbits == 0) + { + c = get_int (fp); + + /* Flip all the bits so that 1's become black and + 0's become white. */ + c ^= 0xffff; + } + + data[rowoffset + k] = c & 1; + c >>= 1; + } + } + + /* Put the data into the image. */ + gimp_progress_update ((double) (i + tileheight) / (double) height); + gimp_pixel_rgn_set_rect (&pixel_rgn, data, 0, i, width, tileheight); + } + + g_free (data); + + gimp_drawable_flush (drawable); + gimp_drawable_detach (drawable); + + fclose (fp); + return image_ID; +} + + +static void +close_callback (GtkWidget *widget, + gpointer data) +{ + gtk_main_quit (); +} + +static int gtk_initialized = FALSE; + + +static void +not_bw_dialog (void) +{ + GtkWidget *dlg, *button, *label, *frame, *vbox; + + if (!gtk_initialized) + { + printf ("XBM: can only save two color indexed images\n"); + return; + } + + dlg = gtk_dialog_new (); + gtk_window_set_title (GTK_WINDOW (dlg), "XBM Warning"); + gtk_window_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE); + gtk_signal_connect (GTK_OBJECT (dlg), "destroy", + (GtkSignalFunc) close_callback, + dlg); + gtk_signal_connect (GTK_OBJECT (dlg), "delete_event", + (GtkSignalFunc) close_callback, + dlg); + + /* Action area */ + button = gtk_button_new_with_label ("Cancel"); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_signal_connect_object (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) gtk_widget_destroy, + GTK_OBJECT (dlg)); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, + TRUE, TRUE, 0); + gtk_widget_show (button); + + /* the warning message */ + frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); + gtk_container_border_width (GTK_CONTAINER (frame), 10); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, + TRUE, 0); + vbox = gtk_vbox_new (FALSE, 5); + gtk_container_border_width (GTK_CONTAINER (vbox), 5); + gtk_container_add (GTK_CONTAINER (frame), vbox); + + label = gtk_label_new ( + "The image which you are trying to save as\n" + "an XBM contains more than two colors.\n\n" + "Please convert it to a black and white\n" + "(1-bit) indexed image and try again." + ); + gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0); + gtk_widget_show (label); + gtk_widget_show (vbox); + gtk_widget_show (frame); + gtk_widget_show (dlg); + gtk_main (); + gtk_widget_destroy (GTK_WIDGET (dlg)); + gdk_flush (); +} + + +static gint +save_image (char *filename, + gint32 image_ID, + gint32 drawable_ID) +{ + GDrawable *drawable; + GPixelRgn pixel_rgn; + FILE *fp; + + int width, height, colors, dark; + int intbits, lineints, need_comma, nints, rowoffset, tileheight; + int c, i, j, k, thisbit; + + guchar *data, *cmap, *name_buf; + char *prefix, *p, *intfmt; + + drawable = gimp_drawable_get (drawable_ID); + width = drawable->width; + height = drawable->height; + cmap = gimp_image_get_cmap (image_ID, &colors); + +#if 0 + printf ("XBM: run `gdb xbm' and `attach %d'\n", getpid ()); + kill (getpid (), 19); +#endif + + if (gimp_drawable_type (drawable_ID) != INDEXED_IMAGE || colors > 2) + { + /* The image is not black-and-white. */ + not_bw_dialog (); + return FALSE; + } + + name_buf = (guchar *) g_malloc (strlen (filename) + 11); + sprintf (name_buf, "Saving %s:", filename); + gimp_progress_init (name_buf); + g_free (name_buf); + + /* Figure out which color is black, and which is white. */ + dark = 0; + if (colors > 1) + { + int first, second; + + /* Maybe the second color is darker than the first. */ + first = (cmap[0] * cmap[0]) + (cmap[1] * cmap[1]) + (cmap[2] * cmap[2]); + second = (cmap[3] * cmap[3]) + (cmap[4] * cmap[4]) + (cmap[5] * cmap[5]); + + if (second < first) + dark = 1; + } + + /* Now actually save the data. */ + fp = fopen (filename, "w"); + if (!fp) + { + printf ("XBM: cannot create \"%s\"\n", filename); + return FALSE; + } + + /* Maybe write the image comment. */ + if (*xsvals.comment) + fprintf (fp, "/* %s */\n", xsvals.comment); + + /* Change any non-alphanumeric prefix characters to underscores. */ + prefix = xsvals.prefix; + p = prefix; + while (*p) + { + if (!isalnum (*p)) + *p = '_'; + p ++; + } + + /* Write out the image height and width. */ + fprintf (fp, "#define %s_width %d\n", prefix, width); + fprintf (fp, "#define %s_height %d\n", prefix, height); + + /* Write out the hotspot, if any. */ + if (xsvals.x_hot >= 0 && xsvals.y_hot >= 0) + { + fprintf (fp, "#define %s_x_hot %d\n", prefix, xsvals.x_hot); + fprintf (fp, "#define %s_y_hot %d\n", prefix, xsvals.y_hot); + } + + /* Now write the actual data. */ + if (xsvals.x10_format) + { + /* We can fit 9 hex shorts on a single line. */ + lineints = 9; + intbits = 16; + intfmt = " 0x%04x"; + } + else + { + /* We can fit 12 hex chars on a single line. */ + lineints = 12; + intbits = 8; + intfmt = " 0x%02x"; + } + + fprintf (fp, "static %s %s_bits[] = {\n ", + xsvals.x10_format ? "short" : "char", prefix); + + /* Allocate a new set of pixels. */ + tileheight = gimp_tile_height (); + data = (guchar *) g_malloc(width * tileheight); + + gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, width, height, + FALSE, FALSE); + + /* Write out the integers. */ + need_comma = 0; + nints = 0; + for (i = 0; i < height; i += tileheight) + { + /* Get a horizontal slice of the image. */ + tileheight = MIN (tileheight, height - i); + gimp_pixel_rgn_get_rect (&pixel_rgn, data, 0, i, width, tileheight); + +#ifdef VERBOSE + if (verbose > 1) + printf ("TGA: writing %dx(%d+%d) pixel region\n", + width, i, tileheight); +#endif + + for (j = 0; j < tileheight; j ++) + { + /* Write out a row at a time. */ + rowoffset = j * width; + c = 0; + thisbit = 0; + + for (k = 0; k < width; k ++) + { + if (k != 0 && thisbit == intbits) + { + /* Output a completed integer. */ + if (need_comma) + fputc (',', fp); + need_comma = 1; + + /* Maybe start a new line. */ + if (nints ++ >= lineints) + { + nints = 1; + fputs ("\n ", fp); + } + fprintf (fp, intfmt, c); + + /* Start a new integer. */ + c = 0; + thisbit = 0; + } + + /* Pack INTBITS pixels into an integer. */ + c |= ((data[rowoffset + k] == dark) ? 1 : 0) << (thisbit ++); + } + + if (thisbit != 0) + { + /* Write out the last oddball int. */ + if (need_comma) + fputc (',', fp); + need_comma = 1; + + /* Maybe start a new line. */ + if (nints ++ == lineints) + { + nints = 1; + fputs ("\n ", fp); + } + fprintf (fp, intfmt, c); + } + } + + gimp_progress_update ((double) (i + tileheight) / (double) height); + } + + /* Write the trailer. */ + fprintf (fp, " };\n"); + fclose (fp); + return TRUE; +} + + +static gint +save_dialog (gint32 drawable_ID) +{ + GtkWidget *dlg, *button, *toggle, *label, *entry, *frame, *hbox, *vbox; + gchar **argv; + gint argc; + + xsint.run = FALSE; + + argc = 1; + argv = g_new (gchar *, 1); + argv[0] = g_strdup ("save"); + + gtk_init (&argc, &argv); + gtk_initialized = TRUE; + + gtk_rc_parse (gimp_gtkrc ()); + gdk_set_use_xshm(gimp_use_xshm()); + + dlg = gtk_dialog_new (); + gtk_window_set_title (GTK_WINDOW (dlg), "Save as XBM"); + gtk_window_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE); + gtk_signal_connect (GTK_OBJECT (dlg), "destroy", + (GtkSignalFunc) close_callback, + NULL); + gtk_signal_connect (GTK_OBJECT (dlg), "delete_event", + (GtkSignalFunc) close_callback, + dlg); + + /* Action area */ + button = gtk_button_new_with_label ("OK"); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) save_ok_callback, + dlg); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, + TRUE, TRUE, 0); + gtk_widget_grab_default (button); + gtk_widget_show (button); + + button = gtk_button_new_with_label ("Cancel"); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_signal_connect_object (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) gtk_widget_destroy, + GTK_OBJECT (dlg)); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, + TRUE, TRUE, 0); + gtk_widget_show (button); + + + /* parameter settings */ + frame = gtk_frame_new ("XBM Options"); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); + gtk_container_border_width (GTK_CONTAINER (frame), 10); + gtk_widget_show (frame); + + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0); + vbox = gtk_vbox_new (FALSE, 5); + gtk_container_border_width (GTK_CONTAINER (vbox), 5); + gtk_container_add (GTK_CONTAINER (frame), vbox); + + /* comment string. */ + hbox = gtk_hbox_new(FALSE, 5); + gtk_container_border_width (GTK_CONTAINER (hbox), 0); + gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0); + + label = gtk_label_new ("Description: "); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + entry = gtk_entry_new_with_max_length (MAX_COMMENT); + gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0); + gtk_widget_set_usize (entry, 240, 0); + gtk_entry_set_text (GTK_ENTRY (entry), xsvals.comment); + gtk_signal_connect (GTK_OBJECT (entry), "changed", + (GtkSignalFunc) comment_entry_callback, + NULL); + gtk_widget_show (entry); + + gtk_widget_show (hbox); + + /* X10 format */ + toggle = gtk_check_button_new_with_label ("X10 format bitmap"); + gtk_box_pack_start (GTK_BOX (vbox), toggle, TRUE, TRUE, 0); + gtk_signal_connect (GTK_OBJECT (toggle), "toggled", + (GtkSignalFunc) save_toggle_update, + &xsvals.x10_format); + gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (toggle), xsvals.x10_format); + gtk_widget_show (toggle); + + /* prefix */ + hbox = gtk_hbox_new(FALSE, 5); + gtk_container_border_width (GTK_CONTAINER (hbox), 0); + gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0); + + label = gtk_label_new ("Identifier prefix: "); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + entry = gtk_entry_new_with_max_length (MAX_PREFIX); + gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0); + gtk_entry_set_text (GTK_ENTRY (entry), xsvals.prefix); + gtk_signal_connect (GTK_OBJECT (entry), "changed", + (GtkSignalFunc) prefix_entry_callback, + NULL); + gtk_widget_show (entry); + + gtk_widget_show (hbox); + + /* Done. */ + gtk_widget_show (vbox); + gtk_widget_show (dlg); + + gtk_main (); + gdk_flush (); + + return xsint.run; +} + + +/* Update the comment string. */ +static void +comment_entry_callback (GtkWidget *widget, + gpointer data) +{ + memset (xsvals.comment, 0, sizeof (xsvals.comment)); + strncpy (xsvals.comment, + gtk_entry_get_text (GTK_ENTRY (widget)), MAX_COMMENT); +} + + +static void +prefix_entry_callback(GtkWidget *widget, + gpointer data) +{ + memset (xsvals.prefix, 0, sizeof (xsvals.prefix)); + strncpy (xsvals.prefix, gtk_entry_get_text (GTK_ENTRY (widget)), MAX_PREFIX); +} + + +static void +save_ok_callback (GtkWidget *widget, + gpointer data) +{ + xsint.run = TRUE; + gtk_widget_destroy (GTK_WIDGET (data)); +} + + +static void +save_toggle_update (GtkWidget *widget, + gpointer data) +{ + int *toggle_val; + + toggle_val = (int *) data; + + if (GTK_TOGGLE_BUTTON (widget)->active) + *toggle_val = TRUE; + else + *toggle_val = FALSE; +} + +/* +Local Variables: +compile-command:"gcc -Wall -Wmissing-prototypes -g -O -o xbm xbm.c -lgimp -lgtk -lgdk -lglib -lm" +End: +*/ diff --git a/plug-ins/dbbrowser/dbbrowser_utils.c b/plug-ins/dbbrowser/dbbrowser_utils.c index 461050f800..a35056331a 100644 --- a/plug-ins/dbbrowser/dbbrowser_utils.c +++ b/plug-ins/dbbrowser/dbbrowser_utils.c @@ -82,8 +82,9 @@ gimp_db_browser(void (* apply_callback) ( gchar *selected_proc_name, /* list : list in a scrolled_win */ dbbrowser->clist = gtk_clist_new(1); - gtk_clist_set_policy (GTK_CLIST (dbbrowser->clist), GTK_POLICY_AUTOMATIC, - GTK_POLICY_AUTOMATIC); + dbbrowser->scrolled_win = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (dbbrowser->scrolled_win), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_clist_set_selection_mode (GTK_CLIST (dbbrowser->clist), GTK_SELECTION_BROWSE); @@ -91,9 +92,10 @@ gimp_db_browser(void (* apply_callback) ( gchar *selected_proc_name, gtk_signal_connect (GTK_OBJECT (dbbrowser->clist), "select_row", (GtkSignalFunc) procedure_select_callback, dbbrowser); - gtk_box_pack_start (GTK_BOX (vbox), - dbbrowser->clist, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (vbox), dbbrowser->scrolled_win, TRUE, TRUE, 0); + gtk_container_add (GTK_CONTAINER (dbbrowser->scrolled_win), dbbrowser->clist); gtk_widget_show(dbbrowser->clist); + gtk_widget_show(dbbrowser->scrolled_win); /* search entry */ diff --git a/plug-ins/dbbrowser/dbbrowser_utils.h b/plug-ins/dbbrowser/dbbrowser_utils.h index b1ff3c1aea..b31dc2bb8c 100644 --- a/plug-ins/dbbrowser/dbbrowser_utils.h +++ b/plug-ins/dbbrowser/dbbrowser_utils.h @@ -52,6 +52,7 @@ typedef struct { GtkWidget* descr_table; GtkWidget* clist; + GtkWidget* scrolled_win; /* the currently selected procedure */ gchar *selected_proc_name; diff --git a/plug-ins/dbbrowser/gimpprocbrowser.c b/plug-ins/dbbrowser/gimpprocbrowser.c index 461050f800..a35056331a 100644 --- a/plug-ins/dbbrowser/gimpprocbrowser.c +++ b/plug-ins/dbbrowser/gimpprocbrowser.c @@ -82,8 +82,9 @@ gimp_db_browser(void (* apply_callback) ( gchar *selected_proc_name, /* list : list in a scrolled_win */ dbbrowser->clist = gtk_clist_new(1); - gtk_clist_set_policy (GTK_CLIST (dbbrowser->clist), GTK_POLICY_AUTOMATIC, - GTK_POLICY_AUTOMATIC); + dbbrowser->scrolled_win = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (dbbrowser->scrolled_win), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_clist_set_selection_mode (GTK_CLIST (dbbrowser->clist), GTK_SELECTION_BROWSE); @@ -91,9 +92,10 @@ gimp_db_browser(void (* apply_callback) ( gchar *selected_proc_name, gtk_signal_connect (GTK_OBJECT (dbbrowser->clist), "select_row", (GtkSignalFunc) procedure_select_callback, dbbrowser); - gtk_box_pack_start (GTK_BOX (vbox), - dbbrowser->clist, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (vbox), dbbrowser->scrolled_win, TRUE, TRUE, 0); + gtk_container_add (GTK_CONTAINER (dbbrowser->scrolled_win), dbbrowser->clist); gtk_widget_show(dbbrowser->clist); + gtk_widget_show(dbbrowser->scrolled_win); /* search entry */ diff --git a/plug-ins/dbbrowser/gimpprocbrowser.h b/plug-ins/dbbrowser/gimpprocbrowser.h index b1ff3c1aea..b31dc2bb8c 100644 --- a/plug-ins/dbbrowser/gimpprocbrowser.h +++ b/plug-ins/dbbrowser/gimpprocbrowser.h @@ -52,6 +52,7 @@ typedef struct { GtkWidget* descr_table; GtkWidget* clist; + GtkWidget* scrolled_win; /* the currently selected procedure */ gchar *selected_proc_name; diff --git a/plug-ins/dbbrowser/gimpprocview.c b/plug-ins/dbbrowser/gimpprocview.c index 461050f800..a35056331a 100644 --- a/plug-ins/dbbrowser/gimpprocview.c +++ b/plug-ins/dbbrowser/gimpprocview.c @@ -82,8 +82,9 @@ gimp_db_browser(void (* apply_callback) ( gchar *selected_proc_name, /* list : list in a scrolled_win */ dbbrowser->clist = gtk_clist_new(1); - gtk_clist_set_policy (GTK_CLIST (dbbrowser->clist), GTK_POLICY_AUTOMATIC, - GTK_POLICY_AUTOMATIC); + dbbrowser->scrolled_win = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (dbbrowser->scrolled_win), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_clist_set_selection_mode (GTK_CLIST (dbbrowser->clist), GTK_SELECTION_BROWSE); @@ -91,9 +92,10 @@ gimp_db_browser(void (* apply_callback) ( gchar *selected_proc_name, gtk_signal_connect (GTK_OBJECT (dbbrowser->clist), "select_row", (GtkSignalFunc) procedure_select_callback, dbbrowser); - gtk_box_pack_start (GTK_BOX (vbox), - dbbrowser->clist, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (vbox), dbbrowser->scrolled_win, TRUE, TRUE, 0); + gtk_container_add (GTK_CONTAINER (dbbrowser->scrolled_win), dbbrowser->clist); gtk_widget_show(dbbrowser->clist); + gtk_widget_show(dbbrowser->scrolled_win); /* search entry */ diff --git a/plug-ins/lic/.cvsignore b/plug-ins/lic/.cvsignore new file mode 100644 index 0000000000..b132a759f3 --- /dev/null +++ b/plug-ins/lic/.cvsignore @@ -0,0 +1,6 @@ +Makefile.in +Makefile +.deps +_libs +.libs +lic diff --git a/plug-ins/lic/Makefile.am b/plug-ins/lic/Makefile.am new file mode 100644 index 0000000000..abd90bedad --- /dev/null +++ b/plug-ins/lic/Makefile.am @@ -0,0 +1,38 @@ +## Process this file with automake to produce Makefile.in + +pluginlibdir = $(gimpplugindir)/plug-ins + +pluginlib_PROGRAMS = lic + +lic_SOURCES = \ + lic.c + +INCLUDES = \ + -I$(top_srcdir) \ + $(GTK_CFLAGS) \ + -I$(includedir) + +LDADD = \ + $(top_builddir)/libgimp/libgimpui.la \ + $(top_builddir)/libgimp/libgimp.la \ + $(GTK_LIBS) \ + @LIBJPEG_LIB@ + +DEPS = \ + $(top_builddir)/libgimp/libgimpui.la \ + $(top_builddir)/libgimp/libgimp.la + +lic_DEPENDENCIES = $(DEPS) + +.PHONY: files + +files: + @files=`ls $(DISTFILES) 2> /dev/null`; for p in $$files; do \ + echo $$p; \ + done + @for subdir in $(SUBDIRS); do \ + files=`cd $$subdir; $(MAKE) files | grep -v "make\[[1-9]\]"`; \ + for file in $$files; do \ + echo $$subdir/$$file; \ + done; \ + done diff --git a/plug-ins/lic/lic.c b/plug-ins/lic/lic.c new file mode 100644 index 0000000000..299618c6b6 --- /dev/null +++ b/plug-ins/lic/lic.c @@ -0,0 +1,1433 @@ +/*********************************************************************************/ +/* LIC 0.14 -- image filter plug-in for The Gimp program */ +/* Copyright (C) 1996 Tom Bech */ +/*===============================================================================*/ +/* E-mail: tomb@gimp.org */ +/* You can contact the original The Gimp authors at gimp@xcf.berkeley.edu */ +/*===============================================================================*/ +/* This program is free software; you can redistribute it and/or modify it under */ +/* the terms of the GNU General Public License as published by the Free Software */ +/* Foundation; either version 2 of the License, or (at your option) any later */ +/* version. */ +/*===============================================================================*/ +/* This program is distributed in the hope that it will be useful, but WITHOUT */ +/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS */ +/* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.*/ +/*===============================================================================*/ +/* You should have received a copy of the GNU General Public License along with */ +/* this program; if not, write to the Free Software Foundation, Inc., 675 Mass */ +/* Ave, Cambridge, MA 02139, USA. */ +/*===============================================================================*/ +/* In other words, you can't sue me for whatever happens while using this ;) */ +/*********************************************************************************/ +/* Changes (post 0.10): */ +/* -> 0.11: Fixed a bug in the convolution kernels (Tom). */ +/* -> 0.12: Added Quartic's bilinear interpolation stuff (Tom). */ +/* -> 0.13 Changed some UI stuff causing trouble with the 0.60 release, added */ +/* the (GIMP) tags and changed random() calls to rand() (Tom) */ +/* -> 0.14 Ported to 0.99.11 (Tom) */ +/*********************************************************************************/ +/* This plug-in implements the Line Integral Convolution (LIC) as described in */ +/* Cabral et al. "Imaging vector fields using line integral convolution" in the */ +/* Proceedings of ACM SIGGRAPH 93. Publ. by ACM, New York, NY, USA. p. 263-270. */ +/* Some of the code is based on code by Steinar Haugen (thanks!), the Perlin */ +/* noise function is practically ripped as is :) */ +/*********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +/************/ +/* Typedefs */ +/************/ + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#define CHECKBOUNDS(x,y) (x>=0 && y>=0 && xr=a->r+b->r; + a->g=a->g+b->g; + a->b=a->b+b->b; +} + +void rgb_mul(rgbpixel *a,gdouble b) +{ + a->r=a->r*b; + a->g=a->g*b; + a->b=a->b*b; +} + +void rgb_clamp(rgbpixel *a) +{ + if (a->r>1.0) + a->r=1.0; + + if (a->g>1.0) + a->g=1.0; + + if (a->b>1.0) + a->b=1.0; + + if (a->r<0.0) + a->r=0.0; + + if (a->g<0.0) + a->g=0.0; + + if (a->b<0.0) + a->b=0.0; +} + +void set_color(rgbpixel *a,gdouble r,gdouble g,gdouble b) +{ + a->r=r; a->g=g; a->b=b; +} + +glong xy_to_index(gint x,gint y) +{ + return((glong)in_channels*((glong)x+(glong)y*(glong)width)); +} + +gboolean checkbounds(gint x,gint y) +{ + if (x<0 || y<0 || x>width-1 || y>height-1) + return(FALSE); + + return(TRUE); +} + +rgbpixel peek(gint x,gint y) +{ + static guchar data[4]; + rgbpixel color; + + gimp_pixel_rgn_get_pixel(&source_region,data,x,y); + + color.r=(gdouble)(data[0])/255.0; + color.g=(gdouble)(data[1])/255.0; + color.b=(gdouble)(data[2])/255.0; + + if (input_drawable->bpp==4) + { + if (in_channels==4) + color.a=(gdouble)(data[3])/255.0; + else + color.a=1.0; + } + else + color.a=1.0; + + return(color); +} + +void poke(gint x,gint y,rgbpixel *color) +{ + static guchar data[4]; + + data[0]=(guchar)(color->r*255.0); + data[1]=(guchar)(color->g*255.0); + data[2]=(guchar)(color->b*255.0); + data[3]=(guchar)(color->a*255.0); + + gimp_pixel_rgn_set_pixel(&dest_region,data,x,y); +} + +void pos_to_int(gdouble x,gdouble y,gint *scr_x,gint *scr_y) +{ + *scr_x=(gint)((x*(gdouble)width)); + *scr_y=(gint)((y*(gdouble)height)); +} + +/****************************************/ +/* Allocate memory for temporary images */ +/****************************************/ + +gint image_setup(GDrawable *drawable,gint interactive) +{ + /* Get some useful info on the input drawable */ + /* ========================================== */ + + input_drawable=drawable; + output_drawable=drawable; + + gimp_drawable_mask_bounds (drawable->id, &border_x1, &border_y1, &border_x2, &border_y2); + + width=input_drawable->width; + height=input_drawable->height; + + gimp_pixel_rgn_init (&source_region, input_drawable, 0, 0, width, height, FALSE, FALSE); + + maxcounter=(glong)width*(glong)height; + + /* Assume at least RGB */ + /* =================== */ + + in_channels=3; + if (gimp_drawable_has_alpha(input_drawable->id)==TRUE) + in_channels++; + + if (interactive==TRUE) + { + /* Allocate memory for temp. images */ + /* ================================ */ + } + + return(TRUE); +} + +guchar peekmap(guchar *image,gint x,gint y) +{ + glong index; + + index=(glong)x+(glong)width*(glong)y; + + return(image[index]); +} + +/*************/ +/* Main part */ +/*************/ + +/***************************************************/ +/* Compute the derivative in the x and y direction */ +/* We use these convolution kernels: */ +/* |1 0 -1| | 1 2 1| */ +/* DX: |2 0 -2| DY: | 0 0 0| */ +/* |1 0 -1| | -1 -2 -1| */ +/* (It's a varation of the Sobel kernels, really) */ +/***************************************************/ + +gint gradx(guchar *image,gint x,gint y) +{ + gint val=0; + + if (CHECKBOUNDS(x-1,y-1)==TRUE) + val=val+(gint)peekmap(image,x-1,y-1); + if (CHECKBOUNDS(x+1,y-1)==TRUE) + val=val-(gint)peekmap(image,x+1,y-1); + + if (CHECKBOUNDS(x-1,y)==TRUE) + val=val+2*(gint)peekmap(image,x-1,y); + if (CHECKBOUNDS(x+1,y)==TRUE) + val=val-2*(gint)peekmap(image,x+1,y); + + if (CHECKBOUNDS(x-1,y+1)==TRUE) + val=val+(gint)peekmap(image,x-1,y+1); + if (CHECKBOUNDS(x+1,y+1)==TRUE) + val=val-(gint)peekmap(image,x+1,y+1); + + return(val); +} + +gint grady(guchar *image,gint x,gint y) +{ + gint val=0; + + if (CHECKBOUNDS(x-1,y-1)==TRUE) + val=val+(gint)peekmap(image,x-1,y-1); + if (CHECKBOUNDS(x,y-1)==TRUE) + val=val+2*(gint)peekmap(image,x,y-1); + if (CHECKBOUNDS(x+1,y-1)==TRUE) + val=val+(gint)peekmap(image,x+1,y-1); + + if (CHECKBOUNDS(x-1,y+1)==TRUE) + val=val-(gint)peekmap(image,x-1,y+1); + if (CHECKBOUNDS(x,y+1)==TRUE) + val=val-2*(gint)peekmap(image,x,y+1); + if (CHECKBOUNDS(x+1,y+1)==TRUE) + val=val-(gint)peekmap(image,x+1,y+1); + + return(val); +} + +/************************************/ +/* A nice 2nd order cubic spline :) */ +/************************************/ + +gdouble cubic(gdouble t) +{ + gdouble at=fabs(t); + + if (at<1.0) + return(2.0*at*at*at-3.0*at*at+1.0); + + return(0.0); +} + +gdouble omega(gdouble u,gdouble v,gint i,gint j) +{ + while (i<0) + i+=numx; + + while (j<0) + j+=numy; + + i%=numx; + j%=numy; + + return(cubic(u)*cubic(v)*(G[i][j][0]*u+G[i][j][1]*v)); +} + +/*************************************************************/ +/* The noise function (2D variant of Perlins noise function) */ +/*************************************************************/ + +gdouble noise(gdouble x,gdouble y) +{ + gint i,sti=(gint)floor(x/dx); + gint j,stj=(gint)floor(y/dy); + + gdouble sum=0.0; + + /* Calculate the gdouble sum */ + /* ======================== */ + + for (i=sti; i<=sti+1; i++) + { + for (j=stj; j<=stj+1; j++) + sum+=omega((x-(gdouble)i*dx)/dx,(y-(gdouble)j*dy)/dy,i,j); + } + + return(sum); +} + +/*************************************************/ +/* Generates pseudo-random vectors with length 1 */ +/*************************************************/ + +void generatevectors(void) +{ + gdouble alpha; + gint i,j; + + for (i=0; i1.0) + i=1.0; + + i=(i/2.0)+0.5; + + return(i); +} + +static rgbpixel bilinear(gdouble x, gdouble y, rgbpixel *p) +{ + gdouble m0, m1; + gdouble ix, iy; + rgbpixel v; + + x = fmod(x, 1.0); + y = fmod(y, 1.0); + + if (x < 0) + x += 1.0; + + if (y < 0) + y += 1.0; + + ix = 1.0 - x; + iy = 1.0 - y; + + /* Red */ + /* === */ + + m0 = ix * p[0].r + x * p[1].r; + m1 = ix * p[2].r + x * p[3].r; + + v.r = iy * m0 + y * m1; + + /* Green */ + /* ===== */ + + m0 = ix * p[0].g + x * p[1].g; + m1 = ix * p[2].g + x * p[3].g; + + v.g = iy * m0 + y * m1; + + /* Blue */ + /* ==== */ + + m0 = ix * p[0].b + x * p[1].b; + m1 = ix * p[2].b + x * p[3].b; + + v.b = iy * m0 + y * m1; + + return(v); +} /* bilinear */ + +void getpixel(rgbpixel *p,gdouble u,gdouble v) +{ + register gint x1, y1, x2, y2; + static rgbpixel pp[4]; + + x1 = (gint)u; + y1 = (gint)v; + + if (x1 < 0) + x1 = width - (-x1 % width); + else + x1 = x1 % width; + + if (y1 < 0) + y1 = height - (-y1 % height); + else + y1 = y1 % height; + + x2 = (x1 + 1) % width; + y2 = (y1 + 1) % height; + + pp[0] = peek(x1, y1); + pp[1] = peek(x2, y1); + pp[2] = peek(x1, y2); + pp[3] = peek(x2, y2); + + *p=bilinear(u,v,pp); +} + +void lic_image(gint x,gint y,gdouble vx,gdouble vy,rgbpixel *color) +{ + gdouble u,step=2.0*l/isteps; + gdouble xx=(gdouble)x,yy=(gdouble)y; + gdouble c,s; + rgbpixel col,col1,col2,col3; + + /* Get vector at x,y */ + /* ================= */ + + c = vx; + s = vy; + + /* Calculate integral numerically */ + /* ============================== */ + + col=black; + getpixel(&col1,xx+l*c,yy+l*s); + rgb_mul(&col1,filter(-l)); + + for (u=-l+step; u<=l; u+=step) + { + getpixel(&col2,xx-u*c,yy-u*s); + rgb_mul(&col2,filter(u)); + + col3=col1; + rgb_add(&col3,&col2); + rgb_mul(&col3,0.5*step); + rgb_add(&col,&col3); + + col1=col2; + } + + rgb_mul(&col,1.0/l); + rgb_clamp(&col); + + *color=col; +} + +gdouble maximum(gdouble a,gdouble b,gdouble c) +{ + gdouble max=a; + + if (b>max) + max=b; + if (c>max) + max=c; + + return(max); +} + +gdouble minimum(gdouble a,gdouble b,gdouble c) +{ + gdouble min=a; + + if (br,col->g,col->b); + min=minimum(col->r,col->g,col->b); + + if (max==min) *hue=-1.0; + else + { + delta=max-min; + if (col->r==max) + *hue=(col->g-col->b)/delta; + else if (col->g==max) + *hue=2.0+(col->b-col->r)/delta; + else if (col->b==max) + *hue=4.0+(col->r-col->g)/delta; + + *hue=*hue*60.0; + if (*hue<0.0) + *hue=*hue+360.0; + } +} + +void get_saturation(rgbpixel *col,gdouble *sat) +{ + gdouble max,min,l; + + max=maximum(col->r,col->g,col->b); + min=minimum(col->r,col->g,col->b); + + if (max==min) + *sat=0.0; + else + { + l=(max+min)/2.0; + if (l<=0.5) + *sat=(max-min)/(max+min); + else + *sat=(max-min)/(2.0-max-min); + } +} + +void get_brightness(rgbpixel *col,gdouble *bri) +{ + gdouble max,min; + + max=maximum(col->r,col->g,col->b); + min=minimum(col->r,col->g,col->b); + + *bri=(max+min)/2.0; +} + +void rgb_to_hue(GDrawable *image,guchar **map) +{ + guchar *themap,data[4]; + gint w,h,x,y; + rgbpixel color; + gdouble val; + glong maxc,index=0; + GPixelRgn region; + + w=image->width; + h=image->height; + maxc=(glong)w*(glong)h; + +/* gimp_drawable_mask_bounds (drawable->id, &border_x1, &border_y1, &border_x2, &border_y2); */ + + gimp_pixel_rgn_init (®ion, image, 0, 0, w, h, FALSE, FALSE); + + themap=(guchar *)malloc((size_t)maxc*sizeof(guchar)); + + for (y=0;ywidth; + h=image->height; + maxc=(glong)w*(glong)h; + +/* gimp_drawable_mask_bounds (drawable->id, &border_x1, &border_y1, &border_x2, &border_y2); */ + + gimp_pixel_rgn_init (®ion, image, 0, 0, w, h, FALSE, FALSE); + + themap=(guchar *)malloc((size_t)maxc*sizeof(guchar)); + + for (y=0;ywidth; + h=image->height; + maxc=(glong)w*(glong)h; + +/* gimp_drawable_mask_bounds (drawable->id, &border_x1, &border_y1, &border_x2, &border_y2); */ + + gimp_pixel_rgn_init (®ion, image, 0, 0, w, h, FALSE, FALSE); + + themap=(guchar *)malloc((size_t)maxc*sizeof(guchar)); + + for (y=0;yid, TRUE); + gimp_drawable_update(output_drawable->id, 0,0, width,height); + + if (new_image_id!=-1) + { + gimp_display_new(new_image_id); + gimp_displays_flush(); + gimp_drawable_detach(output_drawable); + } +} + +/**************************/ +/* Below is only UI stuff */ +/**************************/ + +void ok_button_clicked(GtkWidget *widget, gpointer client_data) +{ + gtk_widget_hide((GtkWidget *)client_data); + gdk_flush(); + compute_image(); + gtk_widget_destroy((GtkWidget *)client_data); +} + +void cancel_button_clicked(GtkWidget *widget, gpointer client_data) +{ + gtk_widget_destroy((GtkWidget *)client_data); +} + +void dialog_destroy(GtkWidget *widget, gpointer client_data) +{ + gtk_main_quit(); +} + +void effect_channel_callback(GtkWidget *widget,gpointer client_data) +{ + if (GTK_TOGGLE_BUTTON(widget)->active) + { + licvals.effect_channel=(guchar)((glong)client_data); + } +} + +void effect_operator_callback(GtkWidget *widget,gpointer client_data) +{ + if (GTK_TOGGLE_BUTTON(widget)->active) + { + licvals.effect_operator=(guchar)((glong)client_data); + } +} + +void effect_convolve_callback(GtkWidget *widget,gpointer client_data) +{ + if (GTK_TOGGLE_BUTTON(widget)->active) + licvals.effect_convolve=(guchar)((glong)client_data); +} + +gint effect_image_constrain(gint32 image_id, gint32 drawable_id, gpointer data) +{ + if (drawable_id == -1) + return(TRUE); + + return(gimp_drawable_color(drawable_id)); +} + +void effect_image_callback(gint32 id, gpointer data) +{ + licvals.effect_image_id=id; +} + +void effect_parameter_update(GtkAdjustment *adjustment, gpointer client_data) +{ + *((glong *)client_data)=(gdouble)adjustment->value; +} + +void create_main_dialog(void) +{ + GtkWidget *frame; + GtkWidget *vbox,*hbox,*vbox2,*hbox2; + GtkWidget *label; + GtkWidget *option_menu; + GtkWidget *menu; + GtkWidget *button; + GtkWidget *scale; + GtkObject *scale_data; + GSList *group=NULL; + + /* Dialog */ + + dialog = gtk_dialog_new(); + gtk_window_set_title(GTK_WINDOW(dialog), "Van Gogh (LIC)"); + gtk_window_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE); + gtk_container_border_width(GTK_CONTAINER(dialog), 0); + gtk_signal_connect(GTK_OBJECT(dialog), "destroy", + (GtkSignalFunc)dialog_destroy, (gpointer)dialog); + + hbox = gtk_hbox_new(FALSE,5); + gtk_container_border_width(GTK_CONTAINER(hbox),5); + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox),hbox); + + vbox=gtk_vbox_new(FALSE,0); + gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 5); + + frame = gtk_frame_new("Options"); + gtk_frame_set_shadow_type(GTK_FRAME(frame),GTK_SHADOW_ETCHED_IN); + gtk_container_add(GTK_CONTAINER(vbox),frame); + + hbox2=gtk_hbox_new(FALSE,5); + gtk_container_add(GTK_CONTAINER(frame),hbox2); + + button = gtk_check_button_new_with_label("Create new image"); + if (licvals.create_new_image==TRUE) + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button),TRUE); + gtk_container_add(GTK_CONTAINER(hbox2),button); + gtk_widget_show(button); + + gtk_widget_show(hbox2); + gtk_widget_show(frame); + + frame = gtk_frame_new("Effect channel"); + gtk_frame_set_shadow_type(GTK_FRAME(frame),GTK_SHADOW_ETCHED_IN); + gtk_container_add(GTK_CONTAINER(vbox),frame); + + vbox2=gtk_vbox_new(FALSE,0); + gtk_container_add(GTK_CONTAINER(frame),vbox2); + + button = gtk_radio_button_new_with_label(NULL,"Hue"); + group = gtk_radio_button_group(GTK_RADIO_BUTTON(button)); + if (licvals.effect_channel==0) + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE); + + gtk_signal_connect(GTK_OBJECT(button), "toggled", + (GtkSignalFunc)effect_channel_callback, + (gpointer)0); + + gtk_box_pack_start(GTK_BOX(vbox2), button, FALSE, FALSE, 0); + gtk_widget_show(button); + + button = gtk_radio_button_new_with_label(group,"Saturation"); + if (licvals.effect_channel==1) + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE); + + gtk_signal_connect(GTK_OBJECT(button), "toggled", + (GtkSignalFunc)effect_channel_callback, + (gpointer)1); + + gtk_box_pack_start(GTK_BOX(vbox2), button, FALSE, FALSE, 0); + gtk_widget_show(button); + + button = gtk_radio_button_new_with_label(group,"Brightness"); + if (licvals.effect_channel==2) + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE); + + gtk_signal_connect(GTK_OBJECT(button), "toggled", + (GtkSignalFunc)effect_channel_callback, + (gpointer)2); + + gtk_box_pack_start(GTK_BOX(vbox2), button, FALSE, FALSE, 0); + gtk_widget_show(button); + + gtk_widget_show(vbox2); + gtk_widget_show(frame); + + frame = gtk_frame_new("Effect operator"); + gtk_frame_set_shadow_type(GTK_FRAME(frame),GTK_SHADOW_ETCHED_IN); + gtk_container_add(GTK_CONTAINER(vbox),frame); + + vbox2=gtk_vbox_new(FALSE,0); + gtk_container_add(GTK_CONTAINER(frame),vbox2); + + button = gtk_radio_button_new_with_label(NULL,"Derivative"); + group = gtk_radio_button_group(GTK_RADIO_BUTTON(button)); + if (licvals.effect_operator==0) + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE); + + gtk_signal_connect(GTK_OBJECT(button), "toggled", + (GtkSignalFunc)effect_operator_callback, + (gpointer)0); + + gtk_box_pack_start(GTK_BOX(vbox2), button, FALSE, FALSE, 0); + gtk_widget_show(button); + + button = gtk_radio_button_new_with_label(group,"Gradient"); + if (licvals.effect_operator==1) + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE); + + gtk_signal_connect(GTK_OBJECT(button), "toggled", + (GtkSignalFunc)effect_operator_callback, + (gpointer)1); + + gtk_box_pack_start(GTK_BOX(vbox2), button, FALSE, FALSE, 0); + gtk_widget_show(button); + + gtk_widget_show(vbox2); + gtk_widget_show(frame); + + frame = gtk_frame_new("Convolve"); + gtk_frame_set_shadow_type(GTK_FRAME(frame),GTK_SHADOW_ETCHED_IN); + gtk_container_add(GTK_CONTAINER(vbox),frame); + + vbox2=gtk_vbox_new(FALSE,0); + gtk_container_add(GTK_CONTAINER(frame),vbox2); + + button = gtk_radio_button_new_with_label(NULL,"With white noise"); + group = gtk_radio_button_group(GTK_RADIO_BUTTON(button)); + if (licvals.effect_convolve==0) + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE); + + gtk_signal_connect(GTK_OBJECT(button), "toggled", + (GtkSignalFunc)effect_convolve_callback, + (gpointer)0); + + gtk_box_pack_start(GTK_BOX(vbox2), button, FALSE, FALSE, 0); + gtk_widget_show(button); + + button = gtk_radio_button_new_with_label(group,"With source image"); + if (licvals.effect_convolve==1) + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE); + + gtk_signal_connect(GTK_OBJECT(button), "toggled", + (GtkSignalFunc)effect_convolve_callback, + (gpointer)1); + + gtk_box_pack_start(GTK_BOX(vbox2), button, FALSE, FALSE, 0); + gtk_widget_show(button); + + gtk_widget_show(vbox2); + gtk_widget_show(frame); + + gtk_widget_show(vbox); + + vbox=gtk_vbox_new(FALSE,0); + gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 5); + + frame = gtk_frame_new("Parameters"); + gtk_frame_set_shadow_type(GTK_FRAME(frame),GTK_SHADOW_ETCHED_IN); + gtk_container_add(GTK_CONTAINER(vbox),frame); + + /* Effect image menu */ + + vbox2=gtk_vbox_new(FALSE,0); + gtk_container_add(GTK_CONTAINER(frame),vbox2); + + hbox2=gtk_hbox_new(FALSE,0); + gtk_container_add(GTK_CONTAINER(vbox2),hbox2); + + label = gtk_label_new("Effect image:"); + gtk_container_add(GTK_CONTAINER(hbox2),label); + gtk_widget_show(label); + + option_menu = gtk_option_menu_new(); + + menu = gimp_drawable_menu_new(effect_image_constrain, + effect_image_callback, + NULL, + licvals.effect_image_id); + + gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu); + + gtk_container_add(GTK_CONTAINER(hbox2),option_menu); + gtk_widget_show(option_menu); + + gtk_widget_show(hbox2); + + label = gtk_label_new("Filter length:"); + gtk_misc_set_alignment(GTK_MISC(label), 0.5, 1.0); + gtk_container_add(GTK_CONTAINER(vbox2),label); + gtk_widget_show(label); + + scale_data = gtk_adjustment_new(licvals.filtlen, 0, 64, 1.0, 1.0, 0.0); + gtk_signal_connect(GTK_OBJECT(scale_data), "value_changed", + (GtkSignalFunc)effect_parameter_update, + (gpointer)&licvals.filtlen); + scale = gtk_hscale_new(GTK_ADJUSTMENT(scale_data)); + gtk_range_set_update_policy(GTK_RANGE(scale), GTK_UPDATE_CONTINUOUS); + gtk_container_add(GTK_CONTAINER(vbox2),scale); + gtk_widget_show(scale); + + label = gtk_label_new("Noise magnitude:"); + gtk_misc_set_alignment(GTK_MISC(label), 0.5, 1.0); + gtk_container_add(GTK_CONTAINER(vbox2),label); + gtk_widget_show(label); + + scale_data = gtk_adjustment_new(licvals.noisemag, 1, 5, 1.0, 1.0, 0.0); + gtk_signal_connect(GTK_OBJECT(scale_data), "value_changed", + (GtkSignalFunc)effect_parameter_update, + (gpointer)&licvals.noisemag); + + scale = gtk_hscale_new(GTK_ADJUSTMENT(scale_data)); + gtk_range_set_update_policy(GTK_RANGE(scale), GTK_UPDATE_CONTINUOUS); + gtk_container_add(GTK_CONTAINER(vbox2),scale); + gtk_widget_show(scale); + + label = gtk_label_new("Integration steps:"); + gtk_misc_set_alignment(GTK_MISC(label), 0.5, 1.0); + gtk_container_add(GTK_CONTAINER(vbox2),label); + gtk_widget_show(label); + + scale_data = gtk_adjustment_new(licvals.intsteps, 1, 40, 1.0, 1.0, 0.0); + gtk_signal_connect(GTK_OBJECT(scale_data), "value_changed", + (GtkSignalFunc)effect_parameter_update, + (gpointer)&licvals.intsteps); + + scale = gtk_hscale_new(GTK_ADJUSTMENT(scale_data)); + gtk_range_set_update_policy(GTK_RANGE(scale), GTK_UPDATE_CONTINUOUS); + gtk_container_add(GTK_CONTAINER(vbox2),scale); + gtk_widget_show(scale); + + label = gtk_label_new("Minimum value:"); + gtk_misc_set_alignment(GTK_MISC(label), 0.5, 1.0); + gtk_container_add(GTK_CONTAINER(vbox2),label); + gtk_widget_show(label); + + scale_data = gtk_adjustment_new(licvals.minv, -100, 0, 1.0, 1.0, 0.0); + gtk_signal_connect(GTK_OBJECT(scale_data), "value_changed", + (GtkSignalFunc)effect_parameter_update, + (gpointer)&licvals.minv); + + scale = gtk_hscale_new(GTK_ADJUSTMENT(scale_data)); + gtk_range_set_update_policy(GTK_RANGE(scale), GTK_UPDATE_CONTINUOUS); + gtk_container_add(GTK_CONTAINER(vbox2),scale); + gtk_widget_show(scale); + + label = gtk_label_new("Maximum value:"); + gtk_misc_set_alignment(GTK_MISC(label), 0.5, 1.0); + gtk_container_add(GTK_CONTAINER(vbox2),label); + gtk_widget_show(label); + + scale_data = gtk_adjustment_new(licvals.maxv, 0, 100, 1.0, 1.0, 0.0); + gtk_signal_connect(GTK_OBJECT(scale_data), "value_changed", + (GtkSignalFunc)effect_parameter_update, + (gpointer)&licvals.maxv); + + scale = gtk_hscale_new(GTK_ADJUSTMENT(scale_data)); + gtk_range_set_update_policy(GTK_RANGE(scale), GTK_UPDATE_CONTINUOUS); + gtk_container_add(GTK_CONTAINER(vbox2),scale); + gtk_widget_show(scale); + + gtk_widget_show(vbox2); + gtk_widget_show(frame); + + gtk_widget_show(vbox); + gtk_widget_show(hbox); + + /* Buttons */ + + gtk_container_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), 6); + + button = gtk_button_new_with_label("OK"); + GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT); + gtk_signal_connect(GTK_OBJECT(button), "clicked", + (GtkSignalFunc)ok_button_clicked,(gpointer)dialog); + + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button, TRUE, TRUE, 0); + gtk_widget_grab_default(button); + gtk_widget_show(button); + + button = gtk_button_new_with_label("Cancel"); + GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT); + gtk_signal_connect(GTK_OBJECT(button), "clicked", + (GtkSignalFunc)cancel_button_clicked,(gpointer)dialog); + + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button, TRUE, TRUE, 0); + gtk_widget_show(button); + + /* Done */ + + gtk_widget_show(dialog); +} + +/******************/ +/* Implementation */ +/******************/ + +void lic_interactive (GDrawable *drawable); +void lic_noninteractive (GDrawable *drawable); + +/*************************************/ +/* Set parameters to standard values */ +/*************************************/ + +void set_default_settings(void) +{ + licvals.filtlen=5; + licvals.noisemag=2; + licvals.intsteps=25; + licvals.minv=-25; + licvals.maxv=25; + licvals.create_new_image=TRUE; + licvals.effect_channel=2; + licvals.effect_operator=1; + licvals.effect_convolve=1; + licvals.effect_image_id=0; +} + +MAIN() + +static void query(void) +{ + static GParamDef args[] = + { + { PARAM_INT32, "run_mode", "Interactive" }, + { PARAM_IMAGE, "image", "Input image" }, + { PARAM_DRAWABLE, "drawable", "Input drawable" }, + }; + + static GParamDef *return_vals = NULL; + static gint nargs = sizeof (args) / sizeof (args[0]); + static gint nreturn_vals = 0; + + gimp_install_procedure ("plug_in_lic", + "Creates a Van Gogh effect (Line Integral Convolution)", + "No help yet", + "Tom Bech & Federico Mena Quintero", + "Tom Bech & Federico Mena Quintero", + "Version 0.14, September 24 1997", + "/Filters/Artistic/Van Gogh (LIC)", + "RGB", + PROC_PLUG_IN, + nargs, nreturn_vals, + args, return_vals); +} + +static void run(gchar *name, + gint nparams, + GParam *param, + gint *nreturn_vals, + GParam **return_vals) +{ + static GParam values[1]; + GDrawable *drawable; + GRunModeType run_mode; + GStatusType status = STATUS_SUCCESS; + + run_mode = param[0].data.d_int32; + + *nreturn_vals = 1; + *return_vals = values; + + values[0].type = PARAM_STATUS; + values[0].data.d_status = status; + + /* Set default values */ + /* ================== */ + + set_default_settings(); + + /* Possibly retrieve data */ + /* ====================== */ + + gimp_get_data ("plug_in_lic", &licvals); + + /* Get the specified drawable */ + /* ========================== */ + + drawable = gimp_drawable_get (param[2].data.d_drawable); + + if (status == STATUS_SUCCESS) + { + /* Make sure that the drawable is RGBA or RGB color */ + /* ================================================ */ + + if (gimp_drawable_color(drawable->id)) + { + /* Set the tile cache size */ + /* ======================= */ + + gimp_tile_cache_ntiles(TILE_CACHE_SIZE); + + switch (run_mode) + { + case RUN_INTERACTIVE: + lic_interactive(drawable); + gimp_set_data("plug_in_lic", &licvals, sizeof(LicValues)); + break; + case RUN_WITH_LAST_VALS: + image_setup(drawable,FALSE); + compute_image(); + break; + default: + break; + } + } + else + status = STATUS_EXECUTION_ERROR; + } + + values[0].data.d_status = status; + gimp_drawable_detach (drawable); +} + +GPlugInInfo PLUG_IN_INFO = +{ + NULL, /* init_proc */ + NULL, /* quit_proc */ + query, /* query_proc */ + run, /* run_proc */ +}; + +void lic_interactive(GDrawable *drawable) +{ + gchar **argv; + gint argc; + + argc = 1; + argv = g_new (gchar *, 1); + argv[0] = g_strdup ("lic"); + + gtk_init (&argc, &argv); + gtk_rc_parse(gimp_gtkrc()); + + gdk_set_use_xshm(gimp_use_xshm()); + + /* Create application window */ + /* ========================= */ + + create_main_dialog(); + + /* Prepare images */ + /* ============== */ + + image_setup(drawable,TRUE); + + /* Gtk main event loop */ + /* =================== */ + + gtk_main(); + gdk_flush(); +} + +void lic_noninteractive(GDrawable *drawable) +{ + printf("Noninteractive not yet implemented! Sorry.\n"); +} + diff --git a/plug-ins/mapcolor/.cvsignore b/plug-ins/mapcolor/.cvsignore new file mode 100644 index 0000000000..4587700cb8 --- /dev/null +++ b/plug-ins/mapcolor/.cvsignore @@ -0,0 +1,6 @@ +Makefile.in +Makefile +.deps +_libs +.libs +mapcolor diff --git a/plug-ins/mapcolor/Makefile.am b/plug-ins/mapcolor/Makefile.am new file mode 100644 index 0000000000..09ffe54f71 --- /dev/null +++ b/plug-ins/mapcolor/Makefile.am @@ -0,0 +1,36 @@ +## Process this file with automake to produce Makefile.in + +pluginlibdir = $(gimpplugindir)/plug-ins + +pluginlib_PROGRAMS = mapcolor + +mapcolor_SOURCES = \ + mapcolor.c + +INCLUDES = \ + -I$(top_srcdir) \ + $(GTK_CFLAGS) \ + -I$(includedir) + +LDADD = \ + $(top_builddir)/libgimp/libgimp.la \ + $(GTK_LIBS) \ + @LIBJPEG_LIB@ + +DEPS = \ + $(top_builddir)/libgimp/libgimp.la + +mapcolor_DEPENDENCIES = $(DEPS) + +.PHONY: files + +files: + @files=`ls $(DISTFILES) 2> /dev/null`; for p in $$files; do \ + echo $$p; \ + done + @for subdir in $(SUBDIRS); do \ + files=`cd $$subdir; $(MAKE) files | grep -v "make\[[1-9]\]"`; \ + for file in $$files; do \ + echo $$subdir/$$file; \ + done; \ + done diff --git a/plug-ins/mapcolor/mapcolor.c b/plug-ins/mapcolor/mapcolor.c new file mode 100644 index 0000000000..936e2537c9 --- /dev/null +++ b/plug-ins/mapcolor/mapcolor.c @@ -0,0 +1,696 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * mapcolor plugin + * Copyright (C) 1998 Peter Kirchgessner + * (email: pkirchg@aol.com, WWW: http://members.aol.com/pkirchg) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +/* Event history: + * V 1.00, PK, 26-Oct-98: Creation. + */ +#define VERSIO 1.00 +static char dversio[] = "v1.00 26-Oct-98"; +static char ident[] = "@(#) GIMP mapcolor plug-in v1.00 26-Oct-98"; + +#include +#include +#include +#include +#include +#include "gtk/gtk.h" +#include "libgimp/gimp.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define PRV_WIDTH 50 +#define PRV_HEIGHT 20 + +typedef struct +{ + guchar colors[4][3]; + gint32 map_mode; +} PLVALS; + +typedef struct +{ + GtkWidget *activate; /* The button that activates the color sel. dialog */ + GtkWidget *colselect; /* The colour selection dialog itself */ + GtkWidget *preview; /* The colour preview */ + unsigned char color[3];/* The selected colour */ +} CSEL; + + +typedef struct +{ + GtkWidget *dialog; + CSEL csel[4]; + PLVALS *plvals; + gint run; /* run */ +} RunInterface; + +static RunInterface runint = +{ + NULL, /* dialog */ + { + { NULL, NULL, NULL, { 0 } }, /* Color selection dialogs */ + { NULL, NULL, NULL, { 0 } }, + { NULL, NULL, NULL, { 0 } }, + { NULL, NULL, NULL, { 0 } } + }, + NULL, /* plug-in values */ + FALSE /* run */ +}; + + +/* Declare some local functions. + */ +static void query (void); +static void run (char *name, + int nparams, + GParam *param, + int *nreturn_vals, + GParam **return_vals); + +GPlugInInfo PLUG_IN_INFO = +{ + NULL, /* init_proc */ + NULL, /* quit_proc */ + query, /* query_proc */ + run, /* run_proc */ +}; + +static gint dialog (PLVALS *plvals); +static void mapcolor_close_callback (GtkWidget *widget, + gpointer data); +static void mapcolor_ok_callback (GtkWidget *widget, + gpointer data); +static void add_color_button (int csel_index, int left, int top, + GtkWidget *table, PLVALS *plvals); +static void color_select_ok_callback (GtkWidget *widget, + gpointer data); +static void color_select_cancel_callback (GtkWidget *widget, + gpointer data); +static void color_preview_show (GtkWidget *widget, + unsigned char *color); + +static void color_mapping (GDrawable *drawable, PLVALS *plvals); + + +/* The run mode */ +static GRunModeType l_run_mode; + + +MAIN () + + +static void +query (void) + +{ + static GParamDef adjust_args[] = + { + { PARAM_INT32, "run_mode", "Interactive, non-interactive" }, + { PARAM_IMAGE, "image", "Input image (not used)" }, + { PARAM_DRAWABLE, "drawable", "Input drawable to adjust" } + }; + static int nadjust_args = sizeof (adjust_args) / sizeof (adjust_args[0]); + + static GParamDef map_args[] = + { + { PARAM_INT32, "run_mode", "Interactive, non-interactive" }, + { PARAM_IMAGE, "image", "Input image (not used)" }, + { PARAM_DRAWABLE, "drawable", "Input drawable where colors are to map" }, + { PARAM_COLOR, "srccolor_1", "First source color" }, + { PARAM_COLOR, "srccolor_2", "Second source color" }, + { PARAM_COLOR, "dstcolor_1", "First destination color" }, + { PARAM_COLOR, "dstcolor_2", "Second destination color" }, + { PARAM_INT32, "map_mode", "Mapping mode (0: linear, others reserved)" } + }; + static int nmap_args = sizeof (map_args) / sizeof (map_args[0]); + + gimp_install_procedure ("plug_in_color_adjust", + "Adjust current foreground/background color in the\ + drawable to black/white", + "The current foreground color is mapped to black, \ +the current background color is mapped to white.", + "Peter Kirchgessner", + "Peter Kirchgessner", + dversio, + "/Filters/Colors/Adjust Fgrd.-Bkgrd.", + "RGB*", + PROC_PLUG_IN, + nadjust_args, 0, + adjust_args, NULL); + + gimp_install_procedure ("plug_in_color_map", + "Map two source colors to two destination colors. \ +Other colors are mapped by interpolation.", + "Map two source colors to two destination colors. \ +Other colors are mapped by interpolation.", + "Peter Kirchgessner", + "Peter Kirchgessner", + dversio, + "/Filters/Colors/Color Mapping", + "RGB*", + PROC_PLUG_IN, + nmap_args, 0, + map_args, NULL); +} + + +static void +run (char *name, + int nparams, + GParam *param, + int *nreturn_vals, + GParam **return_vals) + +{ + static GParam values[1]; + GRunModeType run_mode; + GDrawable *drawable = NULL; + GStatusType status = STATUS_SUCCESS; + guchar *c = (guchar *)ident; + int j; + static PLVALS plvals = + { + { { 0, 0, 0}, { 255, 255, 255 }, { 0, 0, 0 }, { 255, 255, 255 } }, + 0 + }; + + l_run_mode = run_mode = param[0].data.d_int32; + + *nreturn_vals = 1; + *return_vals = values; + + values[0].type = PARAM_STATUS; + values[0].data.d_status = status; + + while (status == STATUS_SUCCESS) + { + if (nparams < 3) + { + status = STATUS_CALLING_ERROR; + break; + } + + /* Make sure the drawable is RGB color */ + drawable = gimp_drawable_get (param[2].data.d_drawable); + if (!gimp_drawable_color (drawable->id)) + { + gimp_message ("color_adjust/color_map: cannot operate on grey/indexed\ + images"); + status = STATUS_EXECUTION_ERROR; + break; + } + + if (strcmp (name, "plug_in_color_adjust") == 0) + { + if (nparams != 3) /* Make sure all the arguments are there */ + { + status = STATUS_CALLING_ERROR; + break; + } + + c = &(plvals.colors[0][0]); /* First source color */ + gimp_palette_get_foreground (c, c+1, c+2); + c = &(plvals.colors[1][0]); /* Second source color */ + gimp_palette_get_background (c, c+1, c+2); + c = &(plvals.colors[2][0]); /* First destination color */ + c[0] = c[1] = c[2] = 0; /* Foreground mapped to black */ + c = &(plvals.colors[3][0]); /* second destination color */ + c[0] = c[1] = c[2] = 255; /* Background mapped to white */ + plvals.map_mode = 0; + + if (run_mode != RUN_NONINTERACTIVE) + gimp_progress_init ("Adjust Foreground/Background"); + + color_mapping (drawable, &plvals); + break; + } + + if (strcmp (name, "plug_in_color_map") == 0) + { + if (run_mode == RUN_NONINTERACTIVE) + { + if (nparams != 8) /* Make sure all the arguments are there */ + { + status = STATUS_CALLING_ERROR; + break; + } + + for (j = 0; j < 4; j++) + { + plvals.colors[j][0] = param[3+j].data.d_color.red; + plvals.colors[j][1] = param[3+j].data.d_color.green; + plvals.colors[j][2] = param[3+j].data.d_color.blue; + } + plvals.map_mode = param[7].data.d_int32; + } + else if (run_mode == RUN_INTERACTIVE) + { + gimp_get_data (name, &plvals); + + c = &(plvals.colors[0][0]); /* First source color */ + gimp_palette_get_foreground (c, c+1, c+2); + c = &(plvals.colors[1][0]); /* Second source color */ + gimp_palette_get_background (c, c+1, c+2); + + if (!dialog (&plvals)) break; + } + else if (run_mode == RUN_WITH_LAST_VALS) + { + gimp_get_data (name, &plvals); + } + else + { + status = STATUS_CALLING_ERROR; + break; + } + + if (run_mode != RUN_NONINTERACTIVE) + gimp_progress_init ("Mapping colors"); + + color_mapping (drawable, &plvals); + + if (run_mode == RUN_INTERACTIVE) + gimp_set_data (name, &plvals, sizeof (plvals)); + + break; + } + + status = STATUS_EXECUTION_ERROR; + } + + if ((status == STATUS_SUCCESS) && (run_mode != RUN_NONINTERACTIVE)) + gimp_displays_flush (); + + if (drawable != NULL) gimp_drawable_detach (drawable); + values[0].data.d_status = status; +} + + +static gint +dialog (PLVALS *plvals) + +{ + GtkWidget *button; + GtkWidget *dlg; + GtkWidget *hbox; + GtkWidget *table; + guchar *color_cube; + gchar **argv; + gint argc; + + argc = 1; + argv = g_new (gchar *, 1); + argv[0] = g_strdup ("Map colors"); + + gtk_init (&argc, &argv); + gtk_rc_parse (gimp_gtkrc ()); + + gdk_set_use_xshm(gimp_use_xshm()); + + gtk_preview_set_gamma(gimp_gamma()); + gtk_preview_set_install_cmap(gimp_install_cmap()); + color_cube = gimp_color_cube(); + gtk_preview_set_color_cube(color_cube[0], color_cube[1], color_cube[2], + color_cube[3]); + gtk_widget_set_default_visual(gtk_preview_get_visual()); + gtk_widget_set_default_colormap(gtk_preview_get_cmap()); + + runint.dialog = dlg = gtk_dialog_new (); + gtk_window_set_title (GTK_WINDOW (dlg), "Map colors"); + gtk_window_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE); + gtk_signal_connect (GTK_OBJECT (dlg), "destroy", + (GtkSignalFunc) mapcolor_close_callback, + NULL); + + /* Action area */ + button = gtk_button_new_with_label ("OK"); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) mapcolor_ok_callback, dlg); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, + TRUE, TRUE, 0); + gtk_widget_grab_default (button); + gtk_widget_show (button); + + button = gtk_button_new_with_label ("Cancel"); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_signal_connect_object (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) gtk_widget_destroy, + GTK_OBJECT (dlg)); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, + TRUE, TRUE, 0); + gtk_widget_show (button); + + /* parameter settings */ + runint.plvals = plvals; + + hbox = gtk_hbox_new (FALSE, 0); + gtk_container_border_width (GTK_CONTAINER (hbox), 0); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), hbox, TRUE, TRUE, 0); + gtk_widget_show (hbox); + + /* The table keeps the color selections */ + table = gtk_table_new (4, 2, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (table), 5); + gtk_table_set_col_spacings (GTK_TABLE (table), 5); + gtk_box_pack_start (GTK_BOX (hbox), table, TRUE, TRUE, 0); + gtk_widget_show (table); + + add_color_button (0, 0, 1, table, plvals); + add_color_button (1, 0, 2, table, plvals); + add_color_button (2, 2, 1, table, plvals); + add_color_button (3, 2, 2, table, plvals); + + gtk_widget_show (dlg); + + gtk_main (); + gdk_flush (); + + return runint.run; +} + + +static void +color_preview_show (GtkWidget *widget, + unsigned char *rgb) + +{guchar *buf, *bp; + int j, width, height; + + width = PRV_WIDTH; + height = PRV_HEIGHT; + + bp = buf = g_malloc (width*3); + if (buf == NULL) return; + + for (j = 0; j < width; j++) + { + *(bp++) = rgb[0]; + *(bp++) = rgb[1]; + *(bp++) = rgb[2]; + } + for (j = 0; j < height; j++) + gtk_preview_draw_row (GTK_PREVIEW (widget), buf, 0, j, width); + + gtk_widget_draw (widget, NULL); + + g_free (buf); +} + + +static void +color_select_ok_callback (GtkWidget *widget, + gpointer data) + +{gdouble color[3]; + int idx, j; + GtkWidget *dialog; + + idx = (int)data; + if ((dialog = runint.csel[idx].colselect) == NULL) return; + + gtk_color_selection_get_color ( + GTK_COLOR_SELECTION (GTK_COLOR_SELECTION_DIALOG (dialog)->colorsel), + color); + + for (j = 0; j < 3; j++) + runint.csel[idx].color[j] = (unsigned char)(color[j]*255.0); + + color_preview_show (runint.csel[idx].preview, &(runint.csel[idx].color[0])); + + runint.csel[idx].colselect = NULL; + gtk_widget_destroy (dialog); +} + + +static void +color_select_cancel_callback (GtkWidget *widget, + gpointer data) + +{int idx; + GtkWidget *dialog; + + idx = (int)data; + if ((dialog = runint.csel[idx].colselect) == NULL) return; + + runint.csel[idx].colselect = NULL; + gtk_widget_destroy (dialog); +} + + +static void +mapcolor_color_button_callback (GtkWidget *widget, + gpointer data) + +{int idx, j; + GtkColorSelectionDialog *csd; + GtkWidget *dialog; + gdouble colour[3]; + static char *label[4] = { "First source color", "Second source color", + "First destination color", "Second destination color" }; + + idx = (int)data; + + /* Is the colour selection dialog already running ? */ + if (runint.csel[idx].colselect != NULL) return; + + for (j = 0; j < 3; j++) + colour[j] = runint.csel[idx].color[j] / 255.0; + + dialog = runint.csel[idx].colselect = gtk_color_selection_dialog_new ( + label[idx]); + + csd = GTK_COLOR_SELECTION_DIALOG (dialog); + + gtk_widget_destroy (csd->help_button); + + gtk_signal_connect (GTK_OBJECT (dialog), "destroy", + (GtkSignalFunc) color_select_cancel_callback, data); + gtk_signal_connect (GTK_OBJECT (csd->ok_button), "clicked", + (GtkSignalFunc) color_select_ok_callback, data); + gtk_signal_connect (GTK_OBJECT (csd->cancel_button), "clicked", + (GtkSignalFunc) color_select_cancel_callback, data); + + gtk_color_selection_set_color (GTK_COLOR_SELECTION (csd->colorsel), colour); + + gtk_window_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE); + gtk_widget_show (dialog); +} + + +static void +add_color_button (int csel_index, + int left, + int top, + GtkWidget *table, + PLVALS *plvals) + +{ + GtkWidget *label; + GtkWidget *button; + GtkWidget *preview; + GtkWidget *hbox; + + hbox = gtk_hbox_new (FALSE, 0); + gtk_container_border_width (GTK_CONTAINER (hbox), 5); + gtk_table_attach (GTK_TABLE (table), hbox, left, left+1, top, top+1, + GTK_FILL, GTK_FILL, 0, 0); + + label = gtk_label_new ((left == 0) ? "From:" : "To:"); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0); + gtk_widget_show (label); + gtk_widget_show (hbox); + + hbox = gtk_hbox_new (FALSE, 0); + gtk_container_border_width (GTK_CONTAINER (hbox), 5); + gtk_table_attach (GTK_TABLE (table), hbox, left+1, left+2, top, top+1, + GTK_FILL, GTK_FILL, 0, 0); + + button = runint.csel[csel_index].activate = gtk_button_new (); + + memcpy (&(runint.csel[csel_index].color[0]), + &(plvals->colors[csel_index][0]), 3); + preview = runint.csel[csel_index].preview = gtk_preview_new(GTK_PREVIEW_COLOR); + gtk_preview_size (GTK_PREVIEW (preview), PRV_WIDTH, PRV_HEIGHT); + gtk_container_add (GTK_CONTAINER (button), preview); + gtk_widget_show (preview); + + color_preview_show (preview, &(runint.csel[csel_index].color[0])); + + gtk_signal_connect (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) mapcolor_color_button_callback, + (gpointer)csel_index); + gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); + gtk_widget_show (button); + gtk_widget_show (hbox); +} + + +static void +mapcolor_close_callback (GtkWidget *widget, + gpointer data) + +{ + gtk_main_quit (); +} + + +static void +mapcolor_ok_callback (GtkWidget *widget, + gpointer data) + +{int j; + GtkWidget *dialog; + PLVALS *plvals = runint.plvals; + + for (j = 0; j < 4; j++) + memcpy (&(plvals->colors[j][0]), &(runint.csel[j].color[0]), 3); + + /* Destroy color selection dialogs if still running */ + for (j = 0; j < 4; j++) + { + dialog = runint.csel[j].colselect; + if (dialog != NULL) + { + runint.csel[j].colselect = NULL; + gtk_widget_destroy (GTK_WIDGET (dialog)); + } + } + plvals->map_mode = 0; /* Currently always linear mapping */ + + runint.run = TRUE; + gtk_widget_destroy (GTK_WIDGET (data)); +} + + +static void +get_mapping (guchar *src_col1, + guchar *src_col2, + guchar *dst_col1, + guchar *dst_col2, + gint32 map_mode, + guchar *redmap, + guchar *greenmap, + guchar *bluemap) + +{int rgb, i, j, a, as, b, bs; + guchar *colormap[3]; + + /* Currently we always do a linear mapping */ + + colormap[0] = redmap; + colormap[1] = greenmap; + colormap[2] = bluemap; + + switch (map_mode) + { + case 0: + default: + for (rgb = 0; rgb < 3; rgb++) + { + a = src_col1[rgb]; as = dst_col1[rgb]; + b = src_col2[rgb]; bs = dst_col2[rgb]; + for (i = 0; i < 256; i++) + { + j = ((i - a) * (bs - as)) / (b - a) + as; + if (j > 255) j = 255; else if (j < 0) j = 0; + colormap[rgb][i] = j; + } + } + break; + } +} + + +static void +color_mapping (GDrawable *drawable, + PLVALS *plvals) + +{ + int processed, total; + gint x, y, xmin, xmax, ymin, ymax; + unsigned char *src, *dest; + GPixelRgn src_rgn, dest_rgn; + gpointer pr; + double progress; + unsigned char redmap[256], greenmap[256], bluemap[256]; + unsigned char *src_col1 = plvals->colors[0]; + unsigned char *src_col2 = plvals->colors[1]; + unsigned char *dst_col1 = plvals->colors[2]; + unsigned char *dst_col2 = plvals->colors[3]; + + if ( (src_col1[0] == src_col2[0]) + || (src_col1[1] == src_col2[1]) + || (src_col1[2] == src_col2[2])) return; + + gimp_drawable_mask_bounds (drawable->id, &xmin, &ymin, &xmax, &ymax); + if ((ymin == ymax) || (xmin == xmax)) return; + total = (xmax - xmin) * (ymax - ymin); + + gimp_tile_cache_ntiles (2 * (drawable->width / gimp_tile_width () + 1)); + gimp_pixel_rgn_init (&src_rgn, drawable, xmin, ymin, + (xmax - xmin), (ymax - ymin), FALSE, FALSE); + gimp_pixel_rgn_init (&dest_rgn, drawable, xmin, ymin, + (xmax - xmin), (ymax - ymin), TRUE, TRUE); + + pr = gimp_pixel_rgns_register (2, &src_rgn, &dest_rgn); + + get_mapping (src_col1, src_col2, dst_col1, dst_col2, plvals->map_mode, + redmap, greenmap, bluemap); + + processed = 0; + progress = 0.0; + for (; pr != NULL; pr = gimp_pixel_rgns_process (pr)) + { + for (y = 0; y < src_rgn.h; y++) + { + src = src_rgn.data + y * src_rgn.rowstride; + dest = dest_rgn.data + y * dest_rgn.rowstride; + for (x = 0; x < src_rgn.w; x++) + { + dest[0] = redmap[src[0]]; + dest[1] = greenmap[src[1]]; + dest[2] = bluemap[src[2]]; + src += drawable->bpp; + dest += drawable->bpp; + processed++; + } + } + if (l_run_mode != RUN_NONINTERACTIVE) + { + if ((double)processed/(double)total - progress > 0.1) + { + progress = (double)processed/(double)total; + gimp_progress_update (progress); + } + } + } + if (l_run_mode != RUN_NONINTERACTIVE) + gimp_progress_update (1.0); + + gimp_drawable_flush (drawable); + gimp_drawable_merge_shadow (drawable->id, TRUE); + gimp_drawable_update (drawable->id, xmin, ymin, (xmax - xmin), (ymax - ymin)); +} diff --git a/plug-ins/script-fu/script-fu-enums.h b/plug-ins/script-fu/script-fu-enums.h index 33570f2866..4a2bff1cde 100644 --- a/plug-ins/script-fu/script-fu-enums.h +++ b/plug-ins/script-fu/script-fu-enums.h @@ -35,7 +35,8 @@ typedef enum SF_FONT, SF_PATTERN, SF_BRUSH, - SF_GRADIENT + SF_GRADIENT, + SF_FILENAME } SFArgType; typedef enum diff --git a/plug-ins/script-fu/script-fu-interface.c b/plug-ins/script-fu/script-fu-interface.c index 570ec11c00..1fa13b9a99 100644 --- a/plug-ins/script-fu/script-fu-interface.c +++ b/plug-ins/script-fu/script-fu-interface.c @@ -68,6 +68,13 @@ typedef struct gchar *fontname; } SFFont; +typedef struct +{ + GtkWidget *preview; + GtkWidget *dialog; + gchar *filename; +} SFFilename; + typedef struct { gchar *name; @@ -88,6 +95,7 @@ typedef union gchar * sfa_value; SFAdjustment sfa_adjustment; SFFont sfa_font; + SFFilename sfa_file; gchar * sfa_pattern; gchar * sfa_gradient; SFBrush sfa_brush; @@ -140,7 +148,9 @@ static void script_fu_disable_cc (gint err_msg); static void script_fu_interface (SFScript *script); static void script_fu_color_preview (GtkWidget *preview, gdouble *color); -static void script_fu_font_preview (GtkWidget *preview, +static void script_fu_file_preview (GtkWidget *preview, + gchar *fontname); +static void script_fu_font_preview (GtkWidget *preview, gchar *fontname); static void script_fu_cleanup_widgets (SFScript *script); static void script_fu_ok_callback (GtkWidget *widget, @@ -164,6 +174,15 @@ static void script_fu_color_preview_cancel (GtkWidget *widget, static gint script_fu_color_preview_delete (GtkWidget *widget, GdkEvent *event, gpointer data); +static void script_fu_file_preview_callback (GtkWidget *widget, + gpointer data); +static void script_fu_file_dialog_ok (GtkWidget *widget, + gpointer data); +static void script_fu_file_dialog_cancel (GtkWidget *widget, + gpointer data); +static gint script_fu_file_dialog_delete (GtkWidget *widget, + GdkEvent *event, + gpointer data); static void script_fu_font_preview_callback (GtkWidget *widget, gpointer data); static void script_fu_font_dialog_ok (GtkWidget *widget, @@ -575,6 +594,19 @@ script_fu_add_script (LISP a) args[i + 1].description = script->arg_labels[i]; break; + case SF_FILENAME: + if (!TYPEP (car (a), tc_string)) + return my_err ("script-fu-register: filename defaults must be string values", NIL); + script->arg_defaults[i].sfa_file.filename = g_strdup (get_c_string (car (a))); + script->arg_values[i].sfa_file.filename = g_strdup (script->arg_defaults[i].sfa_file.filename); + script->arg_values[i].sfa_file.preview = NULL; + script->arg_values[i].sfa_file.dialog = NULL; + + args[i + 1].type = PARAM_STRING; + args[i + 1].name = "filename"; + args[i + 1].description = script->arg_labels[i]; + break; + case SF_FONT: if (!TYPEP (car (a), tc_string)) return my_err ("script-fu-register: font defaults must be string values", NIL); @@ -776,6 +808,9 @@ script_fu_script_proc (char *name, case SF_STRING: length += strlen (params[i + 1].data.d_string) + 3; break; + case SF_FILENAME: + length += strlen (params[i + 1].data.d_string) + 3; + break; case SF_ADJUSTMENT: length += strlen (params[i + 1].data.d_string) + 1; break; @@ -827,6 +862,7 @@ script_fu_script_proc (char *name, text = params[i + 1].data.d_string; break; case SF_STRING: + case SF_FILENAME: g_snprintf (buffer, MAX_STRING_LENGTH, "\"%s\"", params[i + 1].data.d_string); text = buffer; break; @@ -938,6 +974,10 @@ script_fu_free_script (SFScript *script) break; case SF_ADJUSTMENT: break; + case SF_FILENAME: + g_free (script->arg_defaults[i].sfa_file.filename); + g_free (script->arg_values[i].sfa_file.filename); + break; case SF_FONT: g_free (script->arg_defaults[i].sfa_font.fontname); g_free (script->arg_values[i].sfa_font.fontname); @@ -1180,6 +1220,7 @@ script_fu_interface (SFScript *script) gtk_range_set_update_policy (GTK_RANGE (script->args_widgets[i]), GTK_UPDATE_DELAYED); break; + case SF_SPINNER: script->args_widgets[i] = gtk_spin_button_new (script->arg_values[i].sfa_adjustment.adj, 0, 0); gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (script->args_widgets[i]), TRUE); @@ -1193,6 +1234,23 @@ script_fu_interface (SFScript *script) break; } break; + + case SF_FILENAME: + script->args_widgets[i] = gtk_button_new(); + script->arg_values[i].sfa_file.preview = gtk_label_new (""); + gtk_widget_set_usize (script->args_widgets[i], TEXT_WIDTH, 0); + gtk_container_add (GTK_CONTAINER (script->args_widgets[i]), + script->arg_values[i].sfa_file.preview); + gtk_widget_show (script->arg_values[i].sfa_file.preview); + + script_fu_file_preview (script->arg_values[i].sfa_file.preview, + script->arg_values[i].sfa_file.filename); + + gtk_signal_connect (GTK_OBJECT (script->args_widgets[i]), "clicked", + (GtkSignalFunc) script_fu_file_preview_callback, + &script->arg_values[i].sfa_file); + break; + case SF_FONT: script->args_widgets[i] = gtk_button_new(); script->arg_values[i].sfa_font.preview = gtk_label_new (""); @@ -1208,6 +1266,7 @@ script_fu_interface (SFScript *script) (GtkSignalFunc) script_fu_font_preview_callback, &script->arg_values[i].sfa_font); break; + case SF_PATTERN: script->args_widgets[i] = gimp_pattern_select_widget("Script-fu Pattern Selection", script->arg_values[i].sfa_pattern, @@ -1220,6 +1279,7 @@ script_fu_interface (SFScript *script) script_fu_gradient_preview, &script->arg_values[i].sfa_gradient); break; + case SF_BRUSH: script->args_widgets[i] = gimp_brush_select_widget("Script-fu brush Selection", @@ -1234,15 +1294,16 @@ script_fu_interface (SFScript *script) default: break; } + hbox = gtk_hbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (hbox), script->args_widgets[i], - ((script->arg_types[i] == SF_VALUE) || (script->arg_types[i] == SF_STRING) || (script->arg_types[i] == SF_FONT)), - ((script->arg_types[i] == SF_VALUE) || (script->arg_types[i] == SF_STRING) || (script->arg_types[i] == SF_FONT)), 0); + ((script->arg_types[i] == SF_VALUE) || (script->arg_types[i] == SF_STRING) || (script->arg_types[i] == SF_FILENAME) ||(script->arg_types[i] == SF_FONT)), + ((script->arg_types[i] == SF_VALUE) || (script->arg_types[i] == SF_STRING) || (script->arg_types[i] == SF_FILENAME) || (script->arg_types[i] == SF_FONT)), 0); gtk_widget_show (hbox); gtk_table_attach (GTK_TABLE (table), hbox, /* script->args_widgets[i], */ 1, 2, i, i + 1, - GTK_FILL | (((script->arg_types[i] == SF_VALUE) || (script->arg_types[i] == SF_STRING) || (script->arg_types[i] == SF_FONT)) + GTK_FILL | (((script->arg_types[i] == SF_VALUE) || (script->arg_types[i] == SF_STRING) || (script->arg_types[i] == SF_FILENAME) || (script->arg_types[i] == SF_FONT)) ? GTK_EXPAND : 0), GTK_FILL, 4, 2); gtk_widget_show (script->args_widgets[i]); @@ -1382,6 +1443,18 @@ script_fu_brush_preview(char * name, /* Name */ +static void +script_fu_file_preview (GtkWidget *preview, + gchar *data) +{ + if (data == NULL) + return; + + gtk_label_set (GTK_LABEL (preview), g_basename((gchar *) data)); +} + + + static void script_fu_font_preview (GtkWidget *preview, gchar *data) @@ -1441,6 +1514,13 @@ script_fu_cleanup_widgets (SFScript *script) script->arg_values[i].sfa_color.dialog = NULL; } break; + case SF_FILENAME: + if (script->arg_values[i].sfa_file.dialog != NULL) + { + gtk_widget_destroy (script->arg_values[i].sfa_file.dialog); + script->arg_values[i].sfa_file.dialog = NULL; + } + break; case SF_FONT: if (script->arg_values[i].sfa_font.dialog != NULL) { @@ -1485,7 +1565,7 @@ script_fu_ok_callback (GtkWidget *widget, font = gdk_font_load (script->arg_values[i].sfa_font.fontname); if (font == NULL) { - g_message (" At least one font you've choosen is invalid. \n Please check your settings.\n"); + g_message ("At least one font you've choosen is invalid.\nPlease check your settings.\n"); return; } g_free (font); @@ -1517,6 +1597,9 @@ script_fu_ok_callback (GtkWidget *widget, case SF_ADJUSTMENT: length += 24; /* Maximum size of float value should not exceed this many characters */ break; + case SF_FILENAME: + length += strlen (script->arg_values[i].sfa_file.filename) + 3; + break; case SF_FONT: length += strlen (script->arg_values[i].sfa_font.fontname) + 3; break; @@ -1588,6 +1671,10 @@ script_fu_ok_callback (GtkWidget *widget, break; } break; + case SF_FILENAME: + g_snprintf (buffer, MAX_STRING_LENGTH, "\"%s\"", script->arg_values[i].sfa_file.filename); + text = buffer; + break; case SF_FONT: g_snprintf (buffer, MAX_STRING_LENGTH, "\"%s\"", script->arg_values[i].sfa_font.fontname); text = buffer; @@ -1833,6 +1920,16 @@ script_fu_reset_callback (GtkWidget *widget, gtk_adjustment_set_value (script->arg_values[i].sfa_adjustment.adj, script->arg_values[i].sfa_adjustment.value); break; + case SF_FILENAME: + g_free (script->arg_values[i].sfa_file.filename); + script->arg_values[i].sfa_file.filename = g_strdup (script->arg_defaults[i].sfa_file.filename); + if (script->arg_values[i].sfa_file.dialog) + { + gtk_file_selection_set_filename (GTK_FILE_SELECTION (script->arg_values[i].sfa_file.dialog), script->arg_values[i].sfa_file.filename); + } + script_fu_file_preview (script->arg_values[i].sfa_file.preview, + script->arg_values[i].sfa_file.filename); + break; case SF_FONT: g_free (script->arg_values[i].sfa_font.fontname); script->arg_values[i].sfa_font.fontname = g_strdup (script->arg_defaults[i].sfa_font.fontname); @@ -1967,6 +2064,77 @@ script_fu_color_preview_delete (GtkWidget *widget, return TRUE; } +static void +script_fu_file_preview_callback (GtkWidget *widget, + gpointer data) +{ + GtkFileSelection *fs; + SFFilename *file; + + file = (SFFilename *) data; + + if (!file->dialog) + { + file->dialog = gtk_file_selection_new ("Script-Fu File Selector"); + fs = GTK_FILE_SELECTION (file->dialog); + + gtk_signal_connect (GTK_OBJECT (fs->ok_button), "clicked", + (GtkSignalFunc) script_fu_file_dialog_ok, + file); + gtk_signal_connect (GTK_OBJECT (fs), "delete_event", + (GtkSignalFunc) script_fu_file_dialog_delete, + file); + gtk_signal_connect (GTK_OBJECT (fs->cancel_button), "clicked", + (GtkSignalFunc) script_fu_file_dialog_cancel, + file); + } + else + fs = GTK_FILE_SELECTION (file->dialog); + + gtk_file_selection_set_filename (GTK_FILE_SELECTION (fs), file->filename); + gtk_window_position (GTK_WINDOW (file->dialog), GTK_WIN_POS_MOUSE); + gtk_widget_show (file->dialog); +} + +static void +script_fu_file_dialog_ok (GtkWidget *widget, + gpointer data) +{ + SFFilename *file; + gchar *filename; + + file = (SFFilename *) data; + + filename = gtk_file_selection_get_filename (GTK_FILE_SELECTION (file->dialog)); + if (filename != NULL) + { + g_free (file->filename); + file->filename = g_strdup(filename); + } + gtk_widget_hide (file->dialog); + + script_fu_file_preview (file->preview, file->filename); +} + +static void +script_fu_file_dialog_cancel (GtkWidget *widget, + gpointer data) +{ + SFFilename *file; + file = (SFFilename *) data; + + gtk_widget_hide (file->dialog); +} + +static gint +script_fu_file_dialog_delete (GtkWidget *widget, + GdkEvent *event, + gpointer data) +{ + script_fu_file_dialog_cancel (widget, data); + return TRUE; + } + static void script_fu_font_preview_callback (GtkWidget *widget, gpointer data) @@ -1990,7 +2158,6 @@ script_fu_font_preview_callback (GtkWidget *widget, gtk_signal_connect (GTK_OBJECT (fsd->cancel_button), "clicked", (GtkSignalFunc) script_fu_font_dialog_cancel, font); - } else fsd = GTK_FONT_SELECTION_DIALOG (font->dialog); diff --git a/plug-ins/script-fu/script-fu-scripts.c b/plug-ins/script-fu/script-fu-scripts.c index 570ec11c00..1fa13b9a99 100644 --- a/plug-ins/script-fu/script-fu-scripts.c +++ b/plug-ins/script-fu/script-fu-scripts.c @@ -68,6 +68,13 @@ typedef struct gchar *fontname; } SFFont; +typedef struct +{ + GtkWidget *preview; + GtkWidget *dialog; + gchar *filename; +} SFFilename; + typedef struct { gchar *name; @@ -88,6 +95,7 @@ typedef union gchar * sfa_value; SFAdjustment sfa_adjustment; SFFont sfa_font; + SFFilename sfa_file; gchar * sfa_pattern; gchar * sfa_gradient; SFBrush sfa_brush; @@ -140,7 +148,9 @@ static void script_fu_disable_cc (gint err_msg); static void script_fu_interface (SFScript *script); static void script_fu_color_preview (GtkWidget *preview, gdouble *color); -static void script_fu_font_preview (GtkWidget *preview, +static void script_fu_file_preview (GtkWidget *preview, + gchar *fontname); +static void script_fu_font_preview (GtkWidget *preview, gchar *fontname); static void script_fu_cleanup_widgets (SFScript *script); static void script_fu_ok_callback (GtkWidget *widget, @@ -164,6 +174,15 @@ static void script_fu_color_preview_cancel (GtkWidget *widget, static gint script_fu_color_preview_delete (GtkWidget *widget, GdkEvent *event, gpointer data); +static void script_fu_file_preview_callback (GtkWidget *widget, + gpointer data); +static void script_fu_file_dialog_ok (GtkWidget *widget, + gpointer data); +static void script_fu_file_dialog_cancel (GtkWidget *widget, + gpointer data); +static gint script_fu_file_dialog_delete (GtkWidget *widget, + GdkEvent *event, + gpointer data); static void script_fu_font_preview_callback (GtkWidget *widget, gpointer data); static void script_fu_font_dialog_ok (GtkWidget *widget, @@ -575,6 +594,19 @@ script_fu_add_script (LISP a) args[i + 1].description = script->arg_labels[i]; break; + case SF_FILENAME: + if (!TYPEP (car (a), tc_string)) + return my_err ("script-fu-register: filename defaults must be string values", NIL); + script->arg_defaults[i].sfa_file.filename = g_strdup (get_c_string (car (a))); + script->arg_values[i].sfa_file.filename = g_strdup (script->arg_defaults[i].sfa_file.filename); + script->arg_values[i].sfa_file.preview = NULL; + script->arg_values[i].sfa_file.dialog = NULL; + + args[i + 1].type = PARAM_STRING; + args[i + 1].name = "filename"; + args[i + 1].description = script->arg_labels[i]; + break; + case SF_FONT: if (!TYPEP (car (a), tc_string)) return my_err ("script-fu-register: font defaults must be string values", NIL); @@ -776,6 +808,9 @@ script_fu_script_proc (char *name, case SF_STRING: length += strlen (params[i + 1].data.d_string) + 3; break; + case SF_FILENAME: + length += strlen (params[i + 1].data.d_string) + 3; + break; case SF_ADJUSTMENT: length += strlen (params[i + 1].data.d_string) + 1; break; @@ -827,6 +862,7 @@ script_fu_script_proc (char *name, text = params[i + 1].data.d_string; break; case SF_STRING: + case SF_FILENAME: g_snprintf (buffer, MAX_STRING_LENGTH, "\"%s\"", params[i + 1].data.d_string); text = buffer; break; @@ -938,6 +974,10 @@ script_fu_free_script (SFScript *script) break; case SF_ADJUSTMENT: break; + case SF_FILENAME: + g_free (script->arg_defaults[i].sfa_file.filename); + g_free (script->arg_values[i].sfa_file.filename); + break; case SF_FONT: g_free (script->arg_defaults[i].sfa_font.fontname); g_free (script->arg_values[i].sfa_font.fontname); @@ -1180,6 +1220,7 @@ script_fu_interface (SFScript *script) gtk_range_set_update_policy (GTK_RANGE (script->args_widgets[i]), GTK_UPDATE_DELAYED); break; + case SF_SPINNER: script->args_widgets[i] = gtk_spin_button_new (script->arg_values[i].sfa_adjustment.adj, 0, 0); gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (script->args_widgets[i]), TRUE); @@ -1193,6 +1234,23 @@ script_fu_interface (SFScript *script) break; } break; + + case SF_FILENAME: + script->args_widgets[i] = gtk_button_new(); + script->arg_values[i].sfa_file.preview = gtk_label_new (""); + gtk_widget_set_usize (script->args_widgets[i], TEXT_WIDTH, 0); + gtk_container_add (GTK_CONTAINER (script->args_widgets[i]), + script->arg_values[i].sfa_file.preview); + gtk_widget_show (script->arg_values[i].sfa_file.preview); + + script_fu_file_preview (script->arg_values[i].sfa_file.preview, + script->arg_values[i].sfa_file.filename); + + gtk_signal_connect (GTK_OBJECT (script->args_widgets[i]), "clicked", + (GtkSignalFunc) script_fu_file_preview_callback, + &script->arg_values[i].sfa_file); + break; + case SF_FONT: script->args_widgets[i] = gtk_button_new(); script->arg_values[i].sfa_font.preview = gtk_label_new (""); @@ -1208,6 +1266,7 @@ script_fu_interface (SFScript *script) (GtkSignalFunc) script_fu_font_preview_callback, &script->arg_values[i].sfa_font); break; + case SF_PATTERN: script->args_widgets[i] = gimp_pattern_select_widget("Script-fu Pattern Selection", script->arg_values[i].sfa_pattern, @@ -1220,6 +1279,7 @@ script_fu_interface (SFScript *script) script_fu_gradient_preview, &script->arg_values[i].sfa_gradient); break; + case SF_BRUSH: script->args_widgets[i] = gimp_brush_select_widget("Script-fu brush Selection", @@ -1234,15 +1294,16 @@ script_fu_interface (SFScript *script) default: break; } + hbox = gtk_hbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (hbox), script->args_widgets[i], - ((script->arg_types[i] == SF_VALUE) || (script->arg_types[i] == SF_STRING) || (script->arg_types[i] == SF_FONT)), - ((script->arg_types[i] == SF_VALUE) || (script->arg_types[i] == SF_STRING) || (script->arg_types[i] == SF_FONT)), 0); + ((script->arg_types[i] == SF_VALUE) || (script->arg_types[i] == SF_STRING) || (script->arg_types[i] == SF_FILENAME) ||(script->arg_types[i] == SF_FONT)), + ((script->arg_types[i] == SF_VALUE) || (script->arg_types[i] == SF_STRING) || (script->arg_types[i] == SF_FILENAME) || (script->arg_types[i] == SF_FONT)), 0); gtk_widget_show (hbox); gtk_table_attach (GTK_TABLE (table), hbox, /* script->args_widgets[i], */ 1, 2, i, i + 1, - GTK_FILL | (((script->arg_types[i] == SF_VALUE) || (script->arg_types[i] == SF_STRING) || (script->arg_types[i] == SF_FONT)) + GTK_FILL | (((script->arg_types[i] == SF_VALUE) || (script->arg_types[i] == SF_STRING) || (script->arg_types[i] == SF_FILENAME) || (script->arg_types[i] == SF_FONT)) ? GTK_EXPAND : 0), GTK_FILL, 4, 2); gtk_widget_show (script->args_widgets[i]); @@ -1382,6 +1443,18 @@ script_fu_brush_preview(char * name, /* Name */ +static void +script_fu_file_preview (GtkWidget *preview, + gchar *data) +{ + if (data == NULL) + return; + + gtk_label_set (GTK_LABEL (preview), g_basename((gchar *) data)); +} + + + static void script_fu_font_preview (GtkWidget *preview, gchar *data) @@ -1441,6 +1514,13 @@ script_fu_cleanup_widgets (SFScript *script) script->arg_values[i].sfa_color.dialog = NULL; } break; + case SF_FILENAME: + if (script->arg_values[i].sfa_file.dialog != NULL) + { + gtk_widget_destroy (script->arg_values[i].sfa_file.dialog); + script->arg_values[i].sfa_file.dialog = NULL; + } + break; case SF_FONT: if (script->arg_values[i].sfa_font.dialog != NULL) { @@ -1485,7 +1565,7 @@ script_fu_ok_callback (GtkWidget *widget, font = gdk_font_load (script->arg_values[i].sfa_font.fontname); if (font == NULL) { - g_message (" At least one font you've choosen is invalid. \n Please check your settings.\n"); + g_message ("At least one font you've choosen is invalid.\nPlease check your settings.\n"); return; } g_free (font); @@ -1517,6 +1597,9 @@ script_fu_ok_callback (GtkWidget *widget, case SF_ADJUSTMENT: length += 24; /* Maximum size of float value should not exceed this many characters */ break; + case SF_FILENAME: + length += strlen (script->arg_values[i].sfa_file.filename) + 3; + break; case SF_FONT: length += strlen (script->arg_values[i].sfa_font.fontname) + 3; break; @@ -1588,6 +1671,10 @@ script_fu_ok_callback (GtkWidget *widget, break; } break; + case SF_FILENAME: + g_snprintf (buffer, MAX_STRING_LENGTH, "\"%s\"", script->arg_values[i].sfa_file.filename); + text = buffer; + break; case SF_FONT: g_snprintf (buffer, MAX_STRING_LENGTH, "\"%s\"", script->arg_values[i].sfa_font.fontname); text = buffer; @@ -1833,6 +1920,16 @@ script_fu_reset_callback (GtkWidget *widget, gtk_adjustment_set_value (script->arg_values[i].sfa_adjustment.adj, script->arg_values[i].sfa_adjustment.value); break; + case SF_FILENAME: + g_free (script->arg_values[i].sfa_file.filename); + script->arg_values[i].sfa_file.filename = g_strdup (script->arg_defaults[i].sfa_file.filename); + if (script->arg_values[i].sfa_file.dialog) + { + gtk_file_selection_set_filename (GTK_FILE_SELECTION (script->arg_values[i].sfa_file.dialog), script->arg_values[i].sfa_file.filename); + } + script_fu_file_preview (script->arg_values[i].sfa_file.preview, + script->arg_values[i].sfa_file.filename); + break; case SF_FONT: g_free (script->arg_values[i].sfa_font.fontname); script->arg_values[i].sfa_font.fontname = g_strdup (script->arg_defaults[i].sfa_font.fontname); @@ -1967,6 +2064,77 @@ script_fu_color_preview_delete (GtkWidget *widget, return TRUE; } +static void +script_fu_file_preview_callback (GtkWidget *widget, + gpointer data) +{ + GtkFileSelection *fs; + SFFilename *file; + + file = (SFFilename *) data; + + if (!file->dialog) + { + file->dialog = gtk_file_selection_new ("Script-Fu File Selector"); + fs = GTK_FILE_SELECTION (file->dialog); + + gtk_signal_connect (GTK_OBJECT (fs->ok_button), "clicked", + (GtkSignalFunc) script_fu_file_dialog_ok, + file); + gtk_signal_connect (GTK_OBJECT (fs), "delete_event", + (GtkSignalFunc) script_fu_file_dialog_delete, + file); + gtk_signal_connect (GTK_OBJECT (fs->cancel_button), "clicked", + (GtkSignalFunc) script_fu_file_dialog_cancel, + file); + } + else + fs = GTK_FILE_SELECTION (file->dialog); + + gtk_file_selection_set_filename (GTK_FILE_SELECTION (fs), file->filename); + gtk_window_position (GTK_WINDOW (file->dialog), GTK_WIN_POS_MOUSE); + gtk_widget_show (file->dialog); +} + +static void +script_fu_file_dialog_ok (GtkWidget *widget, + gpointer data) +{ + SFFilename *file; + gchar *filename; + + file = (SFFilename *) data; + + filename = gtk_file_selection_get_filename (GTK_FILE_SELECTION (file->dialog)); + if (filename != NULL) + { + g_free (file->filename); + file->filename = g_strdup(filename); + } + gtk_widget_hide (file->dialog); + + script_fu_file_preview (file->preview, file->filename); +} + +static void +script_fu_file_dialog_cancel (GtkWidget *widget, + gpointer data) +{ + SFFilename *file; + file = (SFFilename *) data; + + gtk_widget_hide (file->dialog); +} + +static gint +script_fu_file_dialog_delete (GtkWidget *widget, + GdkEvent *event, + gpointer data) +{ + script_fu_file_dialog_cancel (widget, data); + return TRUE; + } + static void script_fu_font_preview_callback (GtkWidget *widget, gpointer data) @@ -1990,7 +2158,6 @@ script_fu_font_preview_callback (GtkWidget *widget, gtk_signal_connect (GTK_OBJECT (fsd->cancel_button), "clicked", (GtkSignalFunc) script_fu_font_dialog_cancel, font); - } else fsd = GTK_FONT_SELECTION_DIALOG (font->dialog); diff --git a/plug-ins/script-fu/script-fu.c b/plug-ins/script-fu/script-fu.c index b408f7c2a3..9576667839 100644 --- a/plug-ins/script-fu/script-fu.c +++ b/plug-ins/script-fu/script-fu.c @@ -474,6 +474,7 @@ init_constants () setvar (cintern ("SF-TOGGLE"), flocons (SF_TOGGLE), NIL); setvar (cintern ("SF-VALUE"), flocons (SF_VALUE), NIL); setvar (cintern ("SF-STRING"), flocons (SF_STRING), NIL); + setvar (cintern ("SF-FILENAME"), flocons (SF_FILENAME), NIL); setvar (cintern ("SF-ADJUSTMENT"), flocons (SF_ADJUSTMENT), NIL); setvar (cintern ("SF-FONT"), flocons (SF_FONT), NIL); setvar (cintern ("SF-PATTERN"), flocons (SF_PATTERN), NIL); diff --git a/plug-ins/script-fu/scripts/carved-logo.scm b/plug-ins/script-fu/scripts/carved-logo.scm index 6fe63f53f6..2d5465fae6 100644 --- a/plug-ins/script-fu/scripts/carved-logo.scm +++ b/plug-ins/script-fu/scripts/carved-logo.scm @@ -160,5 +160,6 @@ SF-STRING "Text String" "Marble" SF-VALUE "Font Size (in pixels)" "100" SF-STRING "Font" "Engraver" - SF-STRING "Background Img" (string-append "" gimp-data-dir "/scripts/texture3.jpg") +; SF-STRING "Background Img" (string-append "" gimp-data-dir "/scripts/texture3.jpg") + SF-FILENAME "Background Img" (string-append "" gimp-data-dir "/scripts/texture3.jpg") SF-TOGGLE "Carve Raised Text" FALSE) diff --git a/plug-ins/script-fu/scripts/chrome-it.scm b/plug-ins/script-fu/scripts/chrome-it.scm index 676865313c..8cd73aeedb 100644 --- a/plug-ins/script-fu/scripts/chrome-it.scm +++ b/plug-ins/script-fu/scripts/chrome-it.scm @@ -193,7 +193,8 @@ SF-VALUE "Chrome Saturation" "-80" SF-VALUE "Chrome Lightness" "-47" SF-VALUE "Chrome Factor" "0.75" - SF-STRING "Environment Map" (string-append "" gimp-data-dir "/scripts/beavis.jpg") +; SF-STRING "Environment Map" (string-append "" gimp-data-dir "/scripts/beavis.jpg") + SF-FILENAME "Environment Map" (string-append "" gimp-data-dir "/scripts/beavis.jpg") SF-COLOR "Highlight Balance" '(211 95 0) SF-COLOR "Chrome Balance" '(0 0 0) SF-TOGGLE "Chrome White Areas" TRUE) diff --git a/plug-ins/script-fu/scripts/crystal-logo.scm b/plug-ins/script-fu/scripts/crystal-logo.scm index ba41ce2028..3a34660348 100644 --- a/plug-ins/script-fu/scripts/crystal-logo.scm +++ b/plug-ins/script-fu/scripts/crystal-logo.scm @@ -197,5 +197,7 @@ SF-STRING "Text String" "Crystal" SF-VALUE "Font Size (in pixels)" "150" SF-STRING "Font" "Engraver" - SF-STRING "Background Img" (string-append "" gimp-data-dir "/scripts/texture1.jpg") - SF-STRING "Environment Map" (string-append "" gimp-data-dir "/scripts/beavis.jpg")) +; SF-STRING "Background Img" (string-append "" gimp-data-dir "/scripts/texture1.jpg") + SF-FILENAME "Background Img" (string-append "" gimp-data-dir "/scripts/texture1.jpg") +; SF-STRING "Environment Map" (string-append "" gimp-data-dir "/scripts/beavis.jpg")) + SF-FILENAME "Environment Map" (string-append "" gimp-data-dir "/scripts/beavis.jpg")) diff --git a/plug-ins/script-fu/scripts/neon-logo.scm b/plug-ins/script-fu/scripts/neon-logo.scm index 3f8e08050b..750998e5ae 100644 --- a/plug-ins/script-fu/scripts/neon-logo.scm +++ b/plug-ins/script-fu/scripts/neon-logo.scm @@ -161,8 +161,8 @@ SF-STRING "Text String" "NEON" ; SF-VALUE "Font Size (in pixels)" "150" SF-ADJUSTMENT "Font Size" '(150 2 256 1 10 0 0) +; SF-STRING "Font" "Blippo" SF-FONT "Font" "-*-blippo-*-*-*-*-*-*-*-*-*-*-*-*" - SF-STRING "Font" "Blippo" SF-COLOR "Background Color" '(0 0 0) SF-COLOR "Glow Color" '(38 211 255) SF-TOGGLE "Create Shadow" FALSE) diff --git a/plug-ins/script-fu/scripts/sota-chrome-logo.scm b/plug-ins/script-fu/scripts/sota-chrome-logo.scm index 540d456110..be86acb0a4 100644 --- a/plug-ins/script-fu/scripts/sota-chrome-logo.scm +++ b/plug-ins/script-fu/scripts/sota-chrome-logo.scm @@ -199,6 +199,7 @@ SF-ADJUSTMENT "Font size (in pixels)" '(150 1 1000 1 10 0 1) ; SF-STRING "Font" "RoostHeavy" SF-FONT "Font" "-*-roostheavy-*-r-*-*-24-*-*-*-p-*-*-*" - SF-STRING "Environment Map" (string-append "" gimp-data-dir "/scripts/beavis.jpg") +; SF-STRING "Environment Map" (string-append "" gimp-data-dir "/scripts/beavis.jpg") + SF-FILENAME "Environment Map" (string-append "" gimp-data-dir "/scripts/beavis.jpg") SF-COLOR "Highlight Balance" '(211 95 0) SF-COLOR "Chrome Balance" '(0 0 0)) diff --git a/plug-ins/xbm/.cvsignore b/plug-ins/xbm/.cvsignore new file mode 100644 index 0000000000..ae9e517e77 --- /dev/null +++ b/plug-ins/xbm/.cvsignore @@ -0,0 +1,6 @@ +Makefile.in +Makefile +.deps +_libs +.libs +xbm diff --git a/plug-ins/xbm/Makefile.am b/plug-ins/xbm/Makefile.am new file mode 100644 index 0000000000..a2192a077d --- /dev/null +++ b/plug-ins/xbm/Makefile.am @@ -0,0 +1,36 @@ +## Process this file with automake to produce Makefile.in + +pluginlibdir = $(gimpplugindir)/plug-ins + +pluginlib_PROGRAMS = xbm + +xbm_SOURCES = \ + xbm.c + +INCLUDES = \ + -I$(top_srcdir) \ + $(GTK_CFLAGS) \ + -I$(includedir) + +LDADD = \ + $(top_builddir)/libgimp/libgimp.la \ + $(GTK_LIBS) \ + @LIBJPEG_LIB@ + +DEPS = \ + $(top_builddir)/libgimp/libgimp.la + +xbm_DEPENDENCIES = $(DEPS) + +.PHONY: files + +files: + @files=`ls $(DISTFILES) 2> /dev/null`; for p in $$files; do \ + echo $$p; \ + done + @for subdir in $(SUBDIRS); do \ + files=`cd $$subdir; $(MAKE) files | grep -v "make\[[1-9]\]"`; \ + for file in $$files; do \ + echo $$subdir/$$file; \ + done; \ + done diff --git a/plug-ins/xbm/xbm.c b/plug-ins/xbm/xbm.c new file mode 100644 index 0000000000..877a33814c --- /dev/null +++ b/plug-ins/xbm/xbm.c @@ -0,0 +1,1092 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * X10 and X11 bitmap (XBM) loading and saving file filter for the GIMP. + * XBM code Copyright (C) 1998 Gordon Matzigkeit + * + * The XBM reading and writing code was written from scratch by Gordon + * Matzigkeit based on the XReadBitmapFile(3X11) manual + * page distributed with X11R6 and by staring at valid XBM files. It + * does not contain any code written for other XBM file loaders. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Release 1.0, 1998-02-04, Gordon Matzigkeit : + * - Load and save X10 and X11 bitmaps. + * - Allow the user to specify the C identifier prefix. + * + * TODO: + * - Parsing is very tolerant, and the algorithms are quite hairy, so + * load_image should be carefully tested to make sure there are no XBM's + * that fail. + * - Allow the user to specify a hotspot, and preserve it when loading. + */ + +/* Set this for debugging. */ +/* #define VERBOSE 2 */ + +#include +#include +#include +#include +#include +#include "libgimp/gimp.h" + +/* Wear your GIMP with pride! */ +#define DEFAULT_USE_COMMENT TRUE +#define DEFAULT_COMMENT "Made with GIMP" +#define MAX_COMMENT 72 + +/* C identifier prefix. */ +#define DEFAULT_PREFIX "bitmap" +#define MAX_PREFIX 24 + +/* Whether or not to save as X10 bitmap. */ +#define DEFAULT_X10_FORMAT FALSE + +typedef struct _XBMSaveVals +{ + gchar comment[MAX_COMMENT + 1]; + gint x10_format; + gint x_hot; + gint y_hot; + gchar prefix[MAX_PREFIX + 1]; +} XBMSaveVals; + +static XBMSaveVals xsvals = +{ + DEFAULT_COMMENT, /* comment */ + DEFAULT_X10_FORMAT, /* x10_format */ + -1, /* x_hot */ + -1, /* y_hot */ + DEFAULT_PREFIX, /* prefix */ +}; + + +typedef struct _XBMSaveInterface +{ + gint run; +} XBMSaveInterface; + +static XBMSaveInterface xsint = +{ + FALSE /* run */ +}; + + +/* Declare some local functions. + */ +static void query (void); +static void run (char *name, + int nparams, + GParam *param, + int *nreturn_vals, + GParam **return_vals); +static gint32 load_image (char *filename); +static gint save_image (char *filename, + gint32 image_ID, + gint32 drawable_ID); +static gint save_dialog (gint32 drawable_ID); +static void close_callback (GtkWidget *widget, + gpointer data); +static void save_ok_callback (GtkWidget *widget, + gpointer data); +static void save_toggle_update (GtkWidget *widget, + gpointer data); +static void comment_entry_callback (GtkWidget *widget, + gpointer data); +static void prefix_entry_callback(GtkWidget *widget, + gpointer data); + + +GPlugInInfo PLUG_IN_INFO = +{ + NULL, /* init_proc */ + NULL, /* quit_proc */ + query, /* query_proc */ + run, /* run_proc */ +}; + +MAIN (); + +#ifdef VERBOSE +static int verbose = VERBOSE; +#endif + + +static void +query () +{ + static GParamDef load_args[] = + { + { PARAM_INT32, "run_mode", "Interactive, non-interactive" }, + { PARAM_STRING, "filename", "The name of the file to load" }, + { PARAM_STRING, "raw_filename", "The name entered" }, + }; + static GParamDef load_return_vals[] = + { + { PARAM_IMAGE, "image", "Output image" }, + }; + static int nload_args = sizeof (load_args) / sizeof (load_args[0]); + static int nload_return_vals = sizeof (load_return_vals) / sizeof (load_return_vals[0]); + + + static GParamDef save_args[] = + { + { PARAM_INT32, "run_mode", "Interactive, non-interactive" }, + { PARAM_IMAGE, "image", "Input image" }, + { PARAM_DRAWABLE, "drawable", "Drawable to save" }, + { PARAM_STRING, "filename", "The name of the file to save" }, + { PARAM_STRING, "raw_filename", "The name entered" }, + { PARAM_STRING, "comment", "Image description (maximum 72 bytes)" }, + { PARAM_INT32, "x10", "Save in X10 format" }, + { PARAM_INT32, "x_hot", "X coordinate of hotspot" }, + { PARAM_INT32, "y_hot", "Y coordinate of hotspot" }, + { PARAM_STRING, "prefix", "Identifier prefix [determined from filename]"}, + } ; + static int nsave_args = sizeof (save_args) / sizeof (save_args[0]); + + gimp_install_procedure ("file_xbm_load", + "Load a file in X10 or X11 bitmap (XBM) file format", + "Load a file in X10 or X11 bitmap (XBM) file format. XBM is a lossless format for flat black-and-white (two color indexed) images.", + "Gordon Matzigkeit", + "Gordon Matzigkeit", + "1998", + "/XBM", + NULL, + PROC_PLUG_IN, + nload_args, nload_return_vals, + load_args, load_return_vals); + + + gimp_install_procedure ("file_xbm_save", + "Save a file in X10 or X11 bitmap (XBM) file format", + "Save a file in X10 or X11 bitmap (XBM) file format. XBM is a lossless format for flat black-and-white (two color indexed) images.", + "Gordon Matzigkeit", + "Gordon Matzigkeit", + "1998", + "/XBM", + "INDEXED", + PROC_PLUG_IN, + nsave_args, 0, + save_args, NULL); + + gimp_register_load_handler ("file_xbm_load", "xbm,icon,bitmap", ""); + gimp_register_save_handler ("file_xbm_save", "xbm,icon,bitmap", ""); +} + + +static void +init_prefix (char *filename) +{ + char *p, *prefix; + int len; + + /* Mangle the filename to get the prefix. */ + prefix = strrchr (filename, '/'); + if (prefix) + prefix ++; + else + prefix = filename; + + /* Strip any extension. */ + p = strrchr (prefix, '.'); + if (p && p != prefix) + len = MIN (MAX_PREFIX, p - prefix); + else + len = MAX_PREFIX; + + memset (xsvals.prefix, 0, sizeof (xsvals.prefix)); + strncpy (xsvals.prefix, prefix, len); +} + + +static void +run (char *name, + int nparams, + GParam *param, + int *nreturn_vals, + GParam **return_vals) +{ + static GParam values[2]; + GStatusType status = STATUS_SUCCESS; + GRunModeType run_mode; + gint32 image_ID; + + run_mode = param[0].data.d_int32; + + *nreturn_vals = 1; + *return_vals = values; + values[0].type = PARAM_STATUS; + values[0].data.d_status = STATUS_CALLING_ERROR; + +#ifdef VERBOSE + if (verbose) + printf ("XBM: RUN %s\n", name); +#endif + + if (strcmp (name, "file_xbm_load") == 0) + { + image_ID = load_image (param[1].data.d_string); + + if (image_ID != -1) + { + *nreturn_vals = 2; + values[0].data.d_status = STATUS_SUCCESS; + values[1].type = PARAM_IMAGE; + values[1].data.d_image = image_ID; + } + else + { + values[0].data.d_status = STATUS_EXECUTION_ERROR; + } + } + else if (strcmp (name, "file_xbm_save") == 0) + { + int argc; + char **argv; + + argc = 1; + argv = g_new (gchar *, 1); + argv[0] = g_strdup ("xbm"); + + switch (run_mode) + { + case RUN_INTERACTIVE: + /* Possibly retrieve data */ + gimp_get_data ("file_xbm_save", &xsvals); + + /* Always override the prefix with the filename. */ + init_prefix (param[3].data.d_string); + + /* First acquire information with a dialog */ + if (! save_dialog (param[2].data.d_int32)) + return; + + break; + + case RUN_NONINTERACTIVE: + /* Make sure all the required arguments are there! */ + if (nparams < 5) + status = STATUS_CALLING_ERROR; + if (status == STATUS_SUCCESS) + { + int i = 5; + + if (nparams > i) + { + memset (xsvals.comment, 0, sizeof (xsvals.comment)); + strncpy (xsvals.comment, param[i].data.d_string, + MAX_COMMENT); + } + + i ++; + if (nparams > i) + xsvals.x10_format = (param[i].data.d_int32) ? TRUE : FALSE; + + i += 2; + if (nparams > i) + { + /* They've asked for a hotspot. */ + xsvals.x_hot = param[i - 1].data.d_int32; + xsvals.y_hot = param[i].data.d_int32; + } + + i ++; + if (nparams > i) + { + memset (xsvals.prefix, 0, sizeof (xsvals.prefix)); + strncpy (xsvals.prefix, param[i].data.d_string, + MAX_PREFIX); + } + else + init_prefix (param[3].data.d_string); + + i ++; + /* Too many arguments. */ + if (nparams > i) + status = STATUS_CALLING_ERROR; + } + break; + + case RUN_WITH_LAST_VALS: + /* Possibly retrieve data */ + gimp_get_data ("file_xbm_save", &xsvals); + break; + + default: + break; + } + + *nreturn_vals = 1; + if (save_image (param[3].data.d_string, param[1].data.d_int32, + param[2].data.d_int32)) + { + /* Store xsvals data */ + gimp_set_data ("file_xbm_save", &xsvals, sizeof (xsvals)); + values[0].data.d_status = STATUS_SUCCESS; + } + else + values[0].data.d_status = STATUS_EXECUTION_ERROR; + } +} + + + +/* Return the value of a digit. */ +static gint +getval (int c, int base) +{ + static guchar *digits = "0123456789abcdefABCDEF"; + int val; + + /* Include uppercase hex digits. */ + if (base == 16) + base = 22; + + /* Find a match. */ + for (val = 0; val < base; val ++) + if (c == digits[val]) + return (val < 16) ? val : (val - 6); + return -1; +} + + +/* Same as fgetc, but skip C-style comments and insert whitespace. */ +static gint +cpp_fgetc (FILE *fp) +{ + int comment, c; + + /* FIXME: insert whitespace as advertised. */ + comment = 0; + do + { + c = fgetc (fp); + if (comment) + { + if (c == '*') + /* In a comment, with potential to leave. */ + comment = 1; + else if (comment == 1 && c == '/') + /* Leaving a comment. */ + comment = 0; + else + /* In a comment, with no potential to leave. */ + comment = 2; + } + else + { + /* Not in a comment. */ + if (c == '/') + { + /* Potential to enter a comment. */ + c = fgetc (fp); + if (c == '*') + /* Entered a comment, with no potential to leave. */ + comment = 2; + else + { + /* Just a slash in the open. */ + ungetc (c, fp); + c = '/'; + } + } + } + } + while (comment && c != EOF); + return c; +} + + +/* Match a string with a file. */ +static gint +match (FILE *fp, char *s) +{ + int c; + + do + { + c = fgetc (fp); + if (c == *s) + s ++; + else + break; + } + while (c != EOF && *s); + + if (!*s) + return TRUE; + + if (c != EOF) + ungetc (c, fp); + return FALSE; +} + + +/* Read the next integer from the file, skipping all non-integers. */ +static gint +get_int (FILE *fp) +{ + int digval, base, val, c; + + do + c = cpp_fgetc (fp); + while (c != EOF && !isdigit (c)); + + if (c == EOF) + return 0; + + /* Check for the base. */ + if (c == '0') + { + c = fgetc (fp); + if (c == 'x' || c == 'X') + { + c = fgetc (fp); + base = 16; + } + else if (isdigit (c)) + base = 8; + else + { + ungetc (c, fp); + return 0; + } + } + else + base = 10; + + val = 0; + for (;;) + { + digval = getval (c, base); + if (digval == -1) + { + ungetc (c, fp); + break; + } + val *= base; + val += digval; + c = fgetc (fp); + } + + return val; +} + + +static gint +load_image (char *filename) +{ + FILE *fp; + gint32 image_ID, layer_ID; + + GPixelRgn pixel_rgn; + GDrawable *drawable; + guchar *data; + int width, height, intbits; + int c, i, j, k; + int tileheight, rowoffset; + + char *name_buf; + + guchar cmap[] = + { + 0x00, 0x00, 0x00, /* black */ + 0xff, 0xff, 0xff /* white */ + }; + + fp = fopen (filename, "rb"); + if (!fp) + { + printf ("XBM: cannot open \"%s\"\n", filename); + return -1; + } + + name_buf = g_malloc (strlen (filename) + 11); + sprintf (name_buf, "Loading %s:", filename); + gimp_progress_init (name_buf); + g_free (name_buf); + + /* Loosely parse the header */ + intbits = height = width = 0; + c = ' '; + do + { + + if (isspace (c)) + { + if (match (fp, "char")) + { + c = fgetc (fp); + if (isspace (c)) + { + intbits = 8; + continue; + } + } + else if (match (fp, "short")) + { + c = fgetc (fp); + if (isspace (c)) + { + intbits = 16; + continue; + } + } + } + + if (c == '_') + { + if (match (fp, "width")) + { + c = fgetc (fp); + if (isspace (c)) + { + width = get_int (fp); + continue; + } + } + else if (match (fp, "height")) + { + c = fgetc (fp); + if (isspace (c)) + { + height = get_int (fp); + continue; + } + } + } + + c = cpp_fgetc (fp); + } + while (c != '{' && c != EOF); + + if (c == EOF) + { + printf ("XBM: cannot read header (ftell == %ld)\n", ftell (fp)); + return -1; + } + + if (width == 0) + { + printf ("XBM: no image width specified\n"); + return -1; + } + + if (height == 0) + { + printf ("XBM: no image height specified\n"); + return -1; + } + + if (intbits == 0) + { + printf ("XBM: no image data type specified\n"); + return -1; + } + + image_ID = gimp_image_new (width, height, INDEXED); + gimp_image_set_filename (image_ID, filename); + + /* Set a black-and-white colormap. */ + gimp_image_set_cmap (image_ID, cmap, 2); + + layer_ID = gimp_layer_new (image_ID, + "Background", + width, height, + INDEXED_IMAGE, + 100, + NORMAL_MODE); + gimp_image_add_layer (image_ID, layer_ID, 0); + + drawable = gimp_drawable_get (layer_ID); + + /* Prepare the pixel region. */ + gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, width, height, TRUE, FALSE); + + /* Allocate the data. */ + tileheight = gimp_tile_height (); + data = (guchar *) g_malloc (width * tileheight); + + for (i = 0; i < height; i += tileheight) + { + tileheight = MIN (tileheight, height - i); + +#ifdef VERBOSE + if (verbose > 1) + printf ("XBM: reading %dx(%d+%d) pixel region\n", width, i, + tileheight); +#endif + + /* Parse the data from the file */ + for (j = 0; j < tileheight; j ++) + { + /* Read each row. */ + rowoffset = j * width; + for (k = 0; k < width; k ++) + { + /* Expand each integer into INTBITS pixels. */ + if (k % intbits == 0) + { + c = get_int (fp); + + /* Flip all the bits so that 1's become black and + 0's become white. */ + c ^= 0xffff; + } + + data[rowoffset + k] = c & 1; + c >>= 1; + } + } + + /* Put the data into the image. */ + gimp_progress_update ((double) (i + tileheight) / (double) height); + gimp_pixel_rgn_set_rect (&pixel_rgn, data, 0, i, width, tileheight); + } + + g_free (data); + + gimp_drawable_flush (drawable); + gimp_drawable_detach (drawable); + + fclose (fp); + return image_ID; +} + + +static void +close_callback (GtkWidget *widget, + gpointer data) +{ + gtk_main_quit (); +} + +static int gtk_initialized = FALSE; + + +static void +not_bw_dialog (void) +{ + GtkWidget *dlg, *button, *label, *frame, *vbox; + + if (!gtk_initialized) + { + printf ("XBM: can only save two color indexed images\n"); + return; + } + + dlg = gtk_dialog_new (); + gtk_window_set_title (GTK_WINDOW (dlg), "XBM Warning"); + gtk_window_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE); + gtk_signal_connect (GTK_OBJECT (dlg), "destroy", + (GtkSignalFunc) close_callback, + dlg); + gtk_signal_connect (GTK_OBJECT (dlg), "delete_event", + (GtkSignalFunc) close_callback, + dlg); + + /* Action area */ + button = gtk_button_new_with_label ("Cancel"); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_signal_connect_object (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) gtk_widget_destroy, + GTK_OBJECT (dlg)); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, + TRUE, TRUE, 0); + gtk_widget_show (button); + + /* the warning message */ + frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); + gtk_container_border_width (GTK_CONTAINER (frame), 10); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, + TRUE, 0); + vbox = gtk_vbox_new (FALSE, 5); + gtk_container_border_width (GTK_CONTAINER (vbox), 5); + gtk_container_add (GTK_CONTAINER (frame), vbox); + + label = gtk_label_new ( + "The image which you are trying to save as\n" + "an XBM contains more than two colors.\n\n" + "Please convert it to a black and white\n" + "(1-bit) indexed image and try again." + ); + gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0); + gtk_widget_show (label); + gtk_widget_show (vbox); + gtk_widget_show (frame); + gtk_widget_show (dlg); + gtk_main (); + gtk_widget_destroy (GTK_WIDGET (dlg)); + gdk_flush (); +} + + +static gint +save_image (char *filename, + gint32 image_ID, + gint32 drawable_ID) +{ + GDrawable *drawable; + GPixelRgn pixel_rgn; + FILE *fp; + + int width, height, colors, dark; + int intbits, lineints, need_comma, nints, rowoffset, tileheight; + int c, i, j, k, thisbit; + + guchar *data, *cmap, *name_buf; + char *prefix, *p, *intfmt; + + drawable = gimp_drawable_get (drawable_ID); + width = drawable->width; + height = drawable->height; + cmap = gimp_image_get_cmap (image_ID, &colors); + +#if 0 + printf ("XBM: run `gdb xbm' and `attach %d'\n", getpid ()); + kill (getpid (), 19); +#endif + + if (gimp_drawable_type (drawable_ID) != INDEXED_IMAGE || colors > 2) + { + /* The image is not black-and-white. */ + not_bw_dialog (); + return FALSE; + } + + name_buf = (guchar *) g_malloc (strlen (filename) + 11); + sprintf (name_buf, "Saving %s:", filename); + gimp_progress_init (name_buf); + g_free (name_buf); + + /* Figure out which color is black, and which is white. */ + dark = 0; + if (colors > 1) + { + int first, second; + + /* Maybe the second color is darker than the first. */ + first = (cmap[0] * cmap[0]) + (cmap[1] * cmap[1]) + (cmap[2] * cmap[2]); + second = (cmap[3] * cmap[3]) + (cmap[4] * cmap[4]) + (cmap[5] * cmap[5]); + + if (second < first) + dark = 1; + } + + /* Now actually save the data. */ + fp = fopen (filename, "w"); + if (!fp) + { + printf ("XBM: cannot create \"%s\"\n", filename); + return FALSE; + } + + /* Maybe write the image comment. */ + if (*xsvals.comment) + fprintf (fp, "/* %s */\n", xsvals.comment); + + /* Change any non-alphanumeric prefix characters to underscores. */ + prefix = xsvals.prefix; + p = prefix; + while (*p) + { + if (!isalnum (*p)) + *p = '_'; + p ++; + } + + /* Write out the image height and width. */ + fprintf (fp, "#define %s_width %d\n", prefix, width); + fprintf (fp, "#define %s_height %d\n", prefix, height); + + /* Write out the hotspot, if any. */ + if (xsvals.x_hot >= 0 && xsvals.y_hot >= 0) + { + fprintf (fp, "#define %s_x_hot %d\n", prefix, xsvals.x_hot); + fprintf (fp, "#define %s_y_hot %d\n", prefix, xsvals.y_hot); + } + + /* Now write the actual data. */ + if (xsvals.x10_format) + { + /* We can fit 9 hex shorts on a single line. */ + lineints = 9; + intbits = 16; + intfmt = " 0x%04x"; + } + else + { + /* We can fit 12 hex chars on a single line. */ + lineints = 12; + intbits = 8; + intfmt = " 0x%02x"; + } + + fprintf (fp, "static %s %s_bits[] = {\n ", + xsvals.x10_format ? "short" : "char", prefix); + + /* Allocate a new set of pixels. */ + tileheight = gimp_tile_height (); + data = (guchar *) g_malloc(width * tileheight); + + gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, width, height, + FALSE, FALSE); + + /* Write out the integers. */ + need_comma = 0; + nints = 0; + for (i = 0; i < height; i += tileheight) + { + /* Get a horizontal slice of the image. */ + tileheight = MIN (tileheight, height - i); + gimp_pixel_rgn_get_rect (&pixel_rgn, data, 0, i, width, tileheight); + +#ifdef VERBOSE + if (verbose > 1) + printf ("TGA: writing %dx(%d+%d) pixel region\n", + width, i, tileheight); +#endif + + for (j = 0; j < tileheight; j ++) + { + /* Write out a row at a time. */ + rowoffset = j * width; + c = 0; + thisbit = 0; + + for (k = 0; k < width; k ++) + { + if (k != 0 && thisbit == intbits) + { + /* Output a completed integer. */ + if (need_comma) + fputc (',', fp); + need_comma = 1; + + /* Maybe start a new line. */ + if (nints ++ >= lineints) + { + nints = 1; + fputs ("\n ", fp); + } + fprintf (fp, intfmt, c); + + /* Start a new integer. */ + c = 0; + thisbit = 0; + } + + /* Pack INTBITS pixels into an integer. */ + c |= ((data[rowoffset + k] == dark) ? 1 : 0) << (thisbit ++); + } + + if (thisbit != 0) + { + /* Write out the last oddball int. */ + if (need_comma) + fputc (',', fp); + need_comma = 1; + + /* Maybe start a new line. */ + if (nints ++ == lineints) + { + nints = 1; + fputs ("\n ", fp); + } + fprintf (fp, intfmt, c); + } + } + + gimp_progress_update ((double) (i + tileheight) / (double) height); + } + + /* Write the trailer. */ + fprintf (fp, " };\n"); + fclose (fp); + return TRUE; +} + + +static gint +save_dialog (gint32 drawable_ID) +{ + GtkWidget *dlg, *button, *toggle, *label, *entry, *frame, *hbox, *vbox; + gchar **argv; + gint argc; + + xsint.run = FALSE; + + argc = 1; + argv = g_new (gchar *, 1); + argv[0] = g_strdup ("save"); + + gtk_init (&argc, &argv); + gtk_initialized = TRUE; + + gtk_rc_parse (gimp_gtkrc ()); + gdk_set_use_xshm(gimp_use_xshm()); + + dlg = gtk_dialog_new (); + gtk_window_set_title (GTK_WINDOW (dlg), "Save as XBM"); + gtk_window_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE); + gtk_signal_connect (GTK_OBJECT (dlg), "destroy", + (GtkSignalFunc) close_callback, + NULL); + gtk_signal_connect (GTK_OBJECT (dlg), "delete_event", + (GtkSignalFunc) close_callback, + dlg); + + /* Action area */ + button = gtk_button_new_with_label ("OK"); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) save_ok_callback, + dlg); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, + TRUE, TRUE, 0); + gtk_widget_grab_default (button); + gtk_widget_show (button); + + button = gtk_button_new_with_label ("Cancel"); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_signal_connect_object (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) gtk_widget_destroy, + GTK_OBJECT (dlg)); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, + TRUE, TRUE, 0); + gtk_widget_show (button); + + + /* parameter settings */ + frame = gtk_frame_new ("XBM Options"); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); + gtk_container_border_width (GTK_CONTAINER (frame), 10); + gtk_widget_show (frame); + + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0); + vbox = gtk_vbox_new (FALSE, 5); + gtk_container_border_width (GTK_CONTAINER (vbox), 5); + gtk_container_add (GTK_CONTAINER (frame), vbox); + + /* comment string. */ + hbox = gtk_hbox_new(FALSE, 5); + gtk_container_border_width (GTK_CONTAINER (hbox), 0); + gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0); + + label = gtk_label_new ("Description: "); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + entry = gtk_entry_new_with_max_length (MAX_COMMENT); + gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0); + gtk_widget_set_usize (entry, 240, 0); + gtk_entry_set_text (GTK_ENTRY (entry), xsvals.comment); + gtk_signal_connect (GTK_OBJECT (entry), "changed", + (GtkSignalFunc) comment_entry_callback, + NULL); + gtk_widget_show (entry); + + gtk_widget_show (hbox); + + /* X10 format */ + toggle = gtk_check_button_new_with_label ("X10 format bitmap"); + gtk_box_pack_start (GTK_BOX (vbox), toggle, TRUE, TRUE, 0); + gtk_signal_connect (GTK_OBJECT (toggle), "toggled", + (GtkSignalFunc) save_toggle_update, + &xsvals.x10_format); + gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (toggle), xsvals.x10_format); + gtk_widget_show (toggle); + + /* prefix */ + hbox = gtk_hbox_new(FALSE, 5); + gtk_container_border_width (GTK_CONTAINER (hbox), 0); + gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0); + + label = gtk_label_new ("Identifier prefix: "); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + entry = gtk_entry_new_with_max_length (MAX_PREFIX); + gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0); + gtk_entry_set_text (GTK_ENTRY (entry), xsvals.prefix); + gtk_signal_connect (GTK_OBJECT (entry), "changed", + (GtkSignalFunc) prefix_entry_callback, + NULL); + gtk_widget_show (entry); + + gtk_widget_show (hbox); + + /* Done. */ + gtk_widget_show (vbox); + gtk_widget_show (dlg); + + gtk_main (); + gdk_flush (); + + return xsint.run; +} + + +/* Update the comment string. */ +static void +comment_entry_callback (GtkWidget *widget, + gpointer data) +{ + memset (xsvals.comment, 0, sizeof (xsvals.comment)); + strncpy (xsvals.comment, + gtk_entry_get_text (GTK_ENTRY (widget)), MAX_COMMENT); +} + + +static void +prefix_entry_callback(GtkWidget *widget, + gpointer data) +{ + memset (xsvals.prefix, 0, sizeof (xsvals.prefix)); + strncpy (xsvals.prefix, gtk_entry_get_text (GTK_ENTRY (widget)), MAX_PREFIX); +} + + +static void +save_ok_callback (GtkWidget *widget, + gpointer data) +{ + xsint.run = TRUE; + gtk_widget_destroy (GTK_WIDGET (data)); +} + + +static void +save_toggle_update (GtkWidget *widget, + gpointer data) +{ + int *toggle_val; + + toggle_val = (int *) data; + + if (GTK_TOGGLE_BUTTON (widget)->active) + *toggle_val = TRUE; + else + *toggle_val = FALSE; +} + +/* +Local Variables: +compile-command:"gcc -Wall -Wmissing-prototypes -g -O -o xbm xbm.c -lgimp -lgtk -lgdk -lglib -lm" +End: +*/ diff --git a/po/Makefile.in.in b/po/Makefile.in.in index c25fea49b0..b22c8b028c 100644 --- a/po/Makefile.in.in +++ b/po/Makefile.in.in @@ -19,7 +19,7 @@ VPATH = @srcdir@ prefix = @prefix@ exec_prefix = @exec_prefix@ datadir = $(prefix)/@DATADIRNAME@ -localedir = $(datadir)/locale +localedir = @localedir@ gnulocaledir = $(prefix)/share/locale gettextsrcdir = $(prefix)/share/gettext/po subdir = po