diff --git a/ChangeLog b/ChangeLog index 2f8b448795..e3c980c5cb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +Wed Dec 10 16:59:56 PST 1997 Manish Singh + + * Added gimprc option for enabling/disabling tooltips + + * Fixed up about dialog + + * Added Fractaltrace plugin + Tue Dec 9 15:26:48 PST 1997 Manish Singh * Added Lauri Alanko's tools patch diff --git a/app/about_dialog.c b/app/about_dialog.c index 1c85513911..6ed56b2e37 100644 --- a/app/about_dialog.c +++ b/app/about_dialog.c @@ -393,7 +393,7 @@ about_dialog_timer (gpointer data) { for (i = 0, k = 0; i < dissolve_height; i++) for (j = 0; j < dissolve_width; j++, k++) - if (frame >= dissolve_map[k]) + if (frame == dissolve_map[k]) { gdk_draw_pixmap (logo_area->window, logo_area->style->black_gc, diff --git a/app/dialogs/about-dialog.c b/app/dialogs/about-dialog.c index 1c85513911..6ed56b2e37 100644 --- a/app/dialogs/about-dialog.c +++ b/app/dialogs/about-dialog.c @@ -393,7 +393,7 @@ about_dialog_timer (gpointer data) { for (i = 0, k = 0; i < dissolve_height; i++) for (j = 0; j < dissolve_width; j++, k++) - if (frame >= dissolve_map[k]) + if (frame == dissolve_map[k]) { gdk_draw_pixmap (logo_area->window, logo_area->style->black_gc, diff --git a/app/display/gimpdisplayshell-draw.c b/app/display/gimpdisplayshell-draw.c index 9c1b0d5726..7a20bc837e 100644 --- a/app/display/gimpdisplayshell-draw.c +++ b/app/display/gimpdisplayshell-draw.c @@ -525,6 +525,8 @@ create_toolbox () gtk_tooltips_set_colors (tool_tips, &colors[11], &main_vbox->style->fg[GTK_STATE_NORMAL]); + if (!show_tool_tips) + gtk_tooltips_disable (tool_tips); /* Build the menu bar with menus */ menus_get_toolbox_menubar (&menubar, &table); diff --git a/app/display/gimpdisplayshell.c b/app/display/gimpdisplayshell.c index 9c1b0d5726..7a20bc837e 100644 --- a/app/display/gimpdisplayshell.c +++ b/app/display/gimpdisplayshell.c @@ -525,6 +525,8 @@ create_toolbox () gtk_tooltips_set_colors (tool_tips, &colors[11], &main_vbox->style->fg[GTK_STATE_NORMAL]); + if (!show_tool_tips) + gtk_tooltips_disable (tool_tips); /* Build the menu bar with menus */ menus_get_toolbox_menubar (&menubar, &table); diff --git a/app/gimprc.c b/app/gimprc.c index 7515aaa315..9974c9dd79 100644 --- a/app/gimprc.c +++ b/app/gimprc.c @@ -111,6 +111,7 @@ int default_height = 256; int default_type = RGB; int show_tips = TRUE; int last_tip = -1; +int show_tool_tips = FALSE; static int get_next_token (void); static int peek_next_token (void); @@ -213,6 +214,7 @@ static ParseFunc funcs[] = { "plug-in", TT_XPLUGIN, NULL, NULL }, { "plug-in-def", TT_XPLUGINDEF, NULL, NULL }, { "menu-path", TT_XMENUPATH, NULL, NULL }, + { "show-tool-tips", TT_BOOLEAN, &show_tool_tips, NULL }, }; static int nfuncs = sizeof (funcs) / sizeof (funcs[0]); diff --git a/app/gimprc.h b/app/gimprc.h index b974d05a4b..ab39bd6ec3 100644 --- a/app/gimprc.h +++ b/app/gimprc.h @@ -61,6 +61,7 @@ extern int default_width, default_height; extern int default_type; extern int show_tips; extern int last_tip; +extern int show_tool_tips; /* function prototypes */ char * gimp_directory (void); diff --git a/app/gui/about-dialog.c b/app/gui/about-dialog.c index 1c85513911..6ed56b2e37 100644 --- a/app/gui/about-dialog.c +++ b/app/gui/about-dialog.c @@ -393,7 +393,7 @@ about_dialog_timer (gpointer data) { for (i = 0, k = 0; i < dissolve_height; i++) for (j = 0; j < dissolve_width; j++, k++) - if (frame >= dissolve_map[k]) + if (frame == dissolve_map[k]) { gdk_draw_pixmap (logo_area->window, logo_area->style->black_gc, diff --git a/app/interface.c b/app/interface.c index 9c1b0d5726..7a20bc837e 100644 --- a/app/interface.c +++ b/app/interface.c @@ -525,6 +525,8 @@ create_toolbox () gtk_tooltips_set_colors (tool_tips, &colors[11], &main_vbox->style->fg[GTK_STATE_NORMAL]); + if (!show_tool_tips) + gtk_tooltips_disable (tool_tips); /* Build the menu bar with menus */ menus_get_toolbox_menubar (&menubar, &table); diff --git a/configure.in b/configure.in index 64e94bc0d2..3363dac6a2 100644 --- a/configure.in +++ b/configure.in @@ -421,6 +421,7 @@ plug-ins/glasstile/Makefile plug-ins/colorify/Makefile plug-ins/papertile/Makefile plug-ins/illusion/Makefile +plug-ins/fractaltrace/Makefile app/Makefile docs/Makefile ) diff --git a/etc/gimprc.in b/etc/gimprc.in index 61dbdd7028..92e49e2a49 100644 --- a/etc/gimprc.in +++ b/etc/gimprc.in @@ -137,6 +137,10 @@ # #: #x# (preview-size small) +# Tooltips +# Comment this out to disable the tooltips in the toolbox +(show-tool-tips) + # Controlling ruler visibility # The default behavior is for rulers to be ON # This can also be toggled with the View->Show Rulers command or shift+control+r diff --git a/gimprc.in b/gimprc.in index 61dbdd7028..92e49e2a49 100644 --- a/gimprc.in +++ b/gimprc.in @@ -137,6 +137,10 @@ # #: #x# (preview-size small) +# Tooltips +# Comment this out to disable the tooltips in the toolbox +(show-tool-tips) + # Controlling ruler visibility # The default behavior is for rulers to be ON # This can also be toggled with the View->Show Rulers command or shift+control+r diff --git a/plug-ins/Makefile.am b/plug-ins/Makefile.am index d73fe5a2b3..b2bba8408e 100644 --- a/plug-ins/Makefile.am +++ b/plug-ins/Makefile.am @@ -63,6 +63,7 @@ SUBDIRS = \ film \ fits \ fp \ + fractaltrace \ gauss_iir \ gauss_rle \ gbr \ diff --git a/plug-ins/common/fractaltrace.c b/plug-ins/common/fractaltrace.c new file mode 100644 index 0000000000..3843ee48fd --- /dev/null +++ b/plug-ins/common/fractaltrace.c @@ -0,0 +1,904 @@ +/******************************************************************************* + + fractaltrace.c -- This is a plug-in for the GIMP 1.0 + + Copyright (C) 1997 Hirotsuna Mizuno + s1041150@u-aizu.ac.jp + + 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. + +*******************************************************************************/ + +#define PLUG_IN_NAME "plug_in_fractal_trace" +#define PLUG_IN_TITLE "Fractal Trace" +#define PLUG_IN_VERSION "v0.4 test version (Dec. 25 1997)" +#define PLUG_IN_CATEGORY "/Filters/Distorts/Fractal Trace" + +/******************************************************************************/ + +#include +#include +#include +#include + +#ifndef PI_2 +#define PI_2 (3.14159265358979323*2.0) +#endif + +/******************************************************************************/ + +static void query( void ); +static void run( char*, int, GParam*, int*, GParam** ); +static void filter( GDrawable* ); + +static void pixels_init( GDrawable* ); +static void pixels_free( void ); + +static int dialog_show( void ); +static void dialog_preview_draw( void ); + +/******************************************************************************/ + +GPlugInInfo PLUG_IN_INFO = { + NULL, /* init_proc */ + NULL, /* quit_proc */ + query, /* query_proc */ + run /* run_proc */ +}; + +MAIN(); + +/******************************************************************************/ + +typedef struct { + gint32 wrap; + gint32 transparent; + gint32 black; + gint32 white; +} outside_type_t; + +#define OUTSIDE_TYPE_WRAP 0 +#define OUTSIDE_TYPE_TRANSPARENT 1 +#define OUTSIDE_TYPE_BLACK 2 +#define OUTSIDE_TYPE_WHITE 3 + +static outside_type_t outside_type = { + OUTSIDE_TYPE_WRAP, + OUTSIDE_TYPE_TRANSPARENT, + OUTSIDE_TYPE_BLACK, + OUTSIDE_TYPE_WHITE +}; + +typedef struct { + gdouble x1; + gdouble x2; + gdouble y1; + gdouble y2; + gint32 depth; + gint32 outside_type; +} parameter_t; + +static parameter_t parameters = { + -1.0, + +0.5, + -1.0, + +1.0, + 3, + OUTSIDE_TYPE_WRAP +}; + +/******************************************************************************/ + +static void query( void ) +{ + static GParamDef args[] = { + { PARAM_INT32, "run_mode", "interactive / non-interactive" }, + { PARAM_IMAGE, "image", "input image (not used)" }, + { PARAM_DRAWABLE, "drawable", "input drawable" }, + { PARAM_FLOAT, "xmin", "xmin fractal image delimiter" }, + { PARAM_FLOAT, "xmax", "xmax fractal image delimiter" }, + { PARAM_FLOAT, "ymin", "ymin fractal image delimiter" }, + { PARAM_FLOAT, "ymax", "ymax fractal image delimiter" }, + { PARAM_FLOAT, "ymax", "ymax fractal image delimiter" }, + { PARAM_INT32, "depth", "trace depth" }, + { PARAM_INT32, "outside_type", "outside type" + "(0=WRAP/1=TRANS/2=BLACK/3=WHITE)" }, + }; + static int nargs = sizeof( args ) / sizeof( args[0] ); + + static GParamDef *rets = NULL; + static int nrets = 0; + + gimp_install_procedure( + PLUG_IN_NAME, + "transform image with the Mandelbrot Fractal", + "transform image with the Mandelbrot Fractal", + "Hirotsuna Mizuno ", + "Copyright (C) 1997 Hirotsuna Mizuno", + PLUG_IN_VERSION, + PLUG_IN_CATEGORY, + "RGB*, GRAY*", + PROC_PLUG_IN, + nargs, + nrets, + args, + rets + ); +} + +/******************************************************************************/ + +typedef struct { + gint x1; + gint x2; + gint y1; + gint y2; + gint width; + gint height; + gdouble center_x; + gdouble center_y; +} selection_t; + +typedef struct { + gint width; + gint height; + gint bpp; + gint alpha; +} image_t; + +static selection_t selection; +static image_t image; + +/******************************************************************************/ + +static void run( char *name, int argc, GParam *args, int *retc, GParam **rets ) +{ + GDrawable *drawable; + GRunModeType run_mode; + GStatusType status; + static GParam returns[1]; + + run_mode = args[0].data.d_int32; + status = STATUS_SUCCESS; + + drawable = gimp_drawable_get( args[2].data.d_drawable ); + image.width = gimp_drawable_width( drawable->id ); + image.height = gimp_drawable_height( drawable->id ); + image.bpp = gimp_drawable_bpp( drawable->id ); + image.alpha = gimp_drawable_has_alpha( drawable->id ); + gimp_drawable_mask_bounds( drawable->id, &selection.x1, &selection.y1, + &selection.x2, &selection.y2 ); + selection.width = selection.x2 - selection.y1; + selection.height = selection.y2 - selection.y1; + selection.center_x = selection.x1 + (gdouble)selection.width / 2.0; + selection.center_y = selection.y1 + (gdouble)selection.height / 2.0; + + pixels_init( drawable ); + + if( !gimp_drawable_color( drawable->id ) && + !gimp_drawable_gray( drawable->id ) ){ + status = STATUS_EXECUTION_ERROR; + } + + switch( run_mode ){ + case RUN_WITH_LAST_VALS: + gimp_get_data( PLUG_IN_NAME, ¶meters ); + break; + case RUN_INTERACTIVE: + gimp_get_data( PLUG_IN_NAME, ¶meters ); + if( !dialog_show() ){ + status = STATUS_EXECUTION_ERROR; + break; + } + gimp_set_data( PLUG_IN_NAME, ¶meters, sizeof( parameter_t ) ); + break; + case RUN_NONINTERACTIVE: + if( argc != 9 ){ + status = STATUS_CALLING_ERROR; + } else { + parameters.x1 = args[3].data.d_float; + parameters.x2 = args[4].data.d_float; + parameters.y1 = args[5].data.d_float; + parameters.y2 = args[6].data.d_float; + parameters.depth = args[7].data.d_int32; + parameters.outside_type = args[8].data.d_int32; + } + break; + } + + if( status == STATUS_SUCCESS ){ + gimp_tile_cache_ntiles( 2 * ( drawable->width / gimp_tile_width() + 1 ) ); + filter( drawable ); + if( run_mode != RUN_NONINTERACTIVE ) gimp_displays_flush(); + } + + gimp_drawable_detach( drawable ); + + pixels_free(); + + returns[0].type = PARAM_STATUS; + returns[0].data.d_status = status; + *retc = 1; + *rets = returns; +} + +/******************************************************************************/ + +static guchar **spixels; +static guchar **dpixels; +static GPixelRgn sPR; +static GPixelRgn dPR; + +typedef struct { + guchar r; + guchar g; + guchar b; + guchar a; +} pixel_t; + +static void pixels_init( GDrawable *drawable ) +{ + gint y; + gimp_pixel_rgn_init( &sPR, drawable, + 0, 0, image.width, image.height, FALSE, FALSE ); + gimp_pixel_rgn_init( &dPR, drawable, + 0, 0, image.width, image.height, TRUE, TRUE ); + spixels = (guchar**)g_malloc( image.height * sizeof( guchar* ) ); + dpixels = (guchar**)g_malloc( image.height * sizeof( guchar* ) ); + for( y = 0; y < image.height; y++ ){ + spixels[y] = (guchar*)g_malloc( image.width * image.bpp * sizeof( guchar ) ); + dpixels[y] = (guchar*)g_malloc( image.width * image.bpp * sizeof( guchar ) ); + gimp_pixel_rgn_get_row( &sPR, spixels[y], 0, y, image.width ); + } +} + +static void pixels_free( void ) +{ + gint y; + for( y = 0; y < image.height; y++ ){ + free( spixels[y] ); + free( dpixels[y] ); + } + free( spixels ); + free( dpixels ); +} + +static void pixels_get( gint x, gint y, pixel_t *pixel ) +{ + if( x < 0 ) x = 0; else if( image.width <= x ) x = image.width - 1; + if( y < 0 ) y = 0; else if( image.height <= y ) y = image.height - 1; + + switch( image.bpp ){ + case 1: /* GRAY */ + pixel->r = spixels[y][x*image.bpp]; + pixel->g = spixels[y][x*image.bpp]; + pixel->b = spixels[y][x*image.bpp]; + pixel->a = 255; + break; + case 2: /* GRAY+A */ + pixel->r = spixels[y][x*image.bpp]; + pixel->g = spixels[y][x*image.bpp]; + pixel->b = spixels[y][x*image.bpp]; + pixel->a = spixels[y][x*image.bpp+1]; + break; + case 3: /* RGB */ + pixel->r = spixels[y][x*image.bpp]; + pixel->g = spixels[y][x*image.bpp+1]; + pixel->b = spixels[y][x*image.bpp+2]; + pixel->a = 255; + break; + case 4: /* RGB+A */ + pixel->r = spixels[y][x*image.bpp]; + pixel->g = spixels[y][x*image.bpp+1]; + pixel->b = spixels[y][x*image.bpp+2]; + pixel->a = spixels[y][x*image.bpp+3]; + break; + } +} + +static void pixels_get_biliner( gdouble x, gdouble y, pixel_t *pixel ) +{ + pixel_t A, B, C, D; + gdouble a, b, c, d; + gint x1, y1, x2, y2; + gdouble dx, dy; + + x1 = (gint)floor( x ); + x2 = x1 + 1; + y1 = (gint)floor( y ); + y2 = y1 + 1; + + dx = x - (gdouble)x1; + dy = y - (gdouble)y1; + a = ( 1.0 - dx ) * ( 1.0 - dy ); + b = dx * ( 1.0 - dy ); + c = ( 1.0 - dx ) * dy; + d = dx * dy; + + pixels_get( x1, y1, &A ); + pixels_get( x2, y1, &B ); + pixels_get( x1, y2, &C ); + pixels_get( x2, y2, &D ); + + pixel->r = (guchar)( a * (gdouble)A.r + b * (gdouble)B.r + + c * (gdouble)C.r + d * (gdouble)D.r ); + pixel->g = (guchar)( a * (gdouble)A.g + b * (gdouble)B.g + + c * (gdouble)C.g + d * (gdouble)D.g ); + pixel->b = (guchar)( a * (gdouble)A.b + b * (gdouble)B.b + + c * (gdouble)C.b + d * (gdouble)D.b ); + pixel->a = (guchar)( a * (gdouble)A.a + b * (gdouble)B.a + + c * (gdouble)C.a + d * (gdouble)D.a ); +} + +static void pixels_set( gint x, gint y, pixel_t *pixel ) +{ + switch( image.bpp ){ + case 1: /* GRAY */ + dpixels[y][x*image.bpp] = pixel->r; + break; + case 2: /* GRAY+A */ + dpixels[y][x*image.bpp] = pixel->r; + dpixels[y][x*image.bpp+1] = pixel->a; + break; + case 3: /* RGB */ + dpixels[y][x*image.bpp] = pixel->r; + dpixels[y][x*image.bpp+1] = pixel->g; + dpixels[y][x*image.bpp+2] = pixel->b; + break; + case 4: /* RGB+A */ + dpixels[y][x*image.bpp] = pixel->r; + dpixels[y][x*image.bpp+1] = pixel->g; + dpixels[y][x*image.bpp+2] = pixel->b; + dpixels[y][x*image.bpp+3] = pixel->a; + break; + } +} + +static void pixels_store( void ) +{ + gint y; + for( y = selection.y1; y < selection.y2; y++ ){ + gimp_pixel_rgn_set_row( &dPR, dpixels[y], 0, y, image.width ); + } +} + +/******************************************************************************/ + +static void mandelbrot( gdouble x, gdouble y, gdouble *u, gdouble *v ) +{ + gint iter = 0; + gdouble xx = x; + gdouble yy = y; + gdouble x2 = xx * xx; + gdouble y2 = yy * yy; + gdouble tmp; + while( iter < parameters.depth ){ + tmp = x2 - y2 + x; + yy = 2 * xx * yy + y; + xx = tmp; + x2 = xx * xx; + y2 = yy * yy; + iter++; + } + *u = xx; + *v = yy; +} + + +/******************************************************************************/ + +static void filter( GDrawable *drawable ) +{ + gint x, y; + pixel_t pixel; + gdouble scale_x, scale_y; + gdouble cx, cy; + gdouble px, py; + + gimp_progress_init( PLUG_IN_TITLE ); + + scale_x = ( parameters.x2 - parameters.x1 ) / selection.width; + scale_y = ( parameters.y2 - parameters.y1 ) / selection.height; + + for( y = selection.y1; y < selection.y2; y++ ){ + cy = parameters.y1 + ( y - selection.y1 ) * scale_y; + for( x = selection.x1; x < selection.x2; x++ ){ + cx = parameters.x1 + ( x - selection.x1 ) * scale_x; + mandelbrot( cx, cy, &px, &py ); + px = ( px - parameters.x1 ) / scale_x + selection.x1; + py = ( py - parameters.y1 ) / scale_y + selection.y1; + if( 0 <= px && px < image.width && 0 <= py && py < image.height ){ + pixels_get_biliner( px, py, &pixel ); + } else { + switch( parameters.outside_type ){ + case OUTSIDE_TYPE_WRAP: + px = fmod( px, image.width ); + py = fmod( py, image.height ); + if( px < 0.0 ) px += image.width; + if( py < 0.0 ) py += image.height; + pixels_get_biliner( px, py, &pixel ); + break; + case OUTSIDE_TYPE_TRANSPARENT: + pixel.r = pixel.g = pixel.b = 0; + pixel.a = 0; + break; + case OUTSIDE_TYPE_BLACK: + pixel.r = pixel.g = pixel.b = 0; + pixel.a = 255; + break; + case OUTSIDE_TYPE_WHITE: + pixel.r = pixel.g = pixel.b = 255; + pixel.a = 255; + break; + } + } + pixels_set( x, y, &pixel ); + } + gimp_progress_update( (gdouble)(y-selection.y1)/selection.height ); + } + + pixels_store(); + + gimp_drawable_flush( drawable ); + gimp_drawable_merge_shadow( drawable->id, TRUE ); + gimp_drawable_update( drawable->id, + selection.x1, selection.y1, selection.width, selection.height ); +} + +/* + GUI +*/ + +/******************************************************************************/ + +static int dialog_status; + +/******************************************************************************/ + +static void dialog_entry_gint32_callback( GtkWidget *widget, gpointer *data ) +{ + gint32 value = (gint32)atof( gtk_entry_get_text( GTK_ENTRY( widget ) ) ); + if( *(gint32*)data != value ){ + *(gint32*)data = value; + dialog_preview_draw(); + } +} + +static void dialog_entry_gint32_new( char *caption, gint32 *value, + GtkWidget *table, gint row ) +{ + GtkWidget *label; + GtkWidget *entry; + char buffer[256]; + + label = gtk_label_new( caption ); + gtk_table_attach_defaults( GTK_TABLE( table ), label, 0, 1, row, row + 1 ); + gtk_widget_show( label ); + + entry = gtk_entry_new(); + sprintf( buffer, "%d", *value ); + gtk_entry_set_text( GTK_ENTRY( entry ), buffer ); + gtk_signal_connect( GTK_OBJECT( entry ), "changed", + GTK_SIGNAL_FUNC( dialog_entry_gint32_callback ), value ); + gtk_table_attach_defaults( GTK_TABLE( table ), entry, 1, 2, row, row + 1 ); + gtk_widget_show( entry ); +} + +/******************************************************************************/ + +static void dialog_entry_gdouble_callback( GtkWidget *widget, gpointer *data ) +{ + gdouble value = (gdouble)atof( gtk_entry_get_text( GTK_ENTRY( widget ) ) ); + if( *(gdouble*)data != value ){ + *(gdouble*)data = value; + dialog_preview_draw(); + } +} + +static void dialog_entry_gdouble_new( char *caption, gdouble *value, + GtkWidget *table, gint row ) +{ + GtkWidget *label; + GtkWidget *entry; + char buffer[256]; + + label = gtk_label_new( caption ); + gtk_table_attach_defaults( GTK_TABLE( table ), label, 0, 1, row, row + 1 ); + gtk_widget_show( label ); + + entry = gtk_entry_new(); + sprintf( buffer, "%f", *value ); + gtk_entry_set_text( GTK_ENTRY( entry ), buffer ); + gtk_signal_connect( GTK_OBJECT( entry ), "changed", + GTK_SIGNAL_FUNC( dialog_entry_gdouble_callback ), value ); + gtk_table_attach_defaults( GTK_TABLE( table ), entry, 1, 2, row, row + 1 ); + gtk_widget_show( entry ); +} + +/******************************************************************************/ + +static GtkWidget* dialog_entry_table( void ) +{ + GtkWidget *table; + + table = gtk_table_new( 5, 2, FALSE ); + gtk_table_set_row_spacings( GTK_TABLE( table ), 2 ); + gtk_table_set_col_spacings( GTK_TABLE( table ), 10 ); + dialog_entry_gdouble_new( "X1", ¶meters.x1, table, 0 ); + dialog_entry_gdouble_new( "X2", ¶meters.x2, table, 1 ); + dialog_entry_gdouble_new( "Y1", ¶meters.y1, table, 2 ); + dialog_entry_gdouble_new( "Y2", ¶meters.y2, table, 3 ); + dialog_entry_gint32_new( "DEPTH", ¶meters.depth, table, 4 ); + + return table; +} + +/******************************************************************************/ + +static void dialog_destroy_callback( GtkWidget *widget, gpointer data ) +{ + gtk_main_quit(); + gdk_flush(); +} + +static void dialog_ok_callback( GtkWidget *widget, gpointer data ) +{ + dialog_status = TRUE; + gtk_widget_destroy( GTK_WIDGET( data ) ); +} + +static void dialog_cancel_callback( GtkWidget *widget, gpointer data ) +{ + dialog_status = FALSE; + gtk_widget_destroy( GTK_WIDGET( data ) ); +} + +static void dialog_help_callback( GtkWidget *widget, gpointer data ) +{ +} + +/******************************************************************************/ + +static void dialog_outside_type_callback( GtkWidget *widget, gpointer *data ) +{ + gint32 value = *(gint32*)data; + + if( parameters.outside_type != value ){ + parameters.outside_type = value; + dialog_preview_draw(); + } +} + +/******************************************************************************/ + +#define PREVIEW_SIZE 200 + +typedef struct { + GtkWidget *preview; + guchar **source; + guchar **pixels; + gdouble scale; + gint width; + gint height; + gint bpp; +} preview_t; + +static preview_t preview; + +static void dialog_preview_setpixel( gint x, gint y, pixel_t *pixel ) +{ + switch( preview.bpp ){ + case 1: + preview.pixels[y][x*preview.bpp] = pixel->r; + break; + case 3: + preview.pixels[y][x*preview.bpp] = pixel->r; + preview.pixels[y][x*preview.bpp+1] = pixel->g; + preview.pixels[y][x*preview.bpp+2] = pixel->b; + break; + } +} + +static void dialog_preview_store( void ) +{ + gint y; + for( y = 0; y < preview.height; y++ ){ + gtk_preview_draw_row( GTK_PREVIEW( preview.preview ), + preview.pixels[y], 0, y, preview.width ); + } + gtk_widget_draw( preview.preview, NULL ); + gdk_flush(); +} + +static void dialog_preview_init( void ) +{ + { + guchar *cube; + gtk_preview_set_gamma( gimp_gamma() ); + gtk_preview_set_install_cmap( gimp_install_cmap() ); + cube = gimp_color_cube(); + gtk_preview_set_color_cube( cube[0], cube[1], cube[2], cube[3] ); + gtk_widget_set_default_visual( gtk_preview_get_visual() ); + gtk_widget_set_default_colormap( gtk_preview_get_cmap() ); + } + + if( image.width < image.height ) + preview.scale = (gdouble)selection.height / (gdouble)PREVIEW_SIZE; + else + preview.scale = (gdouble)selection.width / (gdouble)PREVIEW_SIZE; + preview.width = (gdouble)selection.width / preview.scale; + preview.height = (gdouble)selection.height / preview.scale; + + if( image.bpp < 3 ){ + preview.bpp = 1; + preview.preview = gtk_preview_new( GTK_PREVIEW_GRAYSCALE ); + } else { + preview.bpp = 3; + preview.preview = gtk_preview_new( GTK_PREVIEW_COLOR ); + } + gtk_preview_size( GTK_PREVIEW( preview.preview ), preview.width, preview.height ); + + { + gint y; + preview.source = (guchar**)g_malloc( preview.height * sizeof( guchar* ) ); + preview.pixels = (guchar**)g_malloc( preview.height * sizeof( guchar* ) ); + for( y = 0; y < preview.height; y++ ){ + preview.source[y] = (guchar*)g_malloc( preview.width * preview.bpp * sizeof( guchar ) ); + preview.pixels[y] = (guchar*)g_malloc( preview.width * preview.bpp * sizeof( guchar ) ); + } + } + + { + pixel_t pixel; + gint x, y; + gdouble cx, cy; + for( y = 0; y < preview.height; y++ ){ + cy = selection.y1 + (gdouble)y * preview.scale; + for( x = 0; x < preview.width; x++ ){ + cx = selection.x1 + (gdouble)x * preview.scale; + pixels_get_biliner( cx, cy, &pixel ); + dialog_preview_setpixel( x, y, &pixel ); + } + } + dialog_preview_store(); + } +} + +static void dialog_preview_free( void ) +{ + gint y; + for( y = 0; y < preview.height; y++ ){ + free( preview.source[y] ); + free( preview.pixels[y] ); + } + free( preview.source ); + free( preview.pixels ); +} + +static void dialog_preview_draw( void ) +{ + gint x, y; + pixel_t pixel; + gdouble scale_x, scale_y; + gdouble cx, cy; + gdouble px, py; + + scale_x = ( parameters.x2 - parameters.x1 ) / preview.width; + scale_y = ( parameters.y2 - parameters.y1 ) / preview.height; + + for( y = 0; y < preview.height; y++ ){ + cy = parameters.y1 + y * scale_y; + for( x = 0; x < preview.width; x++ ){ + cx = parameters.x1 + x * scale_x; + mandelbrot( cx, cy, &px, &py ); + px = ( px - parameters.x1 ) / scale_x * preview.scale + selection.x1; + py = ( py - parameters.y1 ) / scale_y * preview.scale + selection.y1; + if( 0 <= px && px < image.width && 0 <= py && py < image.height ){ + pixels_get_biliner( px, py, &pixel ); + } else { + switch( parameters.outside_type ){ + case OUTSIDE_TYPE_WRAP: + px = fmod( px, image.width ); + py = fmod( py, image.height ); + if( px < 0.0 ) px += image.width; + if( py < 0.0 ) py += image.height; + pixels_get_biliner( px, py, &pixel ); + break; + case OUTSIDE_TYPE_TRANSPARENT: + case OUTSIDE_TYPE_BLACK: + pixel.r = pixel.g = pixel.b = 0; + break; + case OUTSIDE_TYPE_WHITE: + pixel.r = pixel.g = pixel.b = 255; + break; + } + } + dialog_preview_setpixel( x, y, &pixel ); + } + } + + dialog_preview_store(); +} + +/******************************************************************************/ + +static gint dialog_show( void ) +{ + GtkWidget *dialog; + GtkWidget *mainbox; + + dialog_status = FALSE; + + { + gint argc = 1; + gchar **argv = g_new( gchar *, 1 ); + argv[0] = g_strdup( PLUG_IN_TITLE ); + gtk_init( &argc, &argv ); + gtk_rc_parse( gimp_gtkrc() ); + } + + dialog = gtk_dialog_new(); + gtk_signal_connect( GTK_OBJECT( dialog ), "destroy", + GTK_SIGNAL_FUNC( dialog_destroy_callback ), NULL ); + + mainbox = gtk_hbox_new( FALSE, 0 ); + gtk_container_add( GTK_CONTAINER( GTK_DIALOG( dialog )->vbox ), mainbox ); + gtk_widget_show( mainbox ); + + { + GtkWidget *button; + + button = gtk_button_new_with_label( "OK" ); + gtk_signal_connect_object( GTK_OBJECT( button ), "clicked", + GTK_SIGNAL_FUNC( dialog_ok_callback ), + GTK_OBJECT( dialog ) ); + gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ), + button, TRUE, TRUE, 0 ); + GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT ); + gtk_widget_grab_default( button ); + gtk_widget_show( button ); + + button = gtk_button_new_with_label( "Cancel" ); + gtk_signal_connect_object( GTK_OBJECT( button ), "clicked", + GTK_SIGNAL_FUNC( dialog_cancel_callback ), + GTK_OBJECT( dialog ) ); + gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ), + button, TRUE, TRUE, 0 ); + gtk_widget_show( button ); + + button = gtk_button_new_with_label( "Help" ); + gtk_signal_connect_object( GTK_OBJECT( button ), "clicked", + GTK_SIGNAL_FUNC( dialog_help_callback ), + GTK_OBJECT( dialog ) ); + gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ), + button, TRUE, TRUE, 0 ); + gtk_widget_show( button ); + } + + { + GtkWidget *vbox; + GtkWidget *frame; + + vbox = gtk_vbox_new( TRUE, 0 ); + gtk_container_border_width( GTK_CONTAINER( vbox ), 10 ); + gtk_box_pack_start( GTK_BOX( mainbox ), vbox, FALSE, FALSE, 0 ); + gtk_widget_show( vbox ); + + frame = gtk_frame_new( NULL ); + gtk_frame_set_shadow_type( GTK_FRAME( frame ), GTK_SHADOW_IN ); + gtk_box_pack_start( GTK_BOX( vbox ), frame, FALSE, FALSE, 0 ); + gtk_widget_show( frame ); + + dialog_preview_init(); + gtk_container_add( GTK_CONTAINER( frame ), preview.preview ); + gtk_widget_show( preview.preview ); + } + + { + GtkWidget *vbox; + GtkWidget *entrytable; + GtkWidget *separator; + GtkWidget *frame; + GtkWidget *framebox; + GtkWidget *button; + GSList *group; + + vbox = gtk_vbox_new( FALSE, 0 ); + gtk_container_border_width( GTK_CONTAINER( vbox ), 10 ); + gtk_box_pack_start( GTK_BOX( mainbox ), vbox, FALSE, FALSE, 0 ); + gtk_widget_show( vbox ); + + entrytable = dialog_entry_table(); + gtk_box_pack_start( GTK_BOX( vbox ), entrytable, FALSE, FALSE, 0 ); + gtk_widget_show( entrytable ); + + separator = gtk_hseparator_new(); + gtk_box_pack_start( GTK_BOX( vbox ), separator, FALSE, FALSE, 0 ); + gtk_widget_show( entrytable ); + + frame = gtk_frame_new( "Outside Type" ); + gtk_container_border_width( GTK_CONTAINER( frame ), 5 ); + gtk_box_pack_start( GTK_BOX( vbox ), frame, TRUE, TRUE, 0 ); + gtk_widget_show( frame ); + + framebox = gtk_vbox_new( FALSE, 0 ); + gtk_container_border_width( GTK_CONTAINER( framebox ), 5 ); + gtk_container_add( GTK_CONTAINER( frame ), framebox ); + gtk_widget_show( framebox ); + + group = NULL; + + button = gtk_radio_button_new_with_label( group, "Wrap" ); + gtk_box_pack_start( GTK_BOX( framebox ), button, FALSE, FALSE, 0 ); + gtk_signal_connect( GTK_OBJECT( button ), "toggled", + GTK_SIGNAL_FUNC( dialog_outside_type_callback ), + &outside_type.wrap ); + gtk_widget_show( button ); + if( parameters.outside_type == OUTSIDE_TYPE_WRAP ){ + gtk_toggle_button_toggled( GTK_TOGGLE_BUTTON( button ) ); + gtk_toggle_button_set_state( GTK_TOGGLE_BUTTON( button ), TRUE ); + } + group = gtk_radio_button_group( GTK_RADIO_BUTTON( button ) ); + + button = gtk_radio_button_new_with_label( group, "Transparent" ); + gtk_box_pack_start( GTK_BOX( framebox ), button, FALSE, FALSE, 0 ); + gtk_signal_connect( GTK_OBJECT( button ), "toggled", + GTK_SIGNAL_FUNC( dialog_outside_type_callback ), + &outside_type.transparent ); + gtk_widget_show( button ); + if( parameters.outside_type == OUTSIDE_TYPE_TRANSPARENT ){ + gtk_toggle_button_toggled( GTK_TOGGLE_BUTTON( button ) ); + gtk_toggle_button_set_state( GTK_TOGGLE_BUTTON( button ), TRUE ); + } + if( !image.alpha ){ + gtk_widget_set_sensitive( button, FALSE ); + } + group = gtk_radio_button_group( GTK_RADIO_BUTTON( button ) ); + + button = gtk_radio_button_new_with_label( group, "Black" ); + gtk_box_pack_start( GTK_BOX( framebox ), button, FALSE, FALSE, 0 ); + gtk_signal_connect( GTK_OBJECT( button ), "toggled", + GTK_SIGNAL_FUNC( dialog_outside_type_callback ), + &outside_type.black ); + gtk_widget_show( button ); + if( parameters.outside_type == OUTSIDE_TYPE_BLACK ){ + gtk_toggle_button_toggled( GTK_TOGGLE_BUTTON( button ) ); + gtk_toggle_button_set_state( GTK_TOGGLE_BUTTON( button ), TRUE ); + } + group = gtk_radio_button_group( GTK_RADIO_BUTTON( button ) ); + + button = gtk_radio_button_new_with_label( group, "White" ); + gtk_box_pack_start( GTK_BOX( framebox ), button, FALSE, FALSE, 0 ); + gtk_signal_connect( GTK_OBJECT( button ), "toggled", + GTK_SIGNAL_FUNC( dialog_outside_type_callback ), + &outside_type.white ); + gtk_widget_show( button ); + if( parameters.outside_type == OUTSIDE_TYPE_WHITE ){ + gtk_toggle_button_toggled( GTK_TOGGLE_BUTTON( button ) ); + gtk_toggle_button_set_state( GTK_TOGGLE_BUTTON( button ), TRUE ); + } + group = gtk_radio_button_group( GTK_RADIO_BUTTON( button ) ); + + } + + gtk_widget_show( dialog ); + dialog_preview_draw(); + + gtk_main(); + + return dialog_status; +} + +/******************************************************************************/ diff --git a/plug-ins/fractaltrace/.cvsignore b/plug-ins/fractaltrace/.cvsignore new file mode 100644 index 0000000000..2182ae4b53 --- /dev/null +++ b/plug-ins/fractaltrace/.cvsignore @@ -0,0 +1,5 @@ +Makefile.in +Makefile +.deps +_libs +fractaltrace diff --git a/plug-ins/fractaltrace/Makefile.am b/plug-ins/fractaltrace/Makefile.am new file mode 100644 index 0000000000..d9566509cf --- /dev/null +++ b/plug-ins/fractaltrace/Makefile.am @@ -0,0 +1,42 @@ +## Process this file with automake to produce Makefile.in + +pluginlibdir = $(gimpplugindir)/plug-ins + +pluginlib_PROGRAMS = fractaltrace + +fractaltrace_SOURCES = \ + fractaltrace.c + +INCLUDES = \ + $(X_CFLAGS) \ + -I$(top_srcdir) \ + -I$(includedir) + +LDADD = \ + $(top_builddir)/libgimp/libgimpui.la \ + $(top_builddir)/libgimp/libgimp.la \ + $(X_LIBS) \ + \ + -lc + +DEPS = \ + $(top_builddir)/libgimp/libgimpui.la \ + $(top_builddir)/libgimp/libgimp.la + +fractaltrace_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/fractaltrace/fractaltrace.c b/plug-ins/fractaltrace/fractaltrace.c new file mode 100644 index 0000000000..3843ee48fd --- /dev/null +++ b/plug-ins/fractaltrace/fractaltrace.c @@ -0,0 +1,904 @@ +/******************************************************************************* + + fractaltrace.c -- This is a plug-in for the GIMP 1.0 + + Copyright (C) 1997 Hirotsuna Mizuno + s1041150@u-aizu.ac.jp + + 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. + +*******************************************************************************/ + +#define PLUG_IN_NAME "plug_in_fractal_trace" +#define PLUG_IN_TITLE "Fractal Trace" +#define PLUG_IN_VERSION "v0.4 test version (Dec. 25 1997)" +#define PLUG_IN_CATEGORY "/Filters/Distorts/Fractal Trace" + +/******************************************************************************/ + +#include +#include +#include +#include + +#ifndef PI_2 +#define PI_2 (3.14159265358979323*2.0) +#endif + +/******************************************************************************/ + +static void query( void ); +static void run( char*, int, GParam*, int*, GParam** ); +static void filter( GDrawable* ); + +static void pixels_init( GDrawable* ); +static void pixels_free( void ); + +static int dialog_show( void ); +static void dialog_preview_draw( void ); + +/******************************************************************************/ + +GPlugInInfo PLUG_IN_INFO = { + NULL, /* init_proc */ + NULL, /* quit_proc */ + query, /* query_proc */ + run /* run_proc */ +}; + +MAIN(); + +/******************************************************************************/ + +typedef struct { + gint32 wrap; + gint32 transparent; + gint32 black; + gint32 white; +} outside_type_t; + +#define OUTSIDE_TYPE_WRAP 0 +#define OUTSIDE_TYPE_TRANSPARENT 1 +#define OUTSIDE_TYPE_BLACK 2 +#define OUTSIDE_TYPE_WHITE 3 + +static outside_type_t outside_type = { + OUTSIDE_TYPE_WRAP, + OUTSIDE_TYPE_TRANSPARENT, + OUTSIDE_TYPE_BLACK, + OUTSIDE_TYPE_WHITE +}; + +typedef struct { + gdouble x1; + gdouble x2; + gdouble y1; + gdouble y2; + gint32 depth; + gint32 outside_type; +} parameter_t; + +static parameter_t parameters = { + -1.0, + +0.5, + -1.0, + +1.0, + 3, + OUTSIDE_TYPE_WRAP +}; + +/******************************************************************************/ + +static void query( void ) +{ + static GParamDef args[] = { + { PARAM_INT32, "run_mode", "interactive / non-interactive" }, + { PARAM_IMAGE, "image", "input image (not used)" }, + { PARAM_DRAWABLE, "drawable", "input drawable" }, + { PARAM_FLOAT, "xmin", "xmin fractal image delimiter" }, + { PARAM_FLOAT, "xmax", "xmax fractal image delimiter" }, + { PARAM_FLOAT, "ymin", "ymin fractal image delimiter" }, + { PARAM_FLOAT, "ymax", "ymax fractal image delimiter" }, + { PARAM_FLOAT, "ymax", "ymax fractal image delimiter" }, + { PARAM_INT32, "depth", "trace depth" }, + { PARAM_INT32, "outside_type", "outside type" + "(0=WRAP/1=TRANS/2=BLACK/3=WHITE)" }, + }; + static int nargs = sizeof( args ) / sizeof( args[0] ); + + static GParamDef *rets = NULL; + static int nrets = 0; + + gimp_install_procedure( + PLUG_IN_NAME, + "transform image with the Mandelbrot Fractal", + "transform image with the Mandelbrot Fractal", + "Hirotsuna Mizuno ", + "Copyright (C) 1997 Hirotsuna Mizuno", + PLUG_IN_VERSION, + PLUG_IN_CATEGORY, + "RGB*, GRAY*", + PROC_PLUG_IN, + nargs, + nrets, + args, + rets + ); +} + +/******************************************************************************/ + +typedef struct { + gint x1; + gint x2; + gint y1; + gint y2; + gint width; + gint height; + gdouble center_x; + gdouble center_y; +} selection_t; + +typedef struct { + gint width; + gint height; + gint bpp; + gint alpha; +} image_t; + +static selection_t selection; +static image_t image; + +/******************************************************************************/ + +static void run( char *name, int argc, GParam *args, int *retc, GParam **rets ) +{ + GDrawable *drawable; + GRunModeType run_mode; + GStatusType status; + static GParam returns[1]; + + run_mode = args[0].data.d_int32; + status = STATUS_SUCCESS; + + drawable = gimp_drawable_get( args[2].data.d_drawable ); + image.width = gimp_drawable_width( drawable->id ); + image.height = gimp_drawable_height( drawable->id ); + image.bpp = gimp_drawable_bpp( drawable->id ); + image.alpha = gimp_drawable_has_alpha( drawable->id ); + gimp_drawable_mask_bounds( drawable->id, &selection.x1, &selection.y1, + &selection.x2, &selection.y2 ); + selection.width = selection.x2 - selection.y1; + selection.height = selection.y2 - selection.y1; + selection.center_x = selection.x1 + (gdouble)selection.width / 2.0; + selection.center_y = selection.y1 + (gdouble)selection.height / 2.0; + + pixels_init( drawable ); + + if( !gimp_drawable_color( drawable->id ) && + !gimp_drawable_gray( drawable->id ) ){ + status = STATUS_EXECUTION_ERROR; + } + + switch( run_mode ){ + case RUN_WITH_LAST_VALS: + gimp_get_data( PLUG_IN_NAME, ¶meters ); + break; + case RUN_INTERACTIVE: + gimp_get_data( PLUG_IN_NAME, ¶meters ); + if( !dialog_show() ){ + status = STATUS_EXECUTION_ERROR; + break; + } + gimp_set_data( PLUG_IN_NAME, ¶meters, sizeof( parameter_t ) ); + break; + case RUN_NONINTERACTIVE: + if( argc != 9 ){ + status = STATUS_CALLING_ERROR; + } else { + parameters.x1 = args[3].data.d_float; + parameters.x2 = args[4].data.d_float; + parameters.y1 = args[5].data.d_float; + parameters.y2 = args[6].data.d_float; + parameters.depth = args[7].data.d_int32; + parameters.outside_type = args[8].data.d_int32; + } + break; + } + + if( status == STATUS_SUCCESS ){ + gimp_tile_cache_ntiles( 2 * ( drawable->width / gimp_tile_width() + 1 ) ); + filter( drawable ); + if( run_mode != RUN_NONINTERACTIVE ) gimp_displays_flush(); + } + + gimp_drawable_detach( drawable ); + + pixels_free(); + + returns[0].type = PARAM_STATUS; + returns[0].data.d_status = status; + *retc = 1; + *rets = returns; +} + +/******************************************************************************/ + +static guchar **spixels; +static guchar **dpixels; +static GPixelRgn sPR; +static GPixelRgn dPR; + +typedef struct { + guchar r; + guchar g; + guchar b; + guchar a; +} pixel_t; + +static void pixels_init( GDrawable *drawable ) +{ + gint y; + gimp_pixel_rgn_init( &sPR, drawable, + 0, 0, image.width, image.height, FALSE, FALSE ); + gimp_pixel_rgn_init( &dPR, drawable, + 0, 0, image.width, image.height, TRUE, TRUE ); + spixels = (guchar**)g_malloc( image.height * sizeof( guchar* ) ); + dpixels = (guchar**)g_malloc( image.height * sizeof( guchar* ) ); + for( y = 0; y < image.height; y++ ){ + spixels[y] = (guchar*)g_malloc( image.width * image.bpp * sizeof( guchar ) ); + dpixels[y] = (guchar*)g_malloc( image.width * image.bpp * sizeof( guchar ) ); + gimp_pixel_rgn_get_row( &sPR, spixels[y], 0, y, image.width ); + } +} + +static void pixels_free( void ) +{ + gint y; + for( y = 0; y < image.height; y++ ){ + free( spixels[y] ); + free( dpixels[y] ); + } + free( spixels ); + free( dpixels ); +} + +static void pixels_get( gint x, gint y, pixel_t *pixel ) +{ + if( x < 0 ) x = 0; else if( image.width <= x ) x = image.width - 1; + if( y < 0 ) y = 0; else if( image.height <= y ) y = image.height - 1; + + switch( image.bpp ){ + case 1: /* GRAY */ + pixel->r = spixels[y][x*image.bpp]; + pixel->g = spixels[y][x*image.bpp]; + pixel->b = spixels[y][x*image.bpp]; + pixel->a = 255; + break; + case 2: /* GRAY+A */ + pixel->r = spixels[y][x*image.bpp]; + pixel->g = spixels[y][x*image.bpp]; + pixel->b = spixels[y][x*image.bpp]; + pixel->a = spixels[y][x*image.bpp+1]; + break; + case 3: /* RGB */ + pixel->r = spixels[y][x*image.bpp]; + pixel->g = spixels[y][x*image.bpp+1]; + pixel->b = spixels[y][x*image.bpp+2]; + pixel->a = 255; + break; + case 4: /* RGB+A */ + pixel->r = spixels[y][x*image.bpp]; + pixel->g = spixels[y][x*image.bpp+1]; + pixel->b = spixels[y][x*image.bpp+2]; + pixel->a = spixels[y][x*image.bpp+3]; + break; + } +} + +static void pixels_get_biliner( gdouble x, gdouble y, pixel_t *pixel ) +{ + pixel_t A, B, C, D; + gdouble a, b, c, d; + gint x1, y1, x2, y2; + gdouble dx, dy; + + x1 = (gint)floor( x ); + x2 = x1 + 1; + y1 = (gint)floor( y ); + y2 = y1 + 1; + + dx = x - (gdouble)x1; + dy = y - (gdouble)y1; + a = ( 1.0 - dx ) * ( 1.0 - dy ); + b = dx * ( 1.0 - dy ); + c = ( 1.0 - dx ) * dy; + d = dx * dy; + + pixels_get( x1, y1, &A ); + pixels_get( x2, y1, &B ); + pixels_get( x1, y2, &C ); + pixels_get( x2, y2, &D ); + + pixel->r = (guchar)( a * (gdouble)A.r + b * (gdouble)B.r + + c * (gdouble)C.r + d * (gdouble)D.r ); + pixel->g = (guchar)( a * (gdouble)A.g + b * (gdouble)B.g + + c * (gdouble)C.g + d * (gdouble)D.g ); + pixel->b = (guchar)( a * (gdouble)A.b + b * (gdouble)B.b + + c * (gdouble)C.b + d * (gdouble)D.b ); + pixel->a = (guchar)( a * (gdouble)A.a + b * (gdouble)B.a + + c * (gdouble)C.a + d * (gdouble)D.a ); +} + +static void pixels_set( gint x, gint y, pixel_t *pixel ) +{ + switch( image.bpp ){ + case 1: /* GRAY */ + dpixels[y][x*image.bpp] = pixel->r; + break; + case 2: /* GRAY+A */ + dpixels[y][x*image.bpp] = pixel->r; + dpixels[y][x*image.bpp+1] = pixel->a; + break; + case 3: /* RGB */ + dpixels[y][x*image.bpp] = pixel->r; + dpixels[y][x*image.bpp+1] = pixel->g; + dpixels[y][x*image.bpp+2] = pixel->b; + break; + case 4: /* RGB+A */ + dpixels[y][x*image.bpp] = pixel->r; + dpixels[y][x*image.bpp+1] = pixel->g; + dpixels[y][x*image.bpp+2] = pixel->b; + dpixels[y][x*image.bpp+3] = pixel->a; + break; + } +} + +static void pixels_store( void ) +{ + gint y; + for( y = selection.y1; y < selection.y2; y++ ){ + gimp_pixel_rgn_set_row( &dPR, dpixels[y], 0, y, image.width ); + } +} + +/******************************************************************************/ + +static void mandelbrot( gdouble x, gdouble y, gdouble *u, gdouble *v ) +{ + gint iter = 0; + gdouble xx = x; + gdouble yy = y; + gdouble x2 = xx * xx; + gdouble y2 = yy * yy; + gdouble tmp; + while( iter < parameters.depth ){ + tmp = x2 - y2 + x; + yy = 2 * xx * yy + y; + xx = tmp; + x2 = xx * xx; + y2 = yy * yy; + iter++; + } + *u = xx; + *v = yy; +} + + +/******************************************************************************/ + +static void filter( GDrawable *drawable ) +{ + gint x, y; + pixel_t pixel; + gdouble scale_x, scale_y; + gdouble cx, cy; + gdouble px, py; + + gimp_progress_init( PLUG_IN_TITLE ); + + scale_x = ( parameters.x2 - parameters.x1 ) / selection.width; + scale_y = ( parameters.y2 - parameters.y1 ) / selection.height; + + for( y = selection.y1; y < selection.y2; y++ ){ + cy = parameters.y1 + ( y - selection.y1 ) * scale_y; + for( x = selection.x1; x < selection.x2; x++ ){ + cx = parameters.x1 + ( x - selection.x1 ) * scale_x; + mandelbrot( cx, cy, &px, &py ); + px = ( px - parameters.x1 ) / scale_x + selection.x1; + py = ( py - parameters.y1 ) / scale_y + selection.y1; + if( 0 <= px && px < image.width && 0 <= py && py < image.height ){ + pixels_get_biliner( px, py, &pixel ); + } else { + switch( parameters.outside_type ){ + case OUTSIDE_TYPE_WRAP: + px = fmod( px, image.width ); + py = fmod( py, image.height ); + if( px < 0.0 ) px += image.width; + if( py < 0.0 ) py += image.height; + pixels_get_biliner( px, py, &pixel ); + break; + case OUTSIDE_TYPE_TRANSPARENT: + pixel.r = pixel.g = pixel.b = 0; + pixel.a = 0; + break; + case OUTSIDE_TYPE_BLACK: + pixel.r = pixel.g = pixel.b = 0; + pixel.a = 255; + break; + case OUTSIDE_TYPE_WHITE: + pixel.r = pixel.g = pixel.b = 255; + pixel.a = 255; + break; + } + } + pixels_set( x, y, &pixel ); + } + gimp_progress_update( (gdouble)(y-selection.y1)/selection.height ); + } + + pixels_store(); + + gimp_drawable_flush( drawable ); + gimp_drawable_merge_shadow( drawable->id, TRUE ); + gimp_drawable_update( drawable->id, + selection.x1, selection.y1, selection.width, selection.height ); +} + +/* + GUI +*/ + +/******************************************************************************/ + +static int dialog_status; + +/******************************************************************************/ + +static void dialog_entry_gint32_callback( GtkWidget *widget, gpointer *data ) +{ + gint32 value = (gint32)atof( gtk_entry_get_text( GTK_ENTRY( widget ) ) ); + if( *(gint32*)data != value ){ + *(gint32*)data = value; + dialog_preview_draw(); + } +} + +static void dialog_entry_gint32_new( char *caption, gint32 *value, + GtkWidget *table, gint row ) +{ + GtkWidget *label; + GtkWidget *entry; + char buffer[256]; + + label = gtk_label_new( caption ); + gtk_table_attach_defaults( GTK_TABLE( table ), label, 0, 1, row, row + 1 ); + gtk_widget_show( label ); + + entry = gtk_entry_new(); + sprintf( buffer, "%d", *value ); + gtk_entry_set_text( GTK_ENTRY( entry ), buffer ); + gtk_signal_connect( GTK_OBJECT( entry ), "changed", + GTK_SIGNAL_FUNC( dialog_entry_gint32_callback ), value ); + gtk_table_attach_defaults( GTK_TABLE( table ), entry, 1, 2, row, row + 1 ); + gtk_widget_show( entry ); +} + +/******************************************************************************/ + +static void dialog_entry_gdouble_callback( GtkWidget *widget, gpointer *data ) +{ + gdouble value = (gdouble)atof( gtk_entry_get_text( GTK_ENTRY( widget ) ) ); + if( *(gdouble*)data != value ){ + *(gdouble*)data = value; + dialog_preview_draw(); + } +} + +static void dialog_entry_gdouble_new( char *caption, gdouble *value, + GtkWidget *table, gint row ) +{ + GtkWidget *label; + GtkWidget *entry; + char buffer[256]; + + label = gtk_label_new( caption ); + gtk_table_attach_defaults( GTK_TABLE( table ), label, 0, 1, row, row + 1 ); + gtk_widget_show( label ); + + entry = gtk_entry_new(); + sprintf( buffer, "%f", *value ); + gtk_entry_set_text( GTK_ENTRY( entry ), buffer ); + gtk_signal_connect( GTK_OBJECT( entry ), "changed", + GTK_SIGNAL_FUNC( dialog_entry_gdouble_callback ), value ); + gtk_table_attach_defaults( GTK_TABLE( table ), entry, 1, 2, row, row + 1 ); + gtk_widget_show( entry ); +} + +/******************************************************************************/ + +static GtkWidget* dialog_entry_table( void ) +{ + GtkWidget *table; + + table = gtk_table_new( 5, 2, FALSE ); + gtk_table_set_row_spacings( GTK_TABLE( table ), 2 ); + gtk_table_set_col_spacings( GTK_TABLE( table ), 10 ); + dialog_entry_gdouble_new( "X1", ¶meters.x1, table, 0 ); + dialog_entry_gdouble_new( "X2", ¶meters.x2, table, 1 ); + dialog_entry_gdouble_new( "Y1", ¶meters.y1, table, 2 ); + dialog_entry_gdouble_new( "Y2", ¶meters.y2, table, 3 ); + dialog_entry_gint32_new( "DEPTH", ¶meters.depth, table, 4 ); + + return table; +} + +/******************************************************************************/ + +static void dialog_destroy_callback( GtkWidget *widget, gpointer data ) +{ + gtk_main_quit(); + gdk_flush(); +} + +static void dialog_ok_callback( GtkWidget *widget, gpointer data ) +{ + dialog_status = TRUE; + gtk_widget_destroy( GTK_WIDGET( data ) ); +} + +static void dialog_cancel_callback( GtkWidget *widget, gpointer data ) +{ + dialog_status = FALSE; + gtk_widget_destroy( GTK_WIDGET( data ) ); +} + +static void dialog_help_callback( GtkWidget *widget, gpointer data ) +{ +} + +/******************************************************************************/ + +static void dialog_outside_type_callback( GtkWidget *widget, gpointer *data ) +{ + gint32 value = *(gint32*)data; + + if( parameters.outside_type != value ){ + parameters.outside_type = value; + dialog_preview_draw(); + } +} + +/******************************************************************************/ + +#define PREVIEW_SIZE 200 + +typedef struct { + GtkWidget *preview; + guchar **source; + guchar **pixels; + gdouble scale; + gint width; + gint height; + gint bpp; +} preview_t; + +static preview_t preview; + +static void dialog_preview_setpixel( gint x, gint y, pixel_t *pixel ) +{ + switch( preview.bpp ){ + case 1: + preview.pixels[y][x*preview.bpp] = pixel->r; + break; + case 3: + preview.pixels[y][x*preview.bpp] = pixel->r; + preview.pixels[y][x*preview.bpp+1] = pixel->g; + preview.pixels[y][x*preview.bpp+2] = pixel->b; + break; + } +} + +static void dialog_preview_store( void ) +{ + gint y; + for( y = 0; y < preview.height; y++ ){ + gtk_preview_draw_row( GTK_PREVIEW( preview.preview ), + preview.pixels[y], 0, y, preview.width ); + } + gtk_widget_draw( preview.preview, NULL ); + gdk_flush(); +} + +static void dialog_preview_init( void ) +{ + { + guchar *cube; + gtk_preview_set_gamma( gimp_gamma() ); + gtk_preview_set_install_cmap( gimp_install_cmap() ); + cube = gimp_color_cube(); + gtk_preview_set_color_cube( cube[0], cube[1], cube[2], cube[3] ); + gtk_widget_set_default_visual( gtk_preview_get_visual() ); + gtk_widget_set_default_colormap( gtk_preview_get_cmap() ); + } + + if( image.width < image.height ) + preview.scale = (gdouble)selection.height / (gdouble)PREVIEW_SIZE; + else + preview.scale = (gdouble)selection.width / (gdouble)PREVIEW_SIZE; + preview.width = (gdouble)selection.width / preview.scale; + preview.height = (gdouble)selection.height / preview.scale; + + if( image.bpp < 3 ){ + preview.bpp = 1; + preview.preview = gtk_preview_new( GTK_PREVIEW_GRAYSCALE ); + } else { + preview.bpp = 3; + preview.preview = gtk_preview_new( GTK_PREVIEW_COLOR ); + } + gtk_preview_size( GTK_PREVIEW( preview.preview ), preview.width, preview.height ); + + { + gint y; + preview.source = (guchar**)g_malloc( preview.height * sizeof( guchar* ) ); + preview.pixels = (guchar**)g_malloc( preview.height * sizeof( guchar* ) ); + for( y = 0; y < preview.height; y++ ){ + preview.source[y] = (guchar*)g_malloc( preview.width * preview.bpp * sizeof( guchar ) ); + preview.pixels[y] = (guchar*)g_malloc( preview.width * preview.bpp * sizeof( guchar ) ); + } + } + + { + pixel_t pixel; + gint x, y; + gdouble cx, cy; + for( y = 0; y < preview.height; y++ ){ + cy = selection.y1 + (gdouble)y * preview.scale; + for( x = 0; x < preview.width; x++ ){ + cx = selection.x1 + (gdouble)x * preview.scale; + pixels_get_biliner( cx, cy, &pixel ); + dialog_preview_setpixel( x, y, &pixel ); + } + } + dialog_preview_store(); + } +} + +static void dialog_preview_free( void ) +{ + gint y; + for( y = 0; y < preview.height; y++ ){ + free( preview.source[y] ); + free( preview.pixels[y] ); + } + free( preview.source ); + free( preview.pixels ); +} + +static void dialog_preview_draw( void ) +{ + gint x, y; + pixel_t pixel; + gdouble scale_x, scale_y; + gdouble cx, cy; + gdouble px, py; + + scale_x = ( parameters.x2 - parameters.x1 ) / preview.width; + scale_y = ( parameters.y2 - parameters.y1 ) / preview.height; + + for( y = 0; y < preview.height; y++ ){ + cy = parameters.y1 + y * scale_y; + for( x = 0; x < preview.width; x++ ){ + cx = parameters.x1 + x * scale_x; + mandelbrot( cx, cy, &px, &py ); + px = ( px - parameters.x1 ) / scale_x * preview.scale + selection.x1; + py = ( py - parameters.y1 ) / scale_y * preview.scale + selection.y1; + if( 0 <= px && px < image.width && 0 <= py && py < image.height ){ + pixels_get_biliner( px, py, &pixel ); + } else { + switch( parameters.outside_type ){ + case OUTSIDE_TYPE_WRAP: + px = fmod( px, image.width ); + py = fmod( py, image.height ); + if( px < 0.0 ) px += image.width; + if( py < 0.0 ) py += image.height; + pixels_get_biliner( px, py, &pixel ); + break; + case OUTSIDE_TYPE_TRANSPARENT: + case OUTSIDE_TYPE_BLACK: + pixel.r = pixel.g = pixel.b = 0; + break; + case OUTSIDE_TYPE_WHITE: + pixel.r = pixel.g = pixel.b = 255; + break; + } + } + dialog_preview_setpixel( x, y, &pixel ); + } + } + + dialog_preview_store(); +} + +/******************************************************************************/ + +static gint dialog_show( void ) +{ + GtkWidget *dialog; + GtkWidget *mainbox; + + dialog_status = FALSE; + + { + gint argc = 1; + gchar **argv = g_new( gchar *, 1 ); + argv[0] = g_strdup( PLUG_IN_TITLE ); + gtk_init( &argc, &argv ); + gtk_rc_parse( gimp_gtkrc() ); + } + + dialog = gtk_dialog_new(); + gtk_signal_connect( GTK_OBJECT( dialog ), "destroy", + GTK_SIGNAL_FUNC( dialog_destroy_callback ), NULL ); + + mainbox = gtk_hbox_new( FALSE, 0 ); + gtk_container_add( GTK_CONTAINER( GTK_DIALOG( dialog )->vbox ), mainbox ); + gtk_widget_show( mainbox ); + + { + GtkWidget *button; + + button = gtk_button_new_with_label( "OK" ); + gtk_signal_connect_object( GTK_OBJECT( button ), "clicked", + GTK_SIGNAL_FUNC( dialog_ok_callback ), + GTK_OBJECT( dialog ) ); + gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ), + button, TRUE, TRUE, 0 ); + GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT ); + gtk_widget_grab_default( button ); + gtk_widget_show( button ); + + button = gtk_button_new_with_label( "Cancel" ); + gtk_signal_connect_object( GTK_OBJECT( button ), "clicked", + GTK_SIGNAL_FUNC( dialog_cancel_callback ), + GTK_OBJECT( dialog ) ); + gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ), + button, TRUE, TRUE, 0 ); + gtk_widget_show( button ); + + button = gtk_button_new_with_label( "Help" ); + gtk_signal_connect_object( GTK_OBJECT( button ), "clicked", + GTK_SIGNAL_FUNC( dialog_help_callback ), + GTK_OBJECT( dialog ) ); + gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ), + button, TRUE, TRUE, 0 ); + gtk_widget_show( button ); + } + + { + GtkWidget *vbox; + GtkWidget *frame; + + vbox = gtk_vbox_new( TRUE, 0 ); + gtk_container_border_width( GTK_CONTAINER( vbox ), 10 ); + gtk_box_pack_start( GTK_BOX( mainbox ), vbox, FALSE, FALSE, 0 ); + gtk_widget_show( vbox ); + + frame = gtk_frame_new( NULL ); + gtk_frame_set_shadow_type( GTK_FRAME( frame ), GTK_SHADOW_IN ); + gtk_box_pack_start( GTK_BOX( vbox ), frame, FALSE, FALSE, 0 ); + gtk_widget_show( frame ); + + dialog_preview_init(); + gtk_container_add( GTK_CONTAINER( frame ), preview.preview ); + gtk_widget_show( preview.preview ); + } + + { + GtkWidget *vbox; + GtkWidget *entrytable; + GtkWidget *separator; + GtkWidget *frame; + GtkWidget *framebox; + GtkWidget *button; + GSList *group; + + vbox = gtk_vbox_new( FALSE, 0 ); + gtk_container_border_width( GTK_CONTAINER( vbox ), 10 ); + gtk_box_pack_start( GTK_BOX( mainbox ), vbox, FALSE, FALSE, 0 ); + gtk_widget_show( vbox ); + + entrytable = dialog_entry_table(); + gtk_box_pack_start( GTK_BOX( vbox ), entrytable, FALSE, FALSE, 0 ); + gtk_widget_show( entrytable ); + + separator = gtk_hseparator_new(); + gtk_box_pack_start( GTK_BOX( vbox ), separator, FALSE, FALSE, 0 ); + gtk_widget_show( entrytable ); + + frame = gtk_frame_new( "Outside Type" ); + gtk_container_border_width( GTK_CONTAINER( frame ), 5 ); + gtk_box_pack_start( GTK_BOX( vbox ), frame, TRUE, TRUE, 0 ); + gtk_widget_show( frame ); + + framebox = gtk_vbox_new( FALSE, 0 ); + gtk_container_border_width( GTK_CONTAINER( framebox ), 5 ); + gtk_container_add( GTK_CONTAINER( frame ), framebox ); + gtk_widget_show( framebox ); + + group = NULL; + + button = gtk_radio_button_new_with_label( group, "Wrap" ); + gtk_box_pack_start( GTK_BOX( framebox ), button, FALSE, FALSE, 0 ); + gtk_signal_connect( GTK_OBJECT( button ), "toggled", + GTK_SIGNAL_FUNC( dialog_outside_type_callback ), + &outside_type.wrap ); + gtk_widget_show( button ); + if( parameters.outside_type == OUTSIDE_TYPE_WRAP ){ + gtk_toggle_button_toggled( GTK_TOGGLE_BUTTON( button ) ); + gtk_toggle_button_set_state( GTK_TOGGLE_BUTTON( button ), TRUE ); + } + group = gtk_radio_button_group( GTK_RADIO_BUTTON( button ) ); + + button = gtk_radio_button_new_with_label( group, "Transparent" ); + gtk_box_pack_start( GTK_BOX( framebox ), button, FALSE, FALSE, 0 ); + gtk_signal_connect( GTK_OBJECT( button ), "toggled", + GTK_SIGNAL_FUNC( dialog_outside_type_callback ), + &outside_type.transparent ); + gtk_widget_show( button ); + if( parameters.outside_type == OUTSIDE_TYPE_TRANSPARENT ){ + gtk_toggle_button_toggled( GTK_TOGGLE_BUTTON( button ) ); + gtk_toggle_button_set_state( GTK_TOGGLE_BUTTON( button ), TRUE ); + } + if( !image.alpha ){ + gtk_widget_set_sensitive( button, FALSE ); + } + group = gtk_radio_button_group( GTK_RADIO_BUTTON( button ) ); + + button = gtk_radio_button_new_with_label( group, "Black" ); + gtk_box_pack_start( GTK_BOX( framebox ), button, FALSE, FALSE, 0 ); + gtk_signal_connect( GTK_OBJECT( button ), "toggled", + GTK_SIGNAL_FUNC( dialog_outside_type_callback ), + &outside_type.black ); + gtk_widget_show( button ); + if( parameters.outside_type == OUTSIDE_TYPE_BLACK ){ + gtk_toggle_button_toggled( GTK_TOGGLE_BUTTON( button ) ); + gtk_toggle_button_set_state( GTK_TOGGLE_BUTTON( button ), TRUE ); + } + group = gtk_radio_button_group( GTK_RADIO_BUTTON( button ) ); + + button = gtk_radio_button_new_with_label( group, "White" ); + gtk_box_pack_start( GTK_BOX( framebox ), button, FALSE, FALSE, 0 ); + gtk_signal_connect( GTK_OBJECT( button ), "toggled", + GTK_SIGNAL_FUNC( dialog_outside_type_callback ), + &outside_type.white ); + gtk_widget_show( button ); + if( parameters.outside_type == OUTSIDE_TYPE_WHITE ){ + gtk_toggle_button_toggled( GTK_TOGGLE_BUTTON( button ) ); + gtk_toggle_button_set_state( GTK_TOGGLE_BUTTON( button ), TRUE ); + } + group = gtk_radio_button_group( GTK_RADIO_BUTTON( button ) ); + + } + + gtk_widget_show( dialog ); + dialog_preview_draw(); + + gtk_main(); + + return dialog_status; +} + +/******************************************************************************/