/* * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * This is a plug-in for the GIMP. * * Blinds plug-in. Distort an image as though it was stuck to * window blinds and the blinds where opened/closed. * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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:- * * Version 0.5 10 June 1997. * Changes required to work with 0.99.10. * * Version 0.4 20 May 1997. * Fixed problem with using this plugin in RUN_NONINTERACTIVE mode * * Version 0.3 8 May 1997. * Make preview work in Quartics words "The Right Way". * * Allow the background to be transparent. * * Version 0.2 1 May 1997 (not released). * Added patches supplied by Tim Mooney mooney@dogbert.cc.ndsu.NoDak.edu * to allow the plug-in to build with Digitals compiler. */ #include #include #include #include #include "gtk/gtk.h" #include "libgimp/gimp.h" #ifndef M_PI #define M_PI 3.14159265358979323846 #endif /* M_PI */ /***** Magic numbers *****/ /* Don't make preview >255!!! It won't work for horizontal blinds */ #define PREVIEW_SIZE 128 #define SCALE_WIDTH 150 #define ENTRY_WIDTH 30 /* 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_FANS 10 #define HORIZONTAL 0 #define VERTICAL 1 /* Variables set in dialog box */ typedef struct data { gint angledsp; gint numsegs; gint orientation; gint bg_trans; } BlindVals; typedef struct { GtkWidget *preview; guchar preview_row[PREVIEW_SIZE * 4]; gint run; guchar * pv_cache; gint img_bpp; } BlindsInterface; static BlindsInterface bint = { NULL, /* Preview */ {'4','u'}, /* Preview_row */ FALSE, /* run */ NULL, 4 /* bpp of drawable */ }; /* Array to hold each size of fans. And no there are not each the * same size (rounding errors...) */ int fanwidths[MAX_FANS]; GDrawable *blindsdrawable; static void query (void); static void run (gchar *name, gint nparams, GParam *param, gint *nreturn_vals, GParam **return_vals); static gint blinds_dialog (void); static void blinds_close_callback (GtkWidget *widget, gpointer data); static void blinds_ok_callback (GtkWidget *widget, gpointer data); static void blinds_scale_update (GtkAdjustment *adjustment, gint *size_val); static void blinds_entry_update(GtkWidget *widget, gint *value); static void blinds_toggle_update (GtkWidget *widget, gpointer data); static void blinds_button_update (GtkWidget *widget, gpointer data); static void dialog_update_preview(void); static void cache_preview(void); static void apply_blinds(void); static int blinds_get_bg(guchar *bg); GPlugInInfo PLUG_IN_INFO = { NULL, /* init_proc */ NULL, /* quit_proc */ query, /* query_proc */ run, /* run_proc */ }; /* Values when first invoked */ static BlindVals bvals = { 30, 3, HORIZONTAL, FALSE }; /* 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 do_horizontal; static gint do_vertical; static gint do_trans; 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, "angle_dsp", "Angle of Displacement " }, { PARAM_INT32, "number_of_segments", "Number of segments in blinds" }, { PARAM_INT32, "orientation", "orientation; 0 = Horizontal, 1 = Vertical" }, { PARAM_INT32, "backgndg_trans", "background transparent; FALSE,TRUE" } }; static GParamDef *return_vals = NULL; static int nargs = sizeof (args) / sizeof (args[0]); static int nreturn_vals = 0; gimp_install_procedure ("plug_in_blinds", "Adds a blinds effect to the image. Rather like putting the image on a set of window blinds and the closing or opening the blinds", "More here later", "Andy Thomas", "Andy Thomas", "1997", "/Filters/Distorts/Blinds", "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; blindsdrawable = drawable = gimp_drawable_get (param[2].data.d_drawable); 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_blinds", &bvals); if (! blinds_dialog()) { gimp_drawable_detach (drawable); return; } break; case RUN_NONINTERACTIVE: if (nparams != 7) status = STATUS_CALLING_ERROR; if (status == STATUS_SUCCESS) { bvals.angledsp = param[3].data.d_int32; bvals.numsegs = param[4].data.d_int32; bvals.orientation = param[5].data.d_int32; bvals.bg_trans = param[6].data.d_int32; } break; case RUN_WITH_LAST_VALS: gimp_get_data ("plug_in_blinds", &bvals); break; default: break; } if (gimp_drawable_color (drawable->id) || gimp_drawable_gray (drawable->id)) { gimp_progress_init ("Adding Blinds ..."); apply_blinds(); if (run_mode != RUN_NONINTERACTIVE) gimp_displays_flush (); if (run_mode == RUN_INTERACTIVE) gimp_set_data ("plug_in_blinds", &bvals, sizeof (BlindVals)); } 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 blinds_dialog () { GtkWidget *dlg; GtkWidget *button; GtkWidget *frame; GtkWidget *xframe; GtkWidget *table; GtkWidget *label; GtkWidget *entry; GtkWidget *slider; GtkObject *size_data; GtkWidget *toggle_vbox; 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 ("blinds"); do_horizontal = (bvals.orientation == HORIZONTAL); do_vertical = (bvals.orientation == VERTICAL); do_trans = (bvals.bg_trans == TRUE); 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 */ dlg = gtk_dialog_new (); gtk_window_set_title (GTK_WINDOW (dlg), "Blinds"); gtk_window_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE); gtk_signal_connect (GTK_OBJECT (dlg), "destroy", (GtkSignalFunc) blinds_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) blinds_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), 10); table = gtk_table_new (5, 5, FALSE); gtk_container_border_width (GTK_CONTAINER (table), 10); gtk_container_add (GTK_CONTAINER (frame), table); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0); bint.preview = gtk_preview_new(GTK_PREVIEW_COLOR); gtk_preview_size(GTK_PREVIEW(bint.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), bint.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); /* ALT-I */ gtk_widget_show(table); gtk_widget_show(bint.preview); frame = gtk_frame_new ("Orientation"); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); gtk_table_attach (GTK_TABLE (table), frame, 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 5, 5); toggle_vbox = gtk_vbox_new (FALSE, 5); gtk_container_border_width (GTK_CONTAINER (toggle_vbox), 5); gtk_container_add (GTK_CONTAINER (frame), toggle_vbox); toggle = gtk_radio_button_new_with_label (orientation_group, "Horizontal"); orientation_group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle)); gtk_box_pack_start (GTK_BOX (toggle_vbox), toggle, FALSE, FALSE, 0); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", (GtkSignalFunc) blinds_toggle_update, &do_horizontal); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), do_horizontal); gtk_widget_show (toggle); toggle = gtk_radio_button_new_with_label (orientation_group, "Vertical"); orientation_group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle)); gtk_box_pack_start (GTK_BOX (toggle_vbox), toggle, FALSE, FALSE, 0); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", (GtkSignalFunc) blinds_toggle_update, &do_vertical); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), do_vertical); gtk_widget_show (toggle); gtk_widget_show (toggle_vbox); gtk_widget_show(frame); frame = gtk_frame_new ("Background"); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); gtk_table_attach (GTK_TABLE (table), frame, 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 5, 5); toggle_vbox = gtk_vbox_new (FALSE, 5); gtk_container_border_width (GTK_CONTAINER (toggle_vbox), 5); gtk_container_add (GTK_CONTAINER (frame), toggle_vbox); toggle = gtk_check_button_new_with_label ("Transparent"); gtk_box_pack_start (GTK_BOX (toggle_vbox), toggle, FALSE, FALSE, 0); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", (GtkSignalFunc) blinds_button_update, &do_trans); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), do_trans); if(!has_alpha) { gtk_widget_set_sensitive(toggle,FALSE); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), FALSE); } gtk_widget_show (toggle); gtk_widget_show (toggle_vbox); gtk_widget_show(frame); frame = gtk_frame_new ("Parameter Settings"); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); gtk_container_border_width (GTK_CONTAINER (frame), 10); table = gtk_table_new (5, 5, FALSE); gtk_container_border_width (GTK_CONTAINER (table), 10); gtk_container_add (GTK_CONTAINER (frame), table); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0); label = gtk_label_new ("Displacement "); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2, GTK_FILL | GTK_EXPAND, GTK_FILL, 4, 0); gtk_widget_show (label); size_data = gtk_adjustment_new (bvals.angledsp, 1, 90, 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) blinds_scale_update, &bvals.angledsp); 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, "%.2d", bvals.angledsp); gtk_entry_set_text(GTK_ENTRY(entry), buf); gtk_signal_connect(GTK_OBJECT(entry), "changed", (GtkSignalFunc) blinds_entry_update, &bvals.angledsp); gtk_table_attach(GTK_TABLE(table), entry, 3, 4, 1, 2, GTK_FILL, GTK_FILL, 4, 0); gtk_widget_show(entry); label = gtk_label_new ("Num Segments "); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); gtk_widget_show (label); size_data = gtk_adjustment_new (bvals.numsegs, 1, MAX_FANS, 2, 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, 2, 3, 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) blinds_scale_update, &bvals.numsegs); 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, "%d", bvals.numsegs); gtk_entry_set_text(GTK_ENTRY(entry), buf); gtk_signal_connect(GTK_OBJECT(entry), "changed", (GtkSignalFunc) blinds_entry_update, &bvals.numsegs); gtk_table_attach(GTK_TABLE(table), entry, 3, 4, 2, 3, GTK_FILL, GTK_FILL, 4, 0); gtk_widget_show(entry); gtk_widget_show (frame); gtk_widget_show (table); gtk_widget_show (dlg); dialog_update_preview(); gtk_main (); gdk_flush (); /* determine orientation */ if (do_horizontal) bvals.orientation = HORIZONTAL; else bvals.orientation = VERTICAL; /* Use trans bg ? */ bvals.bg_trans = do_trans; return bint.run; } static void blinds_close_callback (GtkWidget *widget, gpointer data) { gtk_main_quit (); } static void blinds_ok_callback (GtkWidget *widget, gpointer data) { bint.run = TRUE; gtk_widget_destroy (GTK_WIDGET (data)); } static void blinds_toggle_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; dialog_update_preview(); } else *toggle_val = FALSE; } static void blinds_button_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; dialog_update_preview(); } static void blinds_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 blinds_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,blindsdrawable,sel_x1,sel_y1,sel_width,sel_height,FALSE,FALSE); src_rows = g_new(guchar ,sel_width*4); p = bint.pv_cache = g_new(guchar ,preview_width*preview_height*4); bint.img_bpp = gimp_drawable_bpp(blindsdrawable->id); has_alpha = gimp_drawable_has_alpha(blindsdrawable->id); if(bint.img_bpp < 3) { bint.img_bpp = 3 + has_alpha; } switch ( gimp_drawable_type (blindsdrawable->id) ) { case GRAYA_IMAGE: case GRAY_IMAGE: isgrey = 1; default: 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*bint.img_bpp+i] = src_rows[((x*sel_width)/preview_width)*src_rgn.bpp +((isgrey)?0:i)]; if(has_alpha) p[x*bint.img_bpp+3] = src_rows[((x*sel_width)/preview_width)*src_rgn.bpp + ((isgrey)?1:3)]; } p += (preview_width*bint.img_bpp); } g_free(src_rows); } static void blindsapply(guchar *srow,guchar *drow,gint width,gint bpp,guchar *bg) { guchar *src; guchar *dst; int i,j,k; double ang; int available; /* Make the row 'shrink' around points along its length */ /* The bvals.numsegs determins how many segments to slip it in to */ /* The angle is the conceptual 'rotation' of each of these segments */ /* Note the row is considered to be made up of a two dim array actual * pixel locations and the RGB colour at these locations. */ /* In the process copy the src row to the destination row */ /* Fill in with background color ? */ for (i = 0 ; i < width ; i++) { dst = &drow[i*bpp]; for (j = 0 ; j < bpp; j++) { dst[j] = bg[j]; } } /* Apply it */ available = width; for ( i = 0 ; i < bvals.numsegs; i++) { /* Width of segs are variable */ fanwidths[i] = available/(bvals.numsegs - i); available -= fanwidths[i]; } /* do center points first - just for fun...*/ available = 0; for (k = 1 ; k <= bvals.numsegs; k++) { int point; point = available + fanwidths[k-1]/2; available += fanwidths[k-1]; src = &srow[point*bpp]; dst = &drow[point*bpp]; /* Copy pixels across */ for (j = 0 ; j < bpp; j++) { dst[j] = src[j]; } } /* Disp for each point */ ang = (bvals.angledsp*2*M_PI)/360; /* Angle in rads */ ang = (1-fabs(cos(ang))); available = 0; for(k = 0 ; k < bvals.numsegs; k++) { int dx; /* Amount to move by */ int fw; for (i = 0 ; i < (fanwidths[k]/2) ; i++) { /* Copy pixels across of left half of fan */ fw = fanwidths[k]/2; dx = (int)(ang * ((double)(fw - (double)(i%fw)))); src = &srow[(available+i)*bpp]; dst = &drow[(available+i+dx)*bpp]; for (j = 0 ; j < bpp; j++) { dst[j] = src[j]; } /* Right side */ j = i + 1; src = &srow[(available + fanwidths[k] - j - (fanwidths[k]%2))*bpp]; dst = &drow[(available + fanwidths[k] - j - (fanwidths[k]%2) - dx)*bpp]; for (j = 0 ; j < bpp; j++) { dst[j] = src[j]; } } available += fanwidths[k]; } } static int blinds_get_bg(guchar *bg) { GParam *return_vals; gint nreturn_vals; /* Get the background color */ int retval = FALSE; /*Return TRUE if of GREYA type */ switch ( gimp_drawable_type (blindsdrawable->id) ) { case RGBA_IMAGE: bg[3] = (do_trans == TRUE)?0:255; break; case RGB_IMAGE : return_vals = gimp_run_procedure ("gimp_palette_get_foreground", &nreturn_vals, PARAM_END); return_vals = gimp_run_procedure ("gimp_palette_get_background", &nreturn_vals, PARAM_END); if (return_vals[0].data.d_status == STATUS_SUCCESS) { bg[0] = return_vals[1].data.d_color.red; bg[1] = return_vals[1].data.d_color.green; bg[2] = return_vals[1].data.d_color.blue; } else { bg[0] = 0; bg[1] = 0; bg[2] = 0; } break; case GRAYA_IMAGE: retval = TRUE; bg[3] = (do_trans == TRUE)?0:255; break; case GRAY_IMAGE: bg[2] = 0; bg[1] = 0; bg[0] = 0; break; default: break; } return retval; } static void dialog_update_preview(void) { int y; guchar *p; guchar bg[4]; gint check,check_0,check_1; p = bint.pv_cache; blinds_get_bg(bg); if(do_vertical) { for (y = 0; y < preview_height; y++) { /* bint.preview_row - this row contains the row to apply the action to */ blindsapply(p,bint.preview_row,preview_width,bint.img_bpp,bg); if ((y / CHECK_SIZE) & 1) { check_0 = CHECK_DARK; check_1 = CHECK_LIGHT; } else { check_0 = CHECK_LIGHT; check_1 = CHECK_DARK; } if(bint.img_bpp > 3) { int i,j; for (i = 0, j = 0 ; i < sizeof(bint.preview_row); i += 4, j += 3 ) { gint alphaval; if (((i/4) / CHECK_SIZE) & 1) check = check_0; else check = check_1; alphaval = bint.preview_row[i + 3]; bint.preview_row[j] = check + (((bint.preview_row[i] - check)*alphaval)/255); bint.preview_row[j + 1] = check + (((bint.preview_row[i + 1] - check)*alphaval)/255); bint.preview_row[j + 2] = check + (((bint.preview_row[i + 2] - check)*alphaval)/255); } } gtk_preview_draw_row(GTK_PREVIEW(bint.preview), bint.preview_row, 0, y, preview_width); p += preview_width*bint.img_bpp; } } else { /* Horizontal blinds */ /* Apply the blinds algo to a single column - * this act as a transfomation matrix for the * rows. Make row 0 invalid so we can find it again! */ int i; int loop1,loop2; guchar *sr = g_new(guchar,(preview_height)*4); guchar *dr = g_new(guchar,(preview_height)*4); guchar dummybg[4]; /* Copy into here after translation*/ guchar copy_row[PREVIEW_SIZE*4]; memset(dummybg,0,4); memset(dr,0,(preview_height)*4); /* all dr rows are background rows */ /* Fill in with background color ? */ for (i = 0 ; i < preview_width ; i++) { int j; int bd = bint.img_bpp; guchar *dst; dst = &bint.preview_row[i*bd]; for (j = 0 ; j < bd; j++) { dst[j] = bg[j]; } } for ( y = 0 ; y < preview_height; y++) { sr[y] = y+1; } /* Bit of a fiddle since blindsapply really works on an image * row not a set of bytes. - preview can't be > 255 * or must make dr sr int rows. */ blindsapply(sr,dr,preview_height,1,dummybg); 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; } if(dr[y] == 0) { /* Draw background line */ p = bint.preview_row; } else { /* Draw line from src */ p = bint.pv_cache + (preview_width*bint.img_bpp*(dr[y]-1)); } if(bint.img_bpp > 3) { /* Take account of alpha channel HERE*/ for (loop1 = 0, loop2 = 0 ; loop1 < preview_width*bint.img_bpp; loop1 += 4, loop2 += 3) { gint alphaval; if (((loop1/4) / CHECK_SIZE) & 1) check = check_0; else check = check_1; alphaval = p[loop1 + 3]; copy_row[loop2] = check + (((p[loop1] - check)*alphaval)/255); copy_row[loop2 + 1] = check + (((p[loop1 + 1] - check)*alphaval)/255); copy_row[loop2 + 2] = check + (((p[loop1 + 2] - check)*alphaval)/255); } p = ©_row[0]; } gtk_preview_draw_row(GTK_PREVIEW(bint.preview), p, 0, y, preview_width); } g_free(sr); g_free(dr); } gtk_widget_draw(bint.preview, NULL); gdk_flush(); } /* STEP tells us how many rows/columns to gulp down in one go... */ /* Note all the "4" literals around here are to do with the depths * of the images. Makes it easier to deal with for my small brain. */ #define STEP 40 static void apply_blinds(void) { GPixelRgn des_rgn; GPixelRgn src_rgn; guchar *src_rows,*des_rows; int x,y; guchar bg[4]; /* Adjust aplha channel if GREYA */ if(blinds_get_bg(bg) == TRUE) bg[1] = (do_trans == TRUE)?0:255; gimp_pixel_rgn_init(&src_rgn,blindsdrawable,sel_x1,sel_y1,sel_width,sel_height,FALSE,FALSE); gimp_pixel_rgn_init(&des_rgn,blindsdrawable,sel_x1,sel_y1,sel_width,sel_height,TRUE,TRUE); src_rows = g_new(guchar ,(MAX(sel_width,sel_height))*4*STEP); des_rows = g_new(guchar ,(MAX(sel_width,sel_height))*4*STEP); if(do_vertical) { for (y = 0; y < sel_height; y += STEP) { int rr; int step; if((y + STEP) > sel_height) step = sel_height - y; else step = STEP; gimp_pixel_rgn_get_rect(&src_rgn, src_rows, sel_x1, sel_y1 + y, sel_width, step); /* OK I could make this better */ for(rr = 0 ; rr < STEP; rr++) blindsapply(src_rows+(sel_width*rr*src_rgn.bpp), des_rows+(sel_width*rr*src_rgn.bpp), sel_width,src_rgn.bpp,bg); gimp_pixel_rgn_set_rect(&des_rgn, des_rows, sel_x1, sel_y1 + y, sel_width, step); gimp_progress_update((double)y/(double)sel_height); } } else { /* Horizontal blinds */ /* Apply the blinds algo to a single column - * this act as a transfomation matrix for the * rows. Make row 0 invalid so we can find it again! */ int i; gint *sr = g_new(gint,(sel_height)*4); gint *dr = g_new(gint,(sel_height)*4); guchar *dst = g_new(guchar,STEP*4); guchar dummybg[4]; memset(dummybg,0,4); memset(dr,0,(sel_height)*4); /* all dr rows are background rows */ for ( y = 0 ; y < sel_height; y++) { sr[y] = y+1; } /* Hmmm. does this work portably? */ /* This "swaps the intergers around that are held in in the * sr & dr arrays. */ blindsapply((guchar*)sr,(guchar*)dr,sel_height,sizeof(gint),dummybg); /* Fill in with background color ? */ for (i = 0 ; i < STEP ; i++) { int j; guchar *bgdst; bgdst = &dst[i*src_rgn.bpp]; for (j = 0 ; j < src_rgn.bpp; j++) { bgdst[j] = bg[j]; } } for (x = 0; x < sel_width; x += STEP) { int rr; int step; guchar *p; if((x + STEP) > sel_width) step = sel_width - x; else step = STEP; gimp_pixel_rgn_get_rect(&src_rgn, src_rows, sel_x1 + x, sel_y1, step, sel_height); /* OK I could make this better */ for(rr = 0 ; rr < sel_height; rr++) { if(dr[rr] == 0) { /* Draw background line */ p = dst; } else { /* Draw line from src */ p = src_rows + (step*src_rgn.bpp*(dr[rr]-1)); } memcpy(des_rows+(rr*step*src_rgn.bpp),p,step*src_rgn.bpp); } gimp_pixel_rgn_set_rect(&des_rgn, des_rows, sel_x1 + x, sel_y1, step, sel_height); gimp_progress_update((double)x/(double)sel_width); } g_free(dst); g_free(sr); g_free(dr); } g_free(src_rows); g_free(des_rows); gimp_drawable_flush(blindsdrawable); gimp_drawable_merge_shadow(blindsdrawable->id, TRUE); gimp_drawable_update(blindsdrawable->id, sel_x1, sel_y1, sel_width, sel_height); }