/* * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * This is a plug-in for the GIMP. * * Tileit - This plugin will take an image an make repeated * copies of it the stepping is 1/(2**n); 1<=n<=6 * * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk * * 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. * * A fair proprotion of this code was taken from the Whirl plug-in * which was copyrighted by Federico Mena Quintero (as below). * * Whirl plug-in --- distort an image into a whirlpool * Copyright (C) 1997 Federico Mena Quintero * */ /* Change log:- * 0.2 Added new functions to allow "editing" of the tile patten. * * 0.1 First version released. */ #include #include #include #include "gtk/gtk.h" #include "libgimp/gimp.h" /***** Magic numbers *****/ #define PREVIEW_SIZE 128 #define SCALE_WIDTH 80 #define ENTRY_WIDTH 25 /* Even more stuff from Quartics plugins */ #define CHECK_SIZE 8 #define CHECK_DARK ((int) (1.0 / 3.0 * 255)) #define CHECK_LIGHT ((int) (2.0 / 3.0 * 255)) #define MAX_SEGS 6 #define TRUE 1 #define FALSE 0 #define PREVIEW_MASK GDK_EXPOSURE_MASK | \ GDK_BUTTON_PRESS_MASK | \ GDK_BUTTON_MOTION_MASK /* Variables set in dialog box */ typedef struct data { gint numtiles; } TileItVals; typedef struct { GtkWidget *preview; guchar preview_row[PREVIEW_SIZE * 4]; gint run; guchar * pv_cache; gint img_bpp; } TileItInterface; static TileItInterface tint = { NULL, /* Preview */ {'4','u'}, /* Preview_row */ FALSE, /* run */ NULL, 4 /* bpp of drawable */ }; GDrawable *tileitdrawable; static gint tile_width, tile_height; static GTile *the_tile = NULL; static gint img_width, img_height,img_bpp; static void query (void); static void run (gchar *name, gint nparams, GParam *param, gint *nreturn_vals, GParam **return_vals); /* static void check (GDrawable * drawable); */ static gint tileit_dialog (void); static void tileit_close_callback (GtkWidget *widget, gpointer data); static void tileit_ok_callback (GtkWidget *widget, gpointer data); static void tileit_scale_update (GtkAdjustment *adjustment, gint *size_val); static void tileit_entry_update(GtkWidget *widget, gint *value); static void tileit_exp_update(GtkWidget *widget, gpointer value); static void tileit_exp_update_f(GtkWidget *widget, gpointer value); static void tileit_reset(GtkWidget *widget, gpointer value); static void tileit_toggle_update(GtkWidget *widget, gpointer data); static void tileit_hvtoggle_update(GtkWidget *widget, gpointer data); static void do_tiles(void); static gint tiles_xy(gint width, gint height,gint x,gint y,gint *nx,gint *ny); static void all_update(void); static void alt_update(void); static void explict_update(gint); static void dialog_update_preview(void); static void cache_preview(void); static gint tileit_preview_expose ( GtkWidget *widget,GdkEvent *event ); static gint tileit_preview_events ( GtkWidget *widget,GdkEvent *event ); GPlugInInfo PLUG_IN_INFO = { NULL, /* init_proc */ NULL, /* quit_proc */ query, /* query_proc */ run, /* run_proc */ }; /* Values when first invoked */ static TileItVals itvals = { 2 }; /* Structures for call backs... */ /* The "explict tile" & family */ typedef enum { ALL, ALT, EXPLICT } AppliedTo; typedef struct { AppliedTo type; gint x; /* X - pos of tile */ gint y; /* Y - pos of tile */ GtkWidget *r_label; /* row label */ GtkWidget *r_entry; /* row entry */ GtkWidget *c_label; /* column label */ GtkWidget *c_entry; /* column entry */ GtkWidget *applybut; /* The apply button */ } Exp_Call; Exp_Call exp_call = { ALL, -1, -1, NULL, NULL, NULL, NULL, NULL, }; /* The reset button needs to know some toggle widgets.. */ typedef struct { GtkWidget *htoggle; GtkWidget *vtoggle; } Reset_Call; Reset_Call res_call = { NULL, NULL, }; /* 2D - Array that holds the actions for each tile */ /* Action type on cell */ #define HORIZONTAL 0x1 #define VERTICAL 0x2 gint tileactions[MAX_SEGS][MAX_SEGS]; /* What actions buttons toggled */ gint do_horz = FALSE; gint do_vert = FALSE; gint opacity = 100; /* Stuff for the preview bit */ static gint sel_x1, sel_y1, sel_x2, sel_y2; static gint sel_width, sel_height; static gint preview_width, preview_height; static gint has_alpha; MAIN () static void query () { static GParamDef args[] = { { PARAM_INT32, "run_mode", "Interactive, non-interactive" }, { PARAM_IMAGE, "image", "Input image (unused)" }, { PARAM_DRAWABLE, "drawable", "Input drawable" }, { PARAM_INT32, "number_of_tiles", "Number of tiles to make" } }; static GParamDef *return_vals = NULL; static int nargs = sizeof (args) / sizeof (args[0]); static int nreturn_vals = 0; gimp_install_procedure ("plug_in_small_tiles", "Tiles image into smaller versions of the orginal", "More here later", "Andy Thomas", "Andy Thomas", "1997", "/Filters/Map/Small Tiles", "RGB*, GRAY*", 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; int pwidth, pheight; run_mode = param[0].data.d_int32; *nreturn_vals = 1; *return_vals = values; values[0].type = PARAM_STATUS; values[0].data.d_status = status; tileitdrawable = drawable = gimp_drawable_get (param[2].data.d_drawable); tile_width = gimp_tile_width(); tile_height = gimp_tile_height(); gimp_drawable_mask_bounds(drawable->id, &sel_x1, &sel_y1, &sel_x2, &sel_y2); sel_width = sel_x2 - sel_x1; sel_height = sel_y2 - sel_y1; /* Calculate preview size */ if (sel_width > sel_height) { pwidth = MIN(sel_width, PREVIEW_SIZE); pheight = sel_height * pwidth / sel_width; } else { pheight = MIN(sel_height, PREVIEW_SIZE); pwidth = sel_width * pheight / sel_height; } preview_width = MAX(pwidth, 2); /* Min size is 2 */ preview_height = MAX(pheight, 2); switch (run_mode) { case RUN_INTERACTIVE: gimp_get_data ("plug_in_tileit", &itvals); if (! tileit_dialog()) { gimp_drawable_detach (drawable); return; } break; case RUN_NONINTERACTIVE: if (nparams != 4) status = STATUS_CALLING_ERROR; if (status == STATUS_SUCCESS) { itvals.numtiles = param[3].data.d_int32; } break; case RUN_WITH_LAST_VALS: gimp_get_data ("plug_in_tileit", &itvals); break; default: break; } if (gimp_drawable_color (drawable->id) || gimp_drawable_gray (drawable->id)) { /* Set the tile cache size */ gimp_tile_cache_ntiles((drawable->width + gimp_tile_width() - 1) / gimp_tile_width()); gimp_progress_init ("Tiling ..."); do_tiles(); if (run_mode != RUN_NONINTERACTIVE) gimp_displays_flush (); if (run_mode == RUN_INTERACTIVE) gimp_set_data ("plug_in_tileit", &itvals, sizeof (TileItVals)); } else { status = STATUS_EXECUTION_ERROR; } values[0].data.d_status = status; gimp_drawable_detach (drawable); } /* Build the dialog up. This was the hard part! */ static gint tileit_dialog () { GtkWidget *dlg; GtkWidget *button; GtkWidget *frame; GtkWidget *xframe; GtkWidget *table; GtkWidget *table2; GtkWidget *table3; GtkWidget *table4; GtkWidget *label; GtkWidget *entry; GtkWidget *slider; GtkObject *size_data; GtkObject *op_data; GtkWidget *toggle; GSList *orientation_group = NULL; guchar *color_cube; gchar **argv; gint argc; char buf[256]; argc = 1; argv = g_new (gchar *, 1); argv[0] = g_strdup ("tileit"); gtk_init (&argc, &argv); gtk_rc_parse (gimp_gtkrc ()); /* Get the stuff for the preview window...*/ 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()); cache_preview(); /* Get the preview image and store it also set has_alpha */ /* Start buildng the dialog up */ dlg = gtk_dialog_new (); gtk_window_set_title (GTK_WINDOW (dlg), "TileIt"); gtk_window_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE); gtk_signal_connect (GTK_OBJECT (dlg), "destroy", (GtkSignalFunc) tileit_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) tileit_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); /* Start building the frame for the preview area */ frame = gtk_frame_new ("preview"); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); gtk_container_border_width (GTK_CONTAINER (frame), 1); table = gtk_table_new (6, 6, FALSE); gtk_container_border_width (GTK_CONTAINER (table), 1); gtk_container_add (GTK_CONTAINER (frame), table); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0); tint.preview = gtk_preview_new(GTK_PREVIEW_COLOR); gtk_widget_set_events( GTK_WIDGET(tint.preview), PREVIEW_MASK ); gtk_signal_connect_after( GTK_OBJECT(tint.preview), "expose_event", (GtkSignalFunc) tileit_preview_expose, NULL); gtk_signal_connect( GTK_OBJECT(tint.preview), "event", (GtkSignalFunc) tileit_preview_events, NULL); gtk_preview_size(GTK_PREVIEW(tint.preview), preview_width, preview_height); xframe = gtk_frame_new(NULL); gtk_frame_set_shadow_type (GTK_FRAME (xframe), GTK_SHADOW_IN); gtk_container_add(GTK_CONTAINER(xframe), tint.preview); gtk_widget_show(xframe); gtk_table_attach(GTK_TABLE(table), xframe, 0, 1, 0, 2, GTK_EXPAND , GTK_EXPAND, 0, 0); gtk_widget_show(frame); gtk_widget_show(table); gtk_container_add(GTK_CONTAINER(frame), xframe); /* Area for buttons etc */ /* This was built up incrementally... shows does'nt it */ frame = gtk_frame_new("Flipping"); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); gtk_container_border_width (GTK_CONTAINER (frame), 1); table2 = gtk_table_new (7, 7, FALSE); gtk_container_border_width (GTK_CONTAINER (table2), 1); gtk_container_add (GTK_CONTAINER (frame), table2); toggle = gtk_check_button_new_with_label ("Horizontal"); gtk_table_attach (GTK_TABLE (table2), toggle, 0, 1, 1, 2, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", (GtkSignalFunc) tileit_hvtoggle_update, &do_horz); gtk_widget_show (toggle); res_call.htoggle = toggle; toggle = gtk_check_button_new_with_label ("Vertical"); gtk_table_attach (GTK_TABLE (table2), toggle, 1, 2, 1, 2, GTK_EXPAND , GTK_EXPAND , 0, 0); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", (GtkSignalFunc) tileit_hvtoggle_update, &do_vert); gtk_widget_show (toggle); res_call.vtoggle = toggle; xframe = gtk_frame_new("Applied to tile"); gtk_frame_set_shadow_type (GTK_FRAME (xframe), GTK_SHADOW_ETCHED_IN); gtk_container_border_width (GTK_CONTAINER (xframe), 10); gtk_table_attach (GTK_TABLE (table2), xframe, 0, 2, 2, 3, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); gtk_widget_show(xframe); /* Table for the inner widgets..*/ table4 = gtk_table_new (6, 6, FALSE); gtk_container_border_width (GTK_CONTAINER (table4), 10); gtk_container_add (GTK_CONTAINER (xframe), table4); gtk_widget_show(table4); toggle = gtk_radio_button_new_with_label (orientation_group,"All tiles"); orientation_group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle)); gtk_table_attach (GTK_TABLE (table4), toggle, 0, 3, 0, 1, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", (GtkSignalFunc) tileit_toggle_update, (gpointer)ALL); gtk_widget_show (toggle); toggle = gtk_radio_button_new_with_label (orientation_group,"Alternate tiles"); orientation_group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle)); gtk_table_attach (GTK_TABLE (table4), toggle, 0, 3, 1, 2, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", (GtkSignalFunc) tileit_toggle_update, (gpointer)ALT); gtk_widget_show (toggle); toggle = gtk_radio_button_new_with_label (orientation_group,"Explict tile"); orientation_group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle)); gtk_table_attach (GTK_TABLE (table4), toggle, 0, 1, 2, 3, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); gtk_widget_show (toggle); /* Table for the stuff next to the explict button */ table3 = gtk_table_new (6, 6, FALSE); gtk_container_border_width (GTK_CONTAINER (table3), 0); gtk_container_add (GTK_CONTAINER (xframe), table3); label = gtk_label_new ("Row"); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_widget_show(label); gtk_table_attach (GTK_TABLE (table3), label, 0 , 1, 0, 1, GTK_FILL | GTK_EXPAND , GTK_FILL, 1, 1); gtk_widget_set_sensitive(label,FALSE); exp_call.r_label = label; entry = gtk_entry_new(); gtk_widget_set_usize(entry, ENTRY_WIDTH, 0); sprintf(buf, "%.1d", 2); gtk_entry_set_text(GTK_ENTRY(entry), buf); gtk_widget_show (entry); gtk_table_attach (GTK_TABLE (table3), entry, 2 , 3, 0, 1, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); gtk_widget_set_sensitive(entry,FALSE); gtk_signal_connect(GTK_OBJECT(entry), "changed", (GtkSignalFunc) tileit_exp_update_f, &exp_call); exp_call.r_entry = entry; label = gtk_label_new ("Column"); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_widget_show(label); gtk_table_attach (GTK_TABLE (table3), label, 0 , 1, 1, 2, GTK_FILL , GTK_FILL, 1, 1); gtk_widget_set_sensitive(label,FALSE); exp_call.c_label = label; entry = gtk_entry_new(); gtk_widget_set_usize(entry, ENTRY_WIDTH, 0); sprintf(buf, "%.1d", 2); gtk_entry_set_text(GTK_ENTRY(entry), buf); gtk_widget_show (entry); gtk_widget_set_sensitive(entry,FALSE); gtk_table_attach (GTK_TABLE (table3), entry, 2 , 3, 1, 2, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); gtk_signal_connect(GTK_OBJECT(entry), "changed", (GtkSignalFunc) tileit_exp_update_f, &exp_call); exp_call.c_entry = entry; gtk_signal_connect (GTK_OBJECT (toggle), "toggled", (GtkSignalFunc) tileit_toggle_update, (gpointer)EXPLICT); button = gtk_button_new_with_label ("Apply"); gtk_widget_set_sensitive(button,FALSE); gtk_table_attach (GTK_TABLE (table3), button, 3, 4, 0, 3, 0, 0, 1, 1); gtk_widget_show (button); gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) tileit_exp_update, (gpointer)&exp_call); exp_call.applybut = button; /* Widget for selecting the Opacity */ sprintf(buf,"Opacity: "); label = gtk_label_new (buf); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_table_attach (GTK_TABLE (table4), label, 0, 1, 3, 4, 0, 0, 0, 0); gtk_widget_show (label); op_data = gtk_adjustment_new (100, 0, 100, 1, 1, 0); slider = gtk_hscale_new (GTK_ADJUSTMENT (op_data)); gtk_widget_set_usize (slider, SCALE_WIDTH, 0); gtk_table_attach (GTK_TABLE (table4), slider, 1,3 , 3, 4, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_LEFT); gtk_scale_set_digits (GTK_SCALE (slider), 0); gtk_range_set_update_policy (GTK_RANGE (slider),GTK_UPDATE_CONTINUOUS ); gtk_signal_connect (GTK_OBJECT (op_data), "value_changed", (GtkSignalFunc) tileit_scale_update, &opacity); if(!has_alpha) gtk_widget_set_sensitive(slider,FALSE); gtk_widget_show (slider); entry = gtk_entry_new(); gtk_object_set_user_data(GTK_OBJECT(entry), op_data); gtk_object_set_user_data(op_data, entry); gtk_widget_set_usize(entry, 3*ENTRY_WIDTH/2, 0); sprintf(buf, "%.1d", opacity); gtk_entry_set_text(GTK_ENTRY(entry), buf); gtk_signal_connect(GTK_OBJECT(entry), "changed", (GtkSignalFunc) tileit_entry_update, &opacity); gtk_table_attach(GTK_TABLE(table4), entry, 3, 4, 3, 4, GTK_FILL, GTK_FILL, 0, 0); if(!has_alpha) gtk_widget_set_sensitive(entry,FALSE); gtk_widget_show(entry); gtk_widget_show(table3); gtk_table_attach (GTK_TABLE (table4), table3, 1, 2, 2, 3, GTK_FILL , GTK_FILL, 0, 0); button = gtk_button_new_with_label ("Reset"); gtk_table_attach (GTK_TABLE (table2), button, 0, 2, 5, 6, 0 , 0, 0, 0); gtk_widget_show (button); gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) tileit_reset, (gpointer)&res_call); gtk_widget_show(frame); gtk_widget_show(table2); gtk_table_attach(GTK_TABLE(table), frame, 1, 2, 0, 2, GTK_EXPAND , GTK_EXPAND, 0, 0); /* Lower frame saying howmany segments */ frame = gtk_frame_new ("Segment Setting"); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); gtk_container_border_width (GTK_CONTAINER (frame), 1); table = gtk_table_new (5, 5, FALSE); gtk_container_border_width (GTK_CONTAINER (table), 1); gtk_container_add (GTK_CONTAINER (frame), table); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0); sprintf(buf,"1/(%d**n) ",2); label = gtk_label_new (buf); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2, GTK_EXPAND, GTK_FILL, 0, 0); gtk_widget_show (label); size_data = gtk_adjustment_new (itvals.numtiles, 2, MAX_SEGS, 1, 1, 0); slider = gtk_hscale_new (GTK_ADJUSTMENT (size_data)); gtk_widget_set_usize (slider, SCALE_WIDTH, 0); gtk_table_attach (GTK_TABLE (table), slider, 2,3 , 1, 2, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_LEFT); gtk_scale_set_digits (GTK_SCALE (slider), 0); gtk_range_set_update_policy (GTK_RANGE (slider),GTK_UPDATE_CONTINUOUS ); gtk_signal_connect (GTK_OBJECT (size_data), "value_changed", (GtkSignalFunc) tileit_scale_update, &itvals.numtiles); gtk_widget_show (slider); entry = gtk_entry_new(); gtk_object_set_user_data(GTK_OBJECT(entry), size_data); gtk_object_set_user_data(size_data, entry); gtk_widget_set_usize(entry, ENTRY_WIDTH, 0); sprintf(buf, "%.1d", itvals.numtiles); gtk_entry_set_text(GTK_ENTRY(entry), buf); gtk_signal_connect(GTK_OBJECT(entry), "changed", (GtkSignalFunc) tileit_entry_update, &itvals.numtiles); gtk_table_attach(GTK_TABLE(table), entry, 3, 4, 1, 2, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show(entry); gtk_widget_show(frame); gtk_widget_show(table); gtk_widget_show(tint.preview); gtk_widget_show (dlg); dialog_update_preview(); gtk_main (); gdk_flush (); return tint.run; } static void tileit_close_callback (GtkWidget *widget, gpointer data) { gtk_main_quit (); } static void tileit_ok_callback (GtkWidget *widget, gpointer data) { tint.run = TRUE; gtk_widget_destroy (GTK_WIDGET (data)); } static void tileit_hvtoggle_update(GtkWidget *widget, gpointer data) { int *toggle_val; toggle_val = (int *) data; if (GTK_TOGGLE_BUTTON (widget)->active) { /* Only do for event that sets a toggle button to true */ /* This will break if any more toggles are added? */ *toggle_val = TRUE; } else *toggle_val = FALSE; switch(exp_call.type) { case ALL: /* Clear current settings */ memset(tileactions,0,sizeof(tileactions)); all_update(); break; case ALT: /* Clear current settings */ memset(tileactions,0,sizeof(tileactions)); alt_update(); break; case EXPLICT: break; } dialog_update_preview(); } static void draw_explict_sel(void) { if(exp_call.type == EXPLICT) { gdouble x,y; gdouble width = (gdouble)preview_width/(gdouble)itvals.numtiles; gdouble height = (gdouble)preview_height/(gdouble)itvals.numtiles; x = width*(exp_call.x - 1); y = height*(exp_call.y - 1); gdk_gc_set_function ( tint.preview->style->black_gc, GDK_INVERT); gdk_draw_rectangle(tint.preview->window, tint.preview->style->black_gc, 0, (gint)x, (gint)y, (gint)width, (gint)height); gdk_draw_rectangle(tint.preview->window, tint.preview->style->black_gc, 0, (gint)x+1, (gint)y+1, (gint)width-2, (gint)height-2); gdk_draw_rectangle(tint.preview->window, tint.preview->style->black_gc, 0, (gint)x+2, (gint)y+2, (gint)width-4, (gint)height-4); gdk_gc_set_function ( tint.preview->style->black_gc, GDK_COPY); } } static gint tileit_preview_expose( GtkWidget *widget, GdkEvent *event ) { draw_explict_sel(); return FALSE; } static void exp_need_update(gint nx, gint ny) { gchar buf[256]; if (nx <= 0 || nx > itvals.numtiles || ny <= 0 || ny > itvals.numtiles) return; if( nx != exp_call.x || ny != exp_call.y ) { draw_explict_sel(); /* Clear old 'un */ exp_call.x = nx; exp_call.y = ny; draw_explict_sel(); sprintf(buf,"%d",nx); gtk_signal_handler_block_by_data(GTK_OBJECT(exp_call.c_entry),&exp_call); gtk_entry_set_text(GTK_ENTRY(exp_call.c_entry), buf); gtk_signal_handler_unblock_by_data(GTK_OBJECT(exp_call.c_entry), &exp_call); sprintf(buf,"%d",ny); gtk_signal_handler_block_by_data(GTK_OBJECT(exp_call.r_entry),&exp_call); gtk_entry_set_text(GTK_ENTRY(exp_call.r_entry), buf); gtk_signal_handler_unblock_by_data(GTK_OBJECT(exp_call.r_entry), &exp_call); } } static gint tileit_preview_events ( GtkWidget *widget, GdkEvent *event ) { GdkEventButton *bevent; GdkEventMotion *mevent; gint nx,ny; gint twidth = preview_width/itvals.numtiles; gint theight = preview_height/itvals.numtiles; switch (event->type) { case GDK_EXPOSE: break; case GDK_BUTTON_PRESS: bevent = (GdkEventButton *) event; nx = bevent->x/twidth + 1; ny = bevent->y/theight + 1; exp_need_update(nx,ny); break; case GDK_MOTION_NOTIFY: mevent = (GdkEventMotion *) event; if ( !mevent->state ) break; if(mevent->x < 0 || mevent->y < 0) break; nx = mevent->x/twidth + 1; ny = mevent->y/theight + 1; exp_need_update(nx,ny); break; default: break; } return FALSE; } static void explict_update(gint settile) { int x,y; /* Make sure bounds are OK */ y = atoi(gtk_entry_get_text(GTK_ENTRY(exp_call.r_entry))); if(y > itvals.numtiles || y <= 0) { y = itvals.numtiles; } x = atoi(gtk_entry_get_text(GTK_ENTRY(exp_call.c_entry))); if(x > itvals.numtiles || x <= 0) { x = itvals.numtiles; } /* Set it */ if(settile == TRUE) tileactions[x-1][y-1] = (((do_horz)?HORIZONTAL:0)|((do_vert)?VERTICAL:0)); exp_call.x = x; exp_call.y = y; } static void all_update(void) { int x,y; for(x = 0 ; x < MAX_SEGS; x++) for(y = 0 ; y < MAX_SEGS; y++) tileactions[x][y] |= (((do_horz)?HORIZONTAL:0)|((do_vert)?VERTICAL:0)); } static void alt_update(void) { int x,y; for(x = 0 ; x < MAX_SEGS; x++) for(y = 0 ; y < MAX_SEGS; y++) if(!((x+y)%2)) tileactions[x][y] |= (((do_horz)?HORIZONTAL:0)|((do_vert)?VERTICAL:0)); } static void tileit_toggle_update(GtkWidget *widget, gpointer data) { AppliedTo type = (AppliedTo)data; if (GTK_TOGGLE_BUTTON (widget)->active) { switch(type) { case ALL: /* Clear current settings */ memset(tileactions,0,sizeof(tileactions)); all_update(); break; case ALT: /* Clear current settings */ memset(tileactions,0,sizeof(tileactions)); alt_update(); break; case EXPLICT: /* Make widget active */ gtk_widget_set_sensitive(exp_call.r_label,TRUE); gtk_widget_set_sensitive(exp_call.r_entry,TRUE); gtk_widget_set_sensitive(exp_call.c_label,TRUE); gtk_widget_set_sensitive(exp_call.c_entry,TRUE); gtk_widget_set_sensitive(exp_call.applybut,TRUE); explict_update(FALSE); break; } exp_call.type = type; } else { switch(type) { case ALL: break; case ALT: break; case EXPLICT: gtk_widget_set_sensitive(exp_call.r_label,FALSE); gtk_widget_set_sensitive(exp_call.r_entry,FALSE); gtk_widget_set_sensitive(exp_call.c_label,FALSE); gtk_widget_set_sensitive(exp_call.c_entry,FALSE); gtk_widget_set_sensitive(exp_call.applybut,FALSE); break; } } dialog_update_preview(); } static void tileit_scale_update(GtkAdjustment *adjustment, gint *value) { GtkWidget *entry; char buf[256]; if (*value != adjustment->value) { *value = adjustment->value; entry = gtk_object_get_user_data(GTK_OBJECT(adjustment)); sprintf(buf,"%d",*value); gtk_signal_handler_block_by_data(GTK_OBJECT(entry), value); gtk_entry_set_text(GTK_ENTRY(entry), buf); gtk_signal_handler_unblock_by_data(GTK_OBJECT(entry), value); dialog_update_preview(); } } static void tileit_reset(GtkWidget *widget, gpointer data) { Reset_Call *r = (Reset_Call *)data; memset(tileactions,0,sizeof(tileactions)); gtk_signal_handler_block_by_data(GTK_OBJECT(r->htoggle),&do_horz); gtk_signal_handler_block_by_data(GTK_OBJECT(r->vtoggle),&do_vert); gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(r->htoggle),FALSE); gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(r->vtoggle),FALSE); gtk_signal_handler_unblock_by_data(GTK_OBJECT(r->htoggle),&do_horz); gtk_signal_handler_unblock_by_data(GTK_OBJECT(r->vtoggle),&do_vert); do_horz = do_vert = FALSE; dialog_update_preview(); } /* Could avoid almost dup. functions by using a field in the data * passed. Must still pass the data since used in sig blocking func. */ static void tileit_exp_update(GtkWidget *widget, gpointer applied) { explict_update(TRUE); dialog_update_preview(); } static void tileit_exp_update_f(GtkWidget *widget, gpointer applied) { explict_update(FALSE); dialog_update_preview(); } static void tileit_entry_update(GtkWidget *widget, gint *value) { GtkAdjustment *adjustment; gdouble new_value; new_value = atoi(gtk_entry_get_text(GTK_ENTRY(widget))); if (*value != new_value) { adjustment = gtk_object_get_user_data(GTK_OBJECT(widget)); if ((new_value >= adjustment->lower) && (new_value <= adjustment->upper)) { *value = new_value; adjustment->value = new_value; gtk_signal_emit_by_name(GTK_OBJECT(adjustment), "value_changed"); dialog_update_preview(); } } } /* Cache the preview image - updates are a lot faster. */ /* The preview_cache will contain the small image */ static void cache_preview() { GPixelRgn src_rgn; int y,x; guchar *src_rows; guchar *p; int isgrey = 0; gimp_pixel_rgn_init(&src_rgn,tileitdrawable,sel_x1,sel_y1,sel_width,sel_height,FALSE,FALSE); src_rows = g_new(guchar ,sel_width*4); p = tint.pv_cache = g_new(guchar ,preview_width*preview_height*4); img_width = gimp_drawable_width(tileitdrawable->id); img_height = gimp_drawable_height(tileitdrawable->id); tint.img_bpp = gimp_drawable_bpp(tileitdrawable->id); has_alpha = gimp_drawable_has_alpha(tileitdrawable->id); if(tint.img_bpp < 3) { tint.img_bpp = 3 + has_alpha; } switch ( gimp_drawable_type (tileitdrawable->id) ) { case GRAYA_IMAGE: case GRAY_IMAGE: isgrey = 1; break; default: isgrey = 0; break; } for (y = 0; y < preview_height; y++) { gimp_pixel_rgn_get_row(&src_rgn, src_rows, sel_x1, sel_y1 + (y*sel_height)/preview_height, sel_width); for (x = 0; x < (preview_width); x ++) { /* Get the pixels of each col */ int i; for (i = 0 ; i < 3; i++ ) p[x*tint.img_bpp+i] = src_rows[((x*sel_width)/preview_width)*src_rgn.bpp +((isgrey)?0:i)]; if(has_alpha) p[x*tint.img_bpp+3] = src_rows[((x*sel_width)/preview_width)*src_rgn.bpp + ((isgrey)?1:3)]; } p += (preview_width*tint.img_bpp); } g_free(src_rows); } static void tileit_get_pixel(int x, int y, guchar *pixel) { static gint row = -1; static gint col = -1; gint newcol, newrow; gint newcoloff, newrowoff; guchar *p; int i; if ((x < 0) || (x >= img_width) || (y < 0) || (y >= img_height)) { pixel[0] = 0; pixel[1] = 0; pixel[2] = 0; pixel[3] = 0; return; } newcol = x / tile_width; newcoloff = x % tile_width; newrow = y / tile_height; newrowoff = y % tile_height; if ((col != newcol) || (row != newrow) || (the_tile == NULL)) { if (the_tile != NULL) gimp_tile_unref(the_tile, FALSE); the_tile = gimp_drawable_get_tile(tileitdrawable, FALSE, newrow, newcol); gimp_tile_ref(the_tile); col = newcol; row = newrow; } p = the_tile->data + the_tile->bpp * (the_tile->ewidth * newrowoff + newcoloff); for (i = img_bpp; i; i--) *pixel++ = *p++; } static void do_tiles(void) { GPixelRgn dest_rgn; gpointer pr; gint progress, max_progress; guchar *dest_row; guchar *dest; gint row, col; guchar pixel[4]; int nc,nr; int i; /* Initialize pixel region */ gimp_pixel_rgn_init(&dest_rgn, tileitdrawable, sel_x1, sel_y1, sel_width, sel_height, TRUE, TRUE); progress = 0; max_progress = sel_width * sel_height; img_bpp = gimp_drawable_bpp(tileitdrawable->id); for (pr = gimp_pixel_rgns_register(1, &dest_rgn); pr != NULL; pr = gimp_pixel_rgns_process(pr)) { dest_row = dest_rgn.data; for (row = dest_rgn.y; row < (dest_rgn.y + dest_rgn.h); row++) { dest = dest_row; for (col = dest_rgn.x; col < (dest_rgn.x + dest_rgn.w); col++) { int an_action; an_action = tiles_xy(sel_width, sel_height, col-sel_x1,row-sel_y1, &nc,&nr); tileit_get_pixel(nc+sel_x1,nr+sel_y1,pixel); for (i = 0; i < img_bpp; i++) *dest++ = pixel[i]; if(an_action && has_alpha) { dest--; *dest = ((*dest)*opacity)/100; dest++; } } dest_row += dest_rgn.rowstride; } progress += dest_rgn.w * dest_rgn.h; gimp_progress_update((double) progress / max_progress); } if (the_tile != NULL) { gimp_tile_unref(the_tile, FALSE); the_tile = NULL; } gimp_drawable_flush(tileitdrawable); gimp_drawable_merge_shadow(tileitdrawable->id, TRUE); gimp_drawable_update(tileitdrawable->id, sel_x1, sel_y1, sel_width, sel_height); } /* Get the xy pos and any action */ static gint tiles_xy(gint width, gint height, gint x, gint y, gint *nx, gint *ny) { gint px,py; gint rnum,cnum; gint actiontype; gdouble rnd = 1 - (1.0/(gdouble)itvals.numtiles) +0.01; rnum = y*itvals.numtiles/height; py = (y*itvals.numtiles)%height; px = (x*itvals.numtiles)%width; cnum = x*itvals.numtiles/width; if((actiontype = tileactions[cnum][rnum])) { if(actiontype & HORIZONTAL) { gdouble pyr; pyr = height - y - 1 + rnd; py = ((int)(pyr*(gdouble)itvals.numtiles))%height; } if(actiontype & VERTICAL) { gdouble pxr; pxr = width - x - 1 + rnd; px = ((int)(pxr*(gdouble)itvals.numtiles))%width; } } *nx = px; *ny = py; return(actiontype); } /* Given a row then srink it down a bit */ static void do_tiles_preview(guchar *dest_row, guchar *src_rows, gint width, gint dh, gint height, gint bpp) { gint x; gint i; gint px,py; gint rnum,cnum; gint actiontype; gdouble rnd = 1 - (1.0/(gdouble)itvals.numtiles) +0.01; rnum = dh*itvals.numtiles/height; for (x = 0; x < width; x ++) { py = (dh*itvals.numtiles)%height; px = (x*itvals.numtiles)%width; cnum = x*itvals.numtiles/width; if((actiontype = tileactions[cnum][rnum])) { if(actiontype & HORIZONTAL) { gdouble pyr; pyr = height - dh - 1 + rnd; py = ((int)(pyr*(gdouble)itvals.numtiles))%height; } if(actiontype & VERTICAL) { gdouble pxr; pxr = width - x - 1 + rnd; px = ((int)(pxr*(gdouble)itvals.numtiles))%width; } } for (i = 0 ; i < bpp; i++ ) dest_row[x*tint.img_bpp+i] = src_rows[(px + (py*width))*bpp+i]; if(has_alpha && actiontype) dest_row[x*tint.img_bpp + (bpp - 1)] = (dest_row[x*tint.img_bpp + (bpp - 1)]*opacity)/100; } } static void dialog_update_preview(void) { int y; gint check,check_0,check_1; for (y = 0; y < preview_height; y++) { if ((y / CHECK_SIZE) & 1) { check_0 = CHECK_DARK; check_1 = CHECK_LIGHT; } else { check_0 = CHECK_LIGHT; check_1 = CHECK_DARK; } do_tiles_preview(tint.preview_row, tint.pv_cache, preview_width, y, preview_height, tint.img_bpp); if(tint.img_bpp > 3) { int i,j; for (i = 0, j = 0 ; i < sizeof(tint.preview_row); i += 4, j += 3 ) { gint alphaval; if (((i/4) / CHECK_SIZE) & 1) check = check_0; else check = check_1; alphaval = tint.preview_row[i + 3]; tint.preview_row[j] = check + (((tint.preview_row[i] - check)*alphaval)/255); tint.preview_row[j + 1] = check + (((tint.preview_row[i + 1] - check)*alphaval)/255); tint.preview_row[j + 2] = check + (((tint.preview_row[i + 2] - check)*alphaval)/255); } } gtk_preview_draw_row(GTK_PREVIEW(tint.preview), tint.preview_row, 0, y, preview_width); } draw_explict_sel(); gtk_widget_draw(tint.preview, NULL); gdk_flush(); }