/* The GIMP -- an image manipulation program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * 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. */ #include #include #include #include "appenv.h" #include "buildmenu.h" #include "drawable.h" #include "errors.h" #include "gdisplay.h" #include "gimpbrushlist.h" #include "gradient.h" #include "paint_funcs.h" #include "paint_core.h" #include "palette.h" #include "paint_options.h" #include "paintbrush.h" #include "pixmapbrush.h" #include "selection.h" #include "tool_options_ui.h" #include "tools.h" #include "libgimp/gimpintl.h" /* defines */ #define PAINT_LEFT_THRESHOLD 0.05 /* defaults for the tool options */ #define PAINTBRUSH_DEFAULT_FADE_OUT 0.0 #define PAINTBRUSH_DEFAULT_INCREMENTAL FALSE #define PAINTBRUSH_DEFAULT_USE_GRADIENT FALSE #define PAINTBRUSH_DEFAULT_GRADIENT_LENGTH 10.0 #define PAINTBRUSH_DEFAULT_GRADIENT_TYPE LOOP_TRIANGLE /* the paintbrush structures */ typedef struct _PaintbrushOptions PaintbrushOptions; struct _PaintbrushOptions { PaintOptions paint_options; double fade_out; double fade_out_d; GtkObject *fade_out_w; int use_gradient; int use_gradient_d; GtkWidget *use_gradient_w; double gradient_length; double gradient_length_d; GtkObject *gradient_length_w; int gradient_type; int gradient_type_d; GtkWidget *gradient_type_w; gboolean incremental; gboolean incremental_d; GtkWidget *incremental_w; }; /* the paint brush tool options */ static PaintbrushOptions * paintbrush_options = NULL; /* local variables */ static double non_gui_fade_out; static double non_gui_gradient_length; static int non_gui_gradient_type; static double non_gui_incremental; /* forward function declarations */ static void paintbrush_motion (PaintCore *, GimpDrawable *, double, double, gboolean, int); /* functions */ static void paintbrush_gradient_toggle_callback (GtkWidget *widget, gpointer data) { PaintbrushOptions *options = paintbrush_options; static int incremental_save = FALSE; tool_options_toggle_update (widget, data); if (paintbrush_options->use_gradient) { incremental_save = options->incremental; gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->incremental_w), TRUE); } else { gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->incremental_w), incremental_save); } } static void paintbrush_gradient_type_callback (GtkWidget *widget, gpointer data) { if (paintbrush_options) paintbrush_options->gradient_type = (GradientPaintMode) data; } static void paintbrush_options_reset (void) { PaintbrushOptions *options = paintbrush_options; paint_options_reset ((PaintOptions *) options); gtk_adjustment_set_value (GTK_ADJUSTMENT (options->fade_out_w), options->fade_out_d); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->use_gradient_w), options->use_gradient_d); gtk_adjustment_set_value (GTK_ADJUSTMENT (options->gradient_length_w), options->gradient_length_d); gtk_option_menu_set_history (GTK_OPTION_MENU (options->gradient_type_w), options->gradient_type_d); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->incremental_w), options->incremental_d); } static PaintbrushOptions * paintbrush_options_new (void) { PaintbrushOptions *options; GtkWidget *vbox; GtkWidget *abox; GtkWidget *table; GtkWidget *label; GtkWidget *length_label; GtkWidget *type_label; GtkWidget *scale; GtkWidget *menu; static MenuItem gradient_type_items[] = { { N_("Once Forward"), 0, 0, paintbrush_gradient_type_callback, (gpointer) ONCE_FORWARD, NULL, NULL }, { N_("Once Backward"), 0, 0, paintbrush_gradient_type_callback, (gpointer) ONCE_BACKWARDS, NULL, NULL }, { N_("Loop Sawtooth"), 0, 0, paintbrush_gradient_type_callback, (gpointer) LOOP_SAWTOOTH, NULL, NULL }, { N_("Loop Triangle"), 0, 0, paintbrush_gradient_type_callback, (gpointer) LOOP_TRIANGLE, NULL, NULL }, { NULL, 0, 0, NULL, NULL, NULL, NULL } }; /* the new paint tool options structure */ options = (PaintbrushOptions *) g_malloc (sizeof (PaintbrushOptions)); paint_options_init ((PaintOptions *) options, PAINTBRUSH, paintbrush_options_reset); options->fade_out = options->fade_out_d = PAINTBRUSH_DEFAULT_FADE_OUT; options->incremental = options->incremental_d = PAINTBRUSH_DEFAULT_INCREMENTAL; options->use_gradient = options->use_gradient_d = PAINTBRUSH_DEFAULT_USE_GRADIENT; options->gradient_length = options->gradient_length_d = PAINTBRUSH_DEFAULT_GRADIENT_LENGTH; options->gradient_type = options->gradient_type_d = PAINTBRUSH_DEFAULT_GRADIENT_TYPE; /* the main vbox */ vbox = ((ToolOptions *) options)->main_vbox; /* the fade-out scale */ table = gtk_table_new (4, 2, FALSE); gtk_table_set_col_spacing (GTK_TABLE (table), 0, 4); gtk_table_set_row_spacing (GTK_TABLE (table), 0, 2); gtk_table_set_row_spacing (GTK_TABLE (table), 2, 3); gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0); label = gtk_label_new (_("Fade Out:")); gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0); gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); gtk_widget_show (label); options->fade_out_w = gtk_adjustment_new (options->fade_out_d, 0.0, 50.0, 1.0, 0.1, 0.0); scale = gtk_hscale_new (GTK_ADJUSTMENT (options->fade_out_w)); gtk_table_attach_defaults (GTK_TABLE (table), scale, 1, 2, 0, 1); gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP); gtk_range_set_update_policy (GTK_RANGE (scale), GTK_UPDATE_DELAYED); gtk_signal_connect (GTK_OBJECT (options->fade_out_w), "value_changed", (GtkSignalFunc) tool_options_double_adjustment_update, &options->fade_out); gtk_widget_show (scale); /* the use gradient toggle */ options->use_gradient_w = gtk_check_button_new_with_label (_("Gradient")); gtk_table_attach (GTK_TABLE (table), options->use_gradient_w, 0, 1, 1, 2, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); gtk_signal_connect (GTK_OBJECT (options->use_gradient_w), "toggled", (GtkSignalFunc) paintbrush_gradient_toggle_callback, &options->use_gradient); gtk_widget_show (options->use_gradient_w); length_label = gtk_label_new (_("Length:")); gtk_misc_set_alignment (GTK_MISC (length_label), 1.0, 1.0); gtk_table_attach (GTK_TABLE (table), length_label, 0, 1, 2, 3, GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); gtk_widget_show (length_label); /* the gradient length scale */ abox = gtk_alignment_new (0.5, 1.0, 1.0, 0.0); gtk_table_attach (GTK_TABLE (table), abox, 1, 2, 1, 3, GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); gtk_widget_show (abox); options->gradient_length_w = gtk_adjustment_new (options->gradient_length_d, 1.0, 50.0, 1.1, 0.1, 0.0); scale = gtk_hscale_new (GTK_ADJUSTMENT (options->gradient_length_w)); gtk_container_add (GTK_CONTAINER (abox), scale); gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP); gtk_range_set_update_policy (GTK_RANGE (scale), GTK_UPDATE_DELAYED); gtk_signal_connect (GTK_OBJECT (options->gradient_length_w), "value_changed", (GtkSignalFunc) tool_options_double_adjustment_update, &options->gradient_length); gtk_widget_show (scale); gtk_widget_show (table); /* the gradient type */ type_label = gtk_label_new (_("Type:")); gtk_misc_set_alignment (GTK_MISC (type_label), 1.0, 0.5); gtk_table_attach (GTK_TABLE (table), type_label, 0, 1, 3, 4, GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); gtk_widget_show (type_label); abox = gtk_alignment_new (0.0, 0.5, 0.0, 0.0); gtk_table_attach_defaults (GTK_TABLE (table), abox, 1, 2, 3, 4); gtk_widget_show (abox); options->gradient_type_w = gtk_option_menu_new (); gtk_container_add (GTK_CONTAINER (abox), options->gradient_type_w); gtk_widget_show (options->gradient_type_w); menu = build_menu (gradient_type_items, NULL); gtk_option_menu_set_menu (GTK_OPTION_MENU (options->gradient_type_w), menu); gtk_option_menu_set_history (GTK_OPTION_MENU (options->gradient_type_w), options->gradient_type_d); /* the incremental toggle */ options->incremental_w = gtk_check_button_new_with_label (_("Incremental")); gtk_box_pack_start (GTK_BOX (vbox), options->incremental_w, FALSE, FALSE, 0); gtk_signal_connect (GTK_OBJECT (options->incremental_w), "toggled", (GtkSignalFunc) tool_options_toggle_update, &options->incremental); gtk_widget_show (options->incremental_w); /* automatically set the sensitive state of the gradient stuff */ gtk_widget_set_sensitive (scale, options->use_gradient_d); gtk_widget_set_sensitive (length_label, options->use_gradient_d); gtk_widget_set_sensitive (options->gradient_type_w, options->use_gradient_d); gtk_widget_set_sensitive (type_label, options->use_gradient_d); gtk_widget_set_sensitive (options->incremental_w, ! options->use_gradient_d); gtk_object_set_data (GTK_OBJECT (options->use_gradient_w), "set_sensitive", scale); gtk_object_set_data (GTK_OBJECT (scale), "set_sensitive", length_label); gtk_object_set_data (GTK_OBJECT (length_label), "set_sensitive", options->gradient_type_w); gtk_object_set_data (GTK_OBJECT (options->gradient_type_w), "set_sensitive", type_label); gtk_object_set_data (GTK_OBJECT (options->use_gradient_w), "inverse_sensitive", options->incremental_w); return options; } #define TIMED_BRUSH 0 void * paintbrush_paint_func (PaintCore *paint_core, GimpDrawable *drawable, int state) { #if TIMED_BRUSH static GTimer *timer = NULL; #endif switch (state) { case INIT_PAINT : #if TIMED_BRUSH timer = g_timer_new(); g_timer_start(timer); #endif /* TIMED_BRUSH */ break; case MOTION_PAINT : paintbrush_motion (paint_core, drawable, paintbrush_options->fade_out, paintbrush_options->use_gradient ? exp(paintbrush_options->gradient_length/10) : 0, paintbrush_options->incremental, paintbrush_options->gradient_type); break; case FINISH_PAINT : #if TIMED_BRUSH if (timer) { g_timer_stop(timer); printf("painting took %f:\n", g_timer_elapsed(timer, NULL)); g_timer_destroy(timer); timer = NULL; } #endif /* TIMED_BRUSH */ break; default : break; } return NULL; } Tool * tools_new_paintbrush () { Tool * tool; PaintCore * private; /* The tool options */ if (! paintbrush_options) { paintbrush_options = paintbrush_options_new (); tools_register (PAINTBRUSH, (ToolOptions *) paintbrush_options); /* press all default buttons */ paintbrush_options_reset (); } tool = paint_core_new (PAINTBRUSH); private = (PaintCore *) tool->private; private->paint_func = paintbrush_paint_func; private->pick_colors = TRUE; return tool; } void tools_free_paintbrush (Tool *tool) { paint_core_free (tool); } static void paintbrush_motion (PaintCore *paint_core, GimpDrawable *drawable, double fade_out, double gradient_length, gboolean incremental, int gradient_type) { GImage *gimage; TempBuf * area; double x, paint_left; double position; unsigned char local_blend = OPAQUE_OPACITY; unsigned char temp_blend = OPAQUE_OPACITY; unsigned char col[MAX_CHANNELS]; double r,g,b,a; int mode; position = 0.0; if (! (gimage = drawable_gimage (drawable))) return; gimage_get_foreground (gimage, drawable, col); /* silly hack to be removed later */ /* paint_core->brush = gimp_brush_list_get_brush_by_index(brush_list,(rand()% gimp_brush_list_length(brush_list))); */ /* Get a region which can be used to paint to */ if (! (area = paint_core_get_paint_area (paint_core, drawable))) return; /* factor in the fade out value */ if (fade_out) { /* Model the amount of paint left as a gaussian curve */ x = ((double) paint_core->distance / fade_out); paint_left = exp (- x * x * 0.5); local_blend = (int) (255 * paint_left); } if (local_blend) { /* set the alpha channel */ col[area->bytes - 1] = OPAQUE_OPACITY; temp_blend = local_blend; /* hard core to mode LOOP_TRIANGLE */ /* need to make a gui to handle this */ mode = gradient_type; if(gradient_length) { paint_core_get_color_from_gradient (paint_core, gradient_length, &r,&g,&b,&a,mode); r = r * 255.0; g = g * 255.0; b = b * 255.0; a = a * 255.0; temp_blend = (gint)((a * local_blend)/255); col[0] = (gint)r; col[1] = (gint)g; col[2] = (gint)b; /* always use incremental mode with gradients */ /* make the gui cool later */ incremental = INCREMENTAL; } /* just leave this because I know as soon as i delete it i'll find a bug */ /* printf("temp_blend: %u grad_len: %f distance: %f \n",temp_blend, gradient_length, distance); */ /* color the pixels */ if(GIMP_IS_BRUSH_PIXMAP(paint_core->brush) && !gradient_length) { color_area_with_pixmap(gimage, drawable, area, paint_core->brush); mode = INCREMENTAL; } else { /* color the pixels */ color_pixels (temp_buf_data (area), col, area->width * area->height, area->bytes); } /* color_pixels (temp_buf_data (area), col, */ /* area->width * area->height, area->bytes); */ /* paste the newly painted canvas to the gimage which is being worked on */ paint_core_paste_canvas (paint_core, drawable, temp_blend, (int) (gimp_context_get_opacity (NULL) * 255), gimp_context_get_paint_mode (NULL), PRESSURE, incremental ? INCREMENTAL : CONSTANT); } } static void * paintbrush_non_gui_paint_func (PaintCore *paint_core, GimpDrawable *drawable, int state) { paintbrush_motion(paint_core,drawable, non_gui_fade_out, (non_gui_gradient_length)?exp(non_gui_gradient_length/10):0, non_gui_incremental, non_gui_gradient_type); return NULL; } gboolean paintbrush_non_gui_default (GimpDrawable *drawable, int num_strokes, double *stroke_array) { PaintbrushOptions *options = paintbrush_options; double fade_out = PAINTBRUSH_DEFAULT_FADE_OUT; gboolean incremental = PAINTBRUSH_DEFAULT_INCREMENTAL; int use_gradient = PAINTBRUSH_DEFAULT_USE_GRADIENT; double gradient_length = PAINTBRUSH_DEFAULT_GRADIENT_LENGTH; int gradient_type = PAINTBRUSH_DEFAULT_GRADIENT_TYPE; int i; if(options) { fade_out = options->fade_out; incremental = options->incremental; use_gradient = options->use_gradient; gradient_length = options->gradient_length; gradient_type = options->gradient_type; } if(use_gradient == 0) gradient_length = 0.0; /* Hmmm... PDB paintbrush should have gradient type added to it!*/ /* thats why the code below is duplicated. */ if (paint_core_init (&non_gui_paint_core, drawable, stroke_array[0], stroke_array[1])) { non_gui_fade_out = fade_out; non_gui_gradient_length = gradient_length; non_gui_gradient_type = gradient_type; non_gui_incremental = incremental; /* Set the paint core's paint func */ non_gui_paint_core.paint_func = paintbrush_non_gui_paint_func; non_gui_paint_core.startx = non_gui_paint_core.lastx = stroke_array[0]; non_gui_paint_core.starty = non_gui_paint_core.lasty = stroke_array[1]; paintbrush_non_gui_paint_func (&non_gui_paint_core, drawable, 0); for (i = 1; i < num_strokes; i++) { non_gui_paint_core.curx = stroke_array[i * 2 + 0]; non_gui_paint_core.cury = stroke_array[i * 2 + 1]; paint_core_interpolate (&non_gui_paint_core, drawable); non_gui_paint_core.lastx = non_gui_paint_core.curx; non_gui_paint_core.lasty = non_gui_paint_core.cury; } /* Finish the painting */ paint_core_finish (&non_gui_paint_core, drawable, -1); /* Cleanup */ paint_core_cleanup (); return TRUE; } else return FALSE; } gboolean paintbrush_non_gui (GimpDrawable *drawable, int num_strokes, double *stroke_array, double fade_out, int method, double gradient_length) { int i; /* Code duplicated above */ if (paint_core_init (&non_gui_paint_core, drawable, stroke_array[0], stroke_array[1])) { non_gui_fade_out = fade_out; non_gui_gradient_length = gradient_length; non_gui_gradient_type = LOOP_TRIANGLE; non_gui_incremental = method; /* Set the paint core's paint func */ non_gui_paint_core.paint_func = paintbrush_non_gui_paint_func; non_gui_paint_core.startx = non_gui_paint_core.lastx = stroke_array[0]; non_gui_paint_core.starty = non_gui_paint_core.lasty = stroke_array[1]; if (num_strokes == 1) paintbrush_non_gui_paint_func (&non_gui_paint_core, drawable, 0); for (i = 1; i < num_strokes; i++) { non_gui_paint_core.curx = stroke_array[i * 2 + 0]; non_gui_paint_core.cury = stroke_array[i * 2 + 1]; paint_core_interpolate (&non_gui_paint_core, drawable); non_gui_paint_core.lastx = non_gui_paint_core.curx; non_gui_paint_core.lasty = non_gui_paint_core.cury; } /* Finish the painting */ paint_core_finish (&non_gui_paint_core, drawable, -1); /* Cleanup */ paint_core_cleanup (); return TRUE; } else return FALSE; }