/* 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 "config.h" #include #include "apptypes.h" #include "appenv.h" #include "airbrush.h" #include "drawable.h" #include "gdisplay.h" #include "gradient.h" #include "gimpimage.h" #include "gimpbrush.h" #include "gimpcontext.h" #include "gimpui.h" #include "paint_funcs.h" #include "paint_core.h" #include "paint_options.h" #include "selection.h" #include "temp_buf.h" #include "tool.h" #include "libgimp/gimpintl.h" /* The maximum amount of pressure that can be exerted */ #define MAX_PRESSURE 0.075 /* Default pressure setting */ #define AIRBRUSH_PRESSURE_DEFAULT 10.0 #define AIRBRUSH_INCREMENTAL_DEFAULT FALSE #define OFF 0 #define ON 1 /* the airbrush structures */ typedef struct _AirbrushTimeout AirbrushTimeout; struct _AirbrushTimeout { PaintCore *paint_core; GimpDrawable *drawable; }; typedef struct _AirbrushOptions AirbrushOptions; struct _AirbrushOptions { PaintOptions paint_options; gdouble rate; gdouble rate_d; GtkObject *rate_w; gdouble pressure; gdouble pressure_d; GtkObject *pressure_w; }; /* local function prototypes */ static gpointer airbrush_paint_func (PaintCore *paint_core, GimpDrawable *drawable, PaintState state); static gpointer airbrush_non_gui_paint_func (PaintCore *paint_core, GimpDrawable *drawable, PaintState state); /* the airbrush tool options */ static AirbrushOptions *airbrush_options = NULL; /* local variables */ static gint timer; /* timer for successive paint applications */ static gint timer_state = OFF; /* state of airbrush tool */ static AirbrushTimeout airbrush_timeout; static gdouble non_gui_pressure; static gboolean non_gui_incremental; /* forward function declarations */ static void airbrush_motion (PaintCore *, GimpDrawable *, PaintPressureOptions *, gdouble, PaintApplicationMode); static gint airbrush_time_out (gpointer); /* functions */ static void airbrush_options_reset (void) { AirbrushOptions *options = airbrush_options; paint_options_reset ((PaintOptions *) options); gtk_adjustment_set_value (GTK_ADJUSTMENT (options->rate_w), options->rate_d); gtk_adjustment_set_value (GTK_ADJUSTMENT (options->pressure_w), options->pressure_d); } static AirbrushOptions * airbrush_options_new (void) { AirbrushOptions *options; GtkWidget *vbox; GtkWidget *table; GtkWidget *scale; /* the new airbrush tool options structure */ options = g_new (AirbrushOptions, 1); paint_options_init ((PaintOptions *) options, AIRBRUSH, airbrush_options_reset); options->rate = options->rate_d = 80.0; options->pressure = options->pressure_d = AIRBRUSH_PRESSURE_DEFAULT; /* the main vbox */ vbox = ((ToolOptions *) options)->main_vbox; /* the rate scale */ table = gtk_table_new (2, 2, FALSE); gtk_table_set_col_spacing (GTK_TABLE (table), 0, 4); gtk_table_set_row_spacings (GTK_TABLE (table), 1); gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0); options->rate_w = gtk_adjustment_new (options->rate_d, 0.0, 150.0, 1.0, 1.0, 0.0); scale = gtk_hscale_new (GTK_ADJUSTMENT (options->rate_w)); 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->rate_w), "value_changed", GTK_SIGNAL_FUNC (gimp_double_adjustment_update), &options->rate); gimp_table_attach_aligned (GTK_TABLE (table), 0, 0, _("Rate:"), 1.0, 1.0, scale, 1, FALSE); /* the pressure scale */ options->pressure_w = gtk_adjustment_new (options->pressure_d, 0.0, 100.0, 1.0, 1.0, 0.0); scale = gtk_hscale_new (GTK_ADJUSTMENT (options->pressure_w)); 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->pressure_w), "value_changed", GTK_SIGNAL_FUNC (gimp_double_adjustment_update), &options->pressure); gimp_table_attach_aligned (GTK_TABLE (table), 0, 1, _("Pressure:"), 1.0, 1.0, scale, 1, FALSE); gtk_widget_show (table); return options; } Tool * tools_new_airbrush (void) { Tool * tool; PaintCore * private; /* The tool options */ if (! airbrush_options) { airbrush_options = airbrush_options_new (); tools_register (AIRBRUSH, (ToolOptions *) airbrush_options); } tool = paint_core_new (AIRBRUSH); private = (PaintCore *) tool->private; private->paint_func = airbrush_paint_func; private->pick_colors = TRUE; private->flags |= TOOL_CAN_HANDLE_CHANGING_BRUSH; return tool; } static gpointer airbrush_paint_func (PaintCore *paint_core, GimpDrawable *drawable, PaintState state) { GimpBrush *brush; gdouble rate; if (!drawable) return NULL; brush = gimp_context_get_brush (NULL); switch (state) { case INIT_PAINT : /* timer_state = OFF; */ if (timer_state == ON) { g_warning ("killing stray timer, please report to lewing@gimp.org"); gtk_timeout_remove (timer); } timer_state = OFF; break; case MOTION_PAINT : if (timer_state == ON) gtk_timeout_remove (timer); timer_state = OFF; airbrush_motion (paint_core, drawable, airbrush_options->paint_options.pressure_options, airbrush_options->pressure, airbrush_options->paint_options.incremental ? INCREMENTAL : CONSTANT); if (airbrush_options->rate != 0.0) { airbrush_timeout.paint_core = paint_core; airbrush_timeout.drawable = drawable; rate = airbrush_options->paint_options.pressure_options->rate ? (10000 / (airbrush_options->rate * 2.0 * paint_core->curpressure)) : (10000 / airbrush_options->rate); timer = gtk_timeout_add (rate, airbrush_time_out, NULL); timer_state = ON; } break; case FINISH_PAINT : if (timer_state == ON) gtk_timeout_remove (timer); timer_state = OFF; break; default : break; } return NULL; } void tools_free_airbrush (Tool *tool) { if (timer_state == ON) gtk_timeout_remove (timer); timer_state = OFF; paint_core_free (tool); } static gint airbrush_time_out (gpointer client_data) { /* service the timer */ airbrush_motion (airbrush_timeout.paint_core, airbrush_timeout.drawable, airbrush_options->paint_options.pressure_options, airbrush_options->pressure, airbrush_options->paint_options.incremental ? INCREMENTAL : CONSTANT); gdisplays_flush (); /* restart the timer */ if (airbrush_options->rate != 0.0) { if (airbrush_options->paint_options.pressure_options->rate) { /* set a new timer */ timer = gtk_timeout_add ((10000 / (airbrush_options->rate * 2.0 * airbrush_timeout.paint_core->curpressure)), airbrush_time_out, NULL); return FALSE; } else return TRUE; } else return FALSE; } static void airbrush_motion (PaintCore *paint_core, GimpDrawable *drawable, PaintPressureOptions *pressure_options, double pressure, PaintApplicationMode mode) { GImage *gimage; TempBuf *area; guchar col[MAX_CHANNELS]; gdouble scale; if (!drawable) return; if (! (gimage = gimp_drawable_gimage (drawable))) return; if (pressure_options->size) scale = paint_core->curpressure; else scale = 1.0; if (! (area = paint_core_get_paint_area (paint_core, drawable, scale))) return; /* color the pixels */ if (pressure_options->color) { GimpRGB color; gradient_get_color_at (gimp_context_get_gradient (NULL), paint_core->curpressure, &color); gimp_rgba_get_uchar (&color, &col[RED_PIX], &col[GREEN_PIX], &col[BLUE_PIX], &col[ALPHA_PIX]); mode = INCREMENTAL; color_pixels (temp_buf_data (area), col, area->width * area->height, area->bytes); } else if (paint_core->brush && paint_core->brush->pixmap) { mode = INCREMENTAL; paint_core_color_area_with_pixmap (paint_core, gimage, drawable, area, scale, SOFT); } else { gimp_image_get_foreground (gimage, drawable, col); col[area->bytes - 1] = OPAQUE_OPACITY; color_pixels (temp_buf_data (area), col, area->width * area->height, area->bytes); } if (pressure_options->pressure) pressure = pressure * 2.0 * paint_core->curpressure; /* paste the newly painted area to the image */ paint_core_paste_canvas (paint_core, drawable, MIN (pressure, 255), (gint) (gimp_context_get_opacity (NULL) * 255), gimp_context_get_paint_mode (NULL), SOFT, scale, mode); } static gpointer airbrush_non_gui_paint_func (PaintCore *paint_core, GimpDrawable *drawable, PaintState state) { airbrush_motion (paint_core, drawable, &non_gui_pressure_options, non_gui_pressure, non_gui_incremental); return NULL; } gboolean airbrush_non_gui_default (GimpDrawable *drawable, gint num_strokes, gdouble *stroke_array) { AirbrushOptions *options = airbrush_options; gdouble pressure = AIRBRUSH_PRESSURE_DEFAULT; if(options) pressure = options->pressure; return airbrush_non_gui (drawable, pressure, num_strokes, stroke_array); } gboolean airbrush_non_gui (GimpDrawable *drawable, gdouble pressure, gint num_strokes, gdouble *stroke_array) { gint i; if (paint_core_init (&non_gui_paint_core, drawable, stroke_array[0], stroke_array[1])) { /* Set the paint core's paint func */ non_gui_paint_core.paint_func = airbrush_non_gui_paint_func; non_gui_pressure = pressure; 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]; airbrush_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; } return FALSE; }