/*********************************************************************************/ /* 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 /************/ /* 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"); gdk_set_debug_level(0); gdk_set_show_events(0); gdk_set_use_xshm(gimp_use_xshm()); gtk_init (&argc, &argv); /* 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"); }