From 5b5c24e9dd7bdda8bc982f9fb8c091cc6ab8450e Mon Sep 17 00:00:00 2001 From: People doing a 16 bpc version of gimp Date: Thu, 1 Jul 1999 16:52:50 +0000 Subject: [PATCH] Import of Smudge and dodge and burn tools. Details in Change log. calvin@rhythm.com --- ChangeLog | 31 ++ app/Makefile.am | 4 + app/core/gimptooloptions.c | 6 +- app/dodgeburn.c | 521 ++++++++++++++++++++++++++++++++++ app/dodgeburn.h | 42 +++ app/paint-funcs/paint-funcs.c | 32 +-- app/paint/gimpdodgeburn.c | 521 ++++++++++++++++++++++++++++++++++ app/paint/gimpdodgeburn.h | 42 +++ app/paint/gimpsmudge.c | 423 +++++++++++++++++++++++++++ app/paint/gimpsmudge.h | 42 +++ app/paint_core.c | 143 ++++++++-- app/paint_funcs.c | 32 +-- app/pixmaps2.h | 60 ++++ app/smudge.c | 423 +++++++++++++++++++++++++++ app/smudge.h | 42 +++ app/tool_options.c | 6 +- app/tools.c | 50 +++- app/tools/dodgeburn.c | 521 ++++++++++++++++++++++++++++++++++ app/tools/dodgeburn.h | 42 +++ app/tools/gimpdodgeburntool.c | 521 ++++++++++++++++++++++++++++++++++ app/tools/gimpdodgeburntool.h | 42 +++ app/tools/gimpsmudgetool.c | 423 +++++++++++++++++++++++++++ app/tools/gimpsmudgetool.h | 42 +++ app/tools/paint_core.c | 143 ++++++++-- app/tools/smudge.c | 423 +++++++++++++++++++++++++++ app/tools/smudge.h | 42 +++ app/tools/tool_options.c | 6 +- app/tools/tools.c | 50 +++- app/toolsF.h | 4 +- 29 files changed, 4591 insertions(+), 88 deletions(-) create mode 100644 app/dodgeburn.c create mode 100644 app/dodgeburn.h create mode 100644 app/paint/gimpdodgeburn.c create mode 100644 app/paint/gimpdodgeburn.h create mode 100644 app/paint/gimpsmudge.c create mode 100644 app/paint/gimpsmudge.h create mode 100644 app/smudge.c create mode 100644 app/smudge.h create mode 100644 app/tools/dodgeburn.c create mode 100644 app/tools/dodgeburn.h create mode 100644 app/tools/gimpdodgeburntool.c create mode 100644 app/tools/gimpdodgeburntool.h create mode 100644 app/tools/gimpsmudgetool.c create mode 100644 app/tools/gimpsmudgetool.h create mode 100644 app/tools/smudge.c create mode 100644 app/tools/smudge.h diff --git a/ChangeLog b/ChangeLog index 14db4fc28c..56d8bf01fd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,34 @@ +Thu Jul 1 09:42:40 PDT 1999 Calvin Williamson + + Added the files: + + *app/smudge.[ch] + *app/dodgeburn.[ch] + + Changed: + + *app/Makefile.am + *app/paint_core.c + *app/paint_funcs.c + *app/pixmaps2.h + *app/tool_options.c + *app/tools.c + *app/toolsF.h + + This is the first version of the dodge and burn and smudge tools. + These both use the opacity and spacing from the brush dialog, but + they dont use the apply modes, since they both use a form of + paint_core_replace. This could be added though. + + Smudge affects alpha channels when present, dodgeburn doesnt. + + Dodgeburn computes a gimp_lut based on the settings and uses that. + + Smudge just drags along a version of the initial painthit and mixes + that in with subsequent painthits. + + I didnt added any pdb functionality yet. + Thu Jul 1 10:29:44 MEST 1999 Sven Neumann * tools/pdbgen/pdb/guides.pdb diff --git a/app/Makefile.am b/app/Makefile.am index 0bcade47a1..4355a4b88a 100644 --- a/app/Makefile.am +++ b/app/Makefile.am @@ -126,6 +126,8 @@ gimp_SOURCES = \ docindex.h \ docindexif.c \ docindexif.h \ + dodgeburn.c \ + dodgeburn.h \ draw_core.c \ draw_core.h \ drawable.c \ @@ -349,6 +351,8 @@ gimp_SOURCES = \ session.c \ shear_tool.c \ shear_tool.h \ + smudge.c \ + smudge.h \ temp_buf.c \ temp_buf.h \ text_tool.c \ diff --git a/app/core/gimptooloptions.c b/app/core/gimptooloptions.c index 7d7746b9cd..50298fbd88 100644 --- a/app/core/gimptooloptions.c +++ b/app/core/gimptooloptions.c @@ -570,7 +570,11 @@ paint_options_init (PaintOptions *options, N_("Convolver Options") : ((tool_type == INK) ? N_("Ink Options") : - N_("ERROR: Unknown Paint Type")))))))))), + ((tool_type == DODGEBURN) ? + N_("Dodge or Burn Options") : + ((tool_type == SMUDGE) ? + N_("Smudge Options") : + N_("ERROR: Unknown Paint Type")))))))))))), reset_func); /* initialize the paint options structure */ diff --git a/app/dodgeburn.c b/app/dodgeburn.c new file mode 100644 index 0000000000..62399c7ceb --- /dev/null +++ b/app/dodgeburn.c @@ -0,0 +1,521 @@ +/* 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 "gdk/gdkkeysyms.h" +#include "appenv.h" +#include "drawable.h" +#include "errors.h" +#include "dodgeburn.h" +#include "gdisplay.h" +#include "gimplut.h" +#include "paint_funcs.h" +#include "paint_core.h" +#include "paint_options.h" +#include "selection.h" +#include "tool_options_ui.h" +#include "tools.h" +#include "gimage.h" + +#include "libgimp/gimpintl.h" +#define ROUND(x) (int)((x) + .5) + +/* the dodgeburn structures */ + +typedef struct _DodgeBurnOptions DodgeBurnOptions; +struct _DodgeBurnOptions +{ + PaintOptions paint_options; + + DodgeBurnType type; + DodgeBurnType type_d; + GtkWidget *type_w[2]; + + DodgeBurnMode mode; /*highlights,midtones,shadows*/ + DodgeBurnMode mode_d; + GtkWidget *mode_w[3]; + + double exposure; + double exposure_d; + GtkObject *exposure_w; + + GimpLut *lut; +}; + +static void dodgeburn_make_luts ( PaintCore *, GimpDrawable *); + +static gfloat dodgeburn_highlights_lut_func(void *, int, int, gfloat); +static gfloat dodgeburn_midtones_lut_func(void *, int, int, gfloat); +static gfloat dodgeburn_shadows_lut_func(void *, int, int, gfloat); + +/* The dodge burn lookup tables */ +gfloat dodgeburn_highlights(void *, int, int, gfloat); +gfloat dodgeburn_midtones(void *, int, int, gfloat); +gfloat dodgeburn_shadows(void *, int, int, gfloat); + +/* the dodgeburn tool options */ +static DodgeBurnOptions * dodgeburn_options = NULL; + +static void dodgeburn_motion (PaintCore *, GimpDrawable *); +static void dodgeburn_init (PaintCore *, GimpDrawable *); +static void dodgeburn_finish (PaintCore *, GimpDrawable *); + +/* functions */ + +static void +dodgeburn_options_reset (void) +{ + DodgeBurnOptions *options = dodgeburn_options; + + paint_options_reset ((PaintOptions *) options); + + gtk_adjustment_set_value (GTK_ADJUSTMENT (options->exposure_w), + options->exposure_d); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->type_w[options->type_d]), TRUE); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->mode_w[options->mode_d]), TRUE); +} + +static DodgeBurnOptions * +dodgeburn_options_new (void) +{ + DodgeBurnOptions *options; + + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *scale; + GtkWidget *frame; + + gchar* type_label[2] = { N_("Dodge"), N_("Burn") }; + gint type_value[2] = { DODGE, BURN }; + + gchar* mode_label[3] = { N_("Highlights"), + N_("Midtones"), + N_("Shadows") }; + + gint mode_value[3] = { DODGEBURN_HIGHLIGHTS, + DODGEBURN_MIDTONES, + DODGEBURN_SHADOWS }; + + /* the new dodgeburn tool options structure */ + options = (DodgeBurnOptions *) g_malloc (sizeof (DodgeBurnOptions)); + paint_options_init ((PaintOptions *) options, + DODGEBURN, + dodgeburn_options_reset); + + options->type = options->type_d = DODGE; + options->exposure = options->exposure_d = 50.0; + options->mode = options->mode_d = DODGEBURN_HIGHLIGHTS; + + /* the main vbox */ + vbox = ((ToolOptions *) options)->main_vbox; + + /* the exposure scale */ + hbox = gtk_hbox_new (FALSE, 4); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + + label = gtk_label_new (_("Exposure:")); + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + options->exposure_w = + gtk_adjustment_new (options->exposure_d, 0.0, 100.0, 1.0, 1.0, 0.0); + scale = gtk_hscale_new (GTK_ADJUSTMENT (options->exposure_w)); + gtk_box_pack_start (GTK_BOX (hbox), scale, TRUE, TRUE, 0); + 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->exposure_w), "value_changed", + (GtkSignalFunc) tool_options_double_adjustment_update, + &options->exposure); + + gtk_widget_show (scale); + gtk_widget_show (hbox); + + /* the type (dodge or burn) */ + frame = tool_options_radio_buttons_new (_("Type"), + &options->type, + options->type_w, + type_label, + type_value, + 2); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->type_w[options->type_d]), TRUE); + gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + /* mode (highlights, midtones, or shadows) */ + frame = tool_options_radio_buttons_new (_("Mode"), + &options->mode, + options->mode_w, + mode_label, + mode_value, + 3); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->mode_w[options->mode_d]), TRUE); + gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + return options; +} + +void * +dodgeburn_paint_func (PaintCore *paint_core, + GimpDrawable *drawable, + int state) +{ + switch (state) + { + case INIT_PAINT: + dodgeburn_init (paint_core, drawable); + break; + case MOTION_PAINT: + dodgeburn_motion (paint_core, drawable); + break; + case FINISH_PAINT: + dodgeburn_finish (paint_core, drawable); + break; + } + + return NULL; +} + +static void +dodgeburn_finish ( PaintCore *paint_core, + GimpDrawable * drawable) +{ + /* Here we destroy the luts to do the painting with.*/ + if (dodgeburn_options->lut) + { + gimp_lut_free (dodgeburn_options->lut); + dodgeburn_options->lut = NULL; + } +} + +static void +dodgeburn_init ( PaintCore *paint_core, + GimpDrawable * drawable) +{ + /* Here we create the luts to do the painting with.*/ + dodgeburn_make_luts (paint_core, drawable); +} + +static void +dodgeburn_make_luts ( PaintCore *paint_core, + GimpDrawable * drawable) +{ + GimpLutFunc lut_func; + int nchannels = gimp_drawable_bytes (drawable); + gfloat exposure = (dodgeburn_options->exposure)/100.0; + + /* make the exposure negative if burn for luts*/ + if (dodgeburn_options->type == BURN) + exposure = -exposure; + + dodgeburn_options->lut = gimp_lut_new(); + + switch (dodgeburn_options->mode) + { + case DODGEBURN_HIGHLIGHTS: + lut_func = dodgeburn_highlights_lut_func; + break; + case DODGEBURN_MIDTONES: + lut_func = dodgeburn_midtones_lut_func; + break; + case DODGEBURN_SHADOWS: + lut_func = dodgeburn_shadows_lut_func; + break; + default: + lut_func = NULL; + break; + } + + gimp_lut_setup_exact (dodgeburn_options->lut, + lut_func, (void *)&exposure, + nchannels); + +} + +static void +dodgeburn_modifier_key_func (Tool *tool, + GdkEventKey *kevent, + gpointer gdisp_ptr) +{ + switch (kevent->keyval) + { + case GDK_Alt_L: case GDK_Alt_R: + break; + case GDK_Shift_L: case GDK_Shift_R: + switch (dodgeburn_options->type) + { + case BURN: + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dodgeburn_options->type_w[BURN]), TRUE); + break; + case DODGE: + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dodgeburn_options->type_w[DODGE]), TRUE); + break; + default: + break; + } + break; + case GDK_Control_L: case GDK_Control_R: + break; + } +} + +Tool * +tools_new_dodgeburn () +{ + Tool * tool; + PaintCore * private; + + /* The tool options */ + if (! dodgeburn_options) + { + dodgeburn_options = dodgeburn_options_new (); + tools_register (DODGEBURN, (ToolOptions *) dodgeburn_options); + + /* press all default buttons */ + dodgeburn_options_reset (); + } + + tool = paint_core_new (DODGEBURN); + tool->modifier_key_func = dodgeburn_modifier_key_func; + + private = (PaintCore *) tool->private; + private->paint_func = dodgeburn_paint_func; + + return tool; +} + +void +tools_free_dodgeburn (Tool *tool) +{ + /* delete any luts here */ + paint_core_free (tool); +} + +static void +dodgeburn_motion (PaintCore *paint_core, + GimpDrawable *drawable) +{ + GImage *gimage; + TempBuf * area; + TempBuf * orig; + PixelRegion srcPR, destPR, tempPR; + guchar *temp_data; + gfloat exposure; + gfloat brush_opacity; + + if (! (gimage = drawable_gimage (drawable))) + return; + + /* If the image type is indexed, don't dodgeburn */ + if ((drawable_type (drawable) == INDEXED_GIMAGE) || + (drawable_type (drawable) == INDEXEDA_GIMAGE)) + return; + + /* Get a region which can be used to paint to */ + if (! (area = paint_core_get_paint_area (paint_core, drawable))) + return; + + /* Constant painting --get a copy of the orig drawable (with + no paint from this stroke yet) */ + + { + gint x1, y1, x2, y2; + x1 = BOUNDS (area->x, 0, drawable_width (drawable)); + y1 = BOUNDS (area->y, 0, drawable_height (drawable)); + x2 = BOUNDS (area->x + area->width, 0, drawable_width (drawable)); + y2 = BOUNDS (area->y + area->height, 0, drawable_height (drawable)); + + if (!(x2 - x1) || !(y2 - y1)) + return; + + /* get the original untouched image */ + orig = paint_core_get_orig_image (paint_core, drawable, x1, y1, x2, y2); + srcPR.bytes = orig->bytes; + srcPR.x = 0; + srcPR.y = 0; + srcPR.w = x2 - x1; + srcPR.h = y2 - y1; + srcPR.rowstride = srcPR.bytes * orig->width; + srcPR.data = temp_buf_data (orig); + } + + /* tempPR will hold the dodgeburned region*/ + tempPR.bytes = srcPR.bytes; + tempPR.x = srcPR.x; + tempPR.y = srcPR.y; + tempPR.w = srcPR.w; + tempPR.h = srcPR.h; + tempPR.rowstride = tempPR.bytes * tempPR.w; + temp_data = g_malloc (tempPR.h * tempPR.rowstride); + tempPR.data = temp_data; + + brush_opacity = PAINT_OPTIONS_GET_OPACITY (dodgeburn_options); + exposure = (dodgeburn_options->exposure)/100.0; + + /* DodgeBurn the region */ + gimp_lut_process (dodgeburn_options->lut, &srcPR, &tempPR); + + /* The dest is the paint area we got above (= canvas_buf) */ + destPR.bytes = area->bytes; + destPR.x = 0; destPR.y = 0; + destPR.w = area->width; + destPR.h = area->height; + destPR.rowstride = area->width * destPR.bytes; + destPR.data = temp_buf_data (area); + + /* Now add an alpha to the dodgeburned region + and put this in area = canvas_buf */ + if (!drawable_has_alpha (drawable)) + add_alpha_region (&tempPR, &destPR); + else + copy_region(&tempPR, &destPR); + + /*Replace the newly dodgedburned area (canvas_buf) to the gimage*/ + + paint_core_replace_canvas (paint_core, drawable, ROUND(brush_opacity * 255.0), + OPAQUE_OPACITY, PRESSURE, CONSTANT); + + g_free (temp_data); +} + +static void * +dodgeburn_non_gui_paint_func (PaintCore *paint_core, + GimpDrawable *drawable, + int state) +{ + dodgeburn_motion (paint_core, drawable); + + return NULL; +} + +gboolean +dodgeburn_non_gui (GimpDrawable *drawable, + double pressure, + int num_strokes, + double *stroke_array) +{ + int 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 = dodgeburn_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) + dodgeburn_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; +} + +static gfloat +dodgeburn_highlights_lut_func(void * user_data, + int nchannels, + int channel, + gfloat value) +{ + gfloat * exposure_ptr = (gfloat *)user_data; + gfloat exposure = *exposure_ptr; + gfloat factor = 1.0 + exposure * (.333333); + + if ( (nchannels == 2 && channel == 1) || + (nchannels == 4 && channel == 3)) + return value; + + return factor * value; +} + +static gfloat +dodgeburn_midtones_lut_func(void * user_data, + int nchannels, + int channel, + gfloat value) +{ + gfloat * exposure_ptr = (gfloat *)user_data; + gfloat exposure = *exposure_ptr; + gfloat factor; + + if ( (nchannels == 2 && channel == 1) || + (nchannels == 4 && channel == 3)) + return value; + + if (exposure < 0) + factor = 1.0 - exposure * (.333333); + else + factor = 1/(1.0 + exposure); + return pow (value, factor); +} + +static gfloat +dodgeburn_shadows_lut_func(void * user_data, + int nchannels, + int channel, + gfloat value) +{ + gfloat * exposure_ptr = (gfloat *)user_data; + gfloat exposure = *exposure_ptr; + gfloat new_value; + gfloat factor; + + if ( (nchannels == 2 && channel == 1) || + (nchannels == 4 && channel == 3)) + return value; + + if (exposure >= 0) + { + factor = .333333 * exposure; + new_value = factor + value - factor * value; + } + else /* exposure < 0 */ + { + factor = -.333333 * exposure; + if (value < factor) + new_value = 0; + else /*factor <= value <=1*/ + new_value = (value - factor)/(1 - factor); + } + return new_value; +} diff --git a/app/dodgeburn.h b/app/dodgeburn.h new file mode 100644 index 0000000000..b394206424 --- /dev/null +++ b/app/dodgeburn.h @@ -0,0 +1,42 @@ +/* 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. + */ +#ifndef __DODGEBURN_H__ +#define __DODGEBURN_H__ + +#include "paint_core.h" +#include "tools.h" + +typedef enum +{ + DODGE, + BURN +} DodgeBurnType; + +typedef enum +{ + DODGEBURN_HIGHLIGHTS, + DODGEBURN_MIDTONES, + DODGEBURN_SHADOWS +} DodgeBurnMode; + +void * dodgeburn_paint_func (PaintCore *, GimpDrawable *, int); +gboolean dodgeburn_non_gui (GimpDrawable *, double, int, double *); +Tool * tools_new_dodgeburn (void); +void tools_free_dodgeburn (Tool *); + +#endif /* __DODGEBURN_H__ */ diff --git a/app/paint-funcs/paint-funcs.c b/app/paint-funcs/paint-funcs.c index c33b46f888..f1af10d3ca 100644 --- a/app/paint-funcs/paint-funcs.c +++ b/app/paint-funcs/paint-funcs.c @@ -672,15 +672,11 @@ blend_pixels (const unsigned char *src1, int alpha, b; unsigned char blend2 = (255 - blend); - alpha = (has_alpha) ? bytes - 1 : bytes; while (w --) { - for (b = 0; b < alpha; b++) + for (b = 0; b < bytes; b++) dest[b] = (src1[b] * blend2 + src2[b] * blend) / 255; - if (has_alpha) - dest[alpha] = src1[alpha]; /* alpha channel--assume src2 has none */ - src1 += bytes; src2 += bytes; dest += bytes; @@ -3102,19 +3098,23 @@ blend_region (PixelRegion *src1, int blend) { int h; - unsigned char * s1, * s2, * d; + unsigned char *s1, *s2, * d; + void * pr; - s1 = src1->data; - s2 = src2->data; - d = dest->data; - h = src1->h; - - while (h --) + for (pr = pixel_regions_register (3, src1, src2, dest); pr != NULL; pr = pixel_regions_process (pr)) { -/* blend_pixels (s1, s2, d, blend, src1->w, src1->bytes);*/ - s1 += src1->rowstride; - s2 += src2->rowstride; - d += dest->rowstride; + s1 = src1->data; + s2 = src2->data; + d = dest->data; + h = src1->h; + + while (h --) + { + blend_pixels (s1, s2, d, blend, src1->w, src1->bytes, FALSE); + s1 += src1->rowstride; + s2 += src2->rowstride; + d += dest->rowstride; + } } } diff --git a/app/paint/gimpdodgeburn.c b/app/paint/gimpdodgeburn.c new file mode 100644 index 0000000000..62399c7ceb --- /dev/null +++ b/app/paint/gimpdodgeburn.c @@ -0,0 +1,521 @@ +/* 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 "gdk/gdkkeysyms.h" +#include "appenv.h" +#include "drawable.h" +#include "errors.h" +#include "dodgeburn.h" +#include "gdisplay.h" +#include "gimplut.h" +#include "paint_funcs.h" +#include "paint_core.h" +#include "paint_options.h" +#include "selection.h" +#include "tool_options_ui.h" +#include "tools.h" +#include "gimage.h" + +#include "libgimp/gimpintl.h" +#define ROUND(x) (int)((x) + .5) + +/* the dodgeburn structures */ + +typedef struct _DodgeBurnOptions DodgeBurnOptions; +struct _DodgeBurnOptions +{ + PaintOptions paint_options; + + DodgeBurnType type; + DodgeBurnType type_d; + GtkWidget *type_w[2]; + + DodgeBurnMode mode; /*highlights,midtones,shadows*/ + DodgeBurnMode mode_d; + GtkWidget *mode_w[3]; + + double exposure; + double exposure_d; + GtkObject *exposure_w; + + GimpLut *lut; +}; + +static void dodgeburn_make_luts ( PaintCore *, GimpDrawable *); + +static gfloat dodgeburn_highlights_lut_func(void *, int, int, gfloat); +static gfloat dodgeburn_midtones_lut_func(void *, int, int, gfloat); +static gfloat dodgeburn_shadows_lut_func(void *, int, int, gfloat); + +/* The dodge burn lookup tables */ +gfloat dodgeburn_highlights(void *, int, int, gfloat); +gfloat dodgeburn_midtones(void *, int, int, gfloat); +gfloat dodgeburn_shadows(void *, int, int, gfloat); + +/* the dodgeburn tool options */ +static DodgeBurnOptions * dodgeburn_options = NULL; + +static void dodgeburn_motion (PaintCore *, GimpDrawable *); +static void dodgeburn_init (PaintCore *, GimpDrawable *); +static void dodgeburn_finish (PaintCore *, GimpDrawable *); + +/* functions */ + +static void +dodgeburn_options_reset (void) +{ + DodgeBurnOptions *options = dodgeburn_options; + + paint_options_reset ((PaintOptions *) options); + + gtk_adjustment_set_value (GTK_ADJUSTMENT (options->exposure_w), + options->exposure_d); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->type_w[options->type_d]), TRUE); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->mode_w[options->mode_d]), TRUE); +} + +static DodgeBurnOptions * +dodgeburn_options_new (void) +{ + DodgeBurnOptions *options; + + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *scale; + GtkWidget *frame; + + gchar* type_label[2] = { N_("Dodge"), N_("Burn") }; + gint type_value[2] = { DODGE, BURN }; + + gchar* mode_label[3] = { N_("Highlights"), + N_("Midtones"), + N_("Shadows") }; + + gint mode_value[3] = { DODGEBURN_HIGHLIGHTS, + DODGEBURN_MIDTONES, + DODGEBURN_SHADOWS }; + + /* the new dodgeburn tool options structure */ + options = (DodgeBurnOptions *) g_malloc (sizeof (DodgeBurnOptions)); + paint_options_init ((PaintOptions *) options, + DODGEBURN, + dodgeburn_options_reset); + + options->type = options->type_d = DODGE; + options->exposure = options->exposure_d = 50.0; + options->mode = options->mode_d = DODGEBURN_HIGHLIGHTS; + + /* the main vbox */ + vbox = ((ToolOptions *) options)->main_vbox; + + /* the exposure scale */ + hbox = gtk_hbox_new (FALSE, 4); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + + label = gtk_label_new (_("Exposure:")); + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + options->exposure_w = + gtk_adjustment_new (options->exposure_d, 0.0, 100.0, 1.0, 1.0, 0.0); + scale = gtk_hscale_new (GTK_ADJUSTMENT (options->exposure_w)); + gtk_box_pack_start (GTK_BOX (hbox), scale, TRUE, TRUE, 0); + 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->exposure_w), "value_changed", + (GtkSignalFunc) tool_options_double_adjustment_update, + &options->exposure); + + gtk_widget_show (scale); + gtk_widget_show (hbox); + + /* the type (dodge or burn) */ + frame = tool_options_radio_buttons_new (_("Type"), + &options->type, + options->type_w, + type_label, + type_value, + 2); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->type_w[options->type_d]), TRUE); + gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + /* mode (highlights, midtones, or shadows) */ + frame = tool_options_radio_buttons_new (_("Mode"), + &options->mode, + options->mode_w, + mode_label, + mode_value, + 3); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->mode_w[options->mode_d]), TRUE); + gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + return options; +} + +void * +dodgeburn_paint_func (PaintCore *paint_core, + GimpDrawable *drawable, + int state) +{ + switch (state) + { + case INIT_PAINT: + dodgeburn_init (paint_core, drawable); + break; + case MOTION_PAINT: + dodgeburn_motion (paint_core, drawable); + break; + case FINISH_PAINT: + dodgeburn_finish (paint_core, drawable); + break; + } + + return NULL; +} + +static void +dodgeburn_finish ( PaintCore *paint_core, + GimpDrawable * drawable) +{ + /* Here we destroy the luts to do the painting with.*/ + if (dodgeburn_options->lut) + { + gimp_lut_free (dodgeburn_options->lut); + dodgeburn_options->lut = NULL; + } +} + +static void +dodgeburn_init ( PaintCore *paint_core, + GimpDrawable * drawable) +{ + /* Here we create the luts to do the painting with.*/ + dodgeburn_make_luts (paint_core, drawable); +} + +static void +dodgeburn_make_luts ( PaintCore *paint_core, + GimpDrawable * drawable) +{ + GimpLutFunc lut_func; + int nchannels = gimp_drawable_bytes (drawable); + gfloat exposure = (dodgeburn_options->exposure)/100.0; + + /* make the exposure negative if burn for luts*/ + if (dodgeburn_options->type == BURN) + exposure = -exposure; + + dodgeburn_options->lut = gimp_lut_new(); + + switch (dodgeburn_options->mode) + { + case DODGEBURN_HIGHLIGHTS: + lut_func = dodgeburn_highlights_lut_func; + break; + case DODGEBURN_MIDTONES: + lut_func = dodgeburn_midtones_lut_func; + break; + case DODGEBURN_SHADOWS: + lut_func = dodgeburn_shadows_lut_func; + break; + default: + lut_func = NULL; + break; + } + + gimp_lut_setup_exact (dodgeburn_options->lut, + lut_func, (void *)&exposure, + nchannels); + +} + +static void +dodgeburn_modifier_key_func (Tool *tool, + GdkEventKey *kevent, + gpointer gdisp_ptr) +{ + switch (kevent->keyval) + { + case GDK_Alt_L: case GDK_Alt_R: + break; + case GDK_Shift_L: case GDK_Shift_R: + switch (dodgeburn_options->type) + { + case BURN: + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dodgeburn_options->type_w[BURN]), TRUE); + break; + case DODGE: + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dodgeburn_options->type_w[DODGE]), TRUE); + break; + default: + break; + } + break; + case GDK_Control_L: case GDK_Control_R: + break; + } +} + +Tool * +tools_new_dodgeburn () +{ + Tool * tool; + PaintCore * private; + + /* The tool options */ + if (! dodgeburn_options) + { + dodgeburn_options = dodgeburn_options_new (); + tools_register (DODGEBURN, (ToolOptions *) dodgeburn_options); + + /* press all default buttons */ + dodgeburn_options_reset (); + } + + tool = paint_core_new (DODGEBURN); + tool->modifier_key_func = dodgeburn_modifier_key_func; + + private = (PaintCore *) tool->private; + private->paint_func = dodgeburn_paint_func; + + return tool; +} + +void +tools_free_dodgeburn (Tool *tool) +{ + /* delete any luts here */ + paint_core_free (tool); +} + +static void +dodgeburn_motion (PaintCore *paint_core, + GimpDrawable *drawable) +{ + GImage *gimage; + TempBuf * area; + TempBuf * orig; + PixelRegion srcPR, destPR, tempPR; + guchar *temp_data; + gfloat exposure; + gfloat brush_opacity; + + if (! (gimage = drawable_gimage (drawable))) + return; + + /* If the image type is indexed, don't dodgeburn */ + if ((drawable_type (drawable) == INDEXED_GIMAGE) || + (drawable_type (drawable) == INDEXEDA_GIMAGE)) + return; + + /* Get a region which can be used to paint to */ + if (! (area = paint_core_get_paint_area (paint_core, drawable))) + return; + + /* Constant painting --get a copy of the orig drawable (with + no paint from this stroke yet) */ + + { + gint x1, y1, x2, y2; + x1 = BOUNDS (area->x, 0, drawable_width (drawable)); + y1 = BOUNDS (area->y, 0, drawable_height (drawable)); + x2 = BOUNDS (area->x + area->width, 0, drawable_width (drawable)); + y2 = BOUNDS (area->y + area->height, 0, drawable_height (drawable)); + + if (!(x2 - x1) || !(y2 - y1)) + return; + + /* get the original untouched image */ + orig = paint_core_get_orig_image (paint_core, drawable, x1, y1, x2, y2); + srcPR.bytes = orig->bytes; + srcPR.x = 0; + srcPR.y = 0; + srcPR.w = x2 - x1; + srcPR.h = y2 - y1; + srcPR.rowstride = srcPR.bytes * orig->width; + srcPR.data = temp_buf_data (orig); + } + + /* tempPR will hold the dodgeburned region*/ + tempPR.bytes = srcPR.bytes; + tempPR.x = srcPR.x; + tempPR.y = srcPR.y; + tempPR.w = srcPR.w; + tempPR.h = srcPR.h; + tempPR.rowstride = tempPR.bytes * tempPR.w; + temp_data = g_malloc (tempPR.h * tempPR.rowstride); + tempPR.data = temp_data; + + brush_opacity = PAINT_OPTIONS_GET_OPACITY (dodgeburn_options); + exposure = (dodgeburn_options->exposure)/100.0; + + /* DodgeBurn the region */ + gimp_lut_process (dodgeburn_options->lut, &srcPR, &tempPR); + + /* The dest is the paint area we got above (= canvas_buf) */ + destPR.bytes = area->bytes; + destPR.x = 0; destPR.y = 0; + destPR.w = area->width; + destPR.h = area->height; + destPR.rowstride = area->width * destPR.bytes; + destPR.data = temp_buf_data (area); + + /* Now add an alpha to the dodgeburned region + and put this in area = canvas_buf */ + if (!drawable_has_alpha (drawable)) + add_alpha_region (&tempPR, &destPR); + else + copy_region(&tempPR, &destPR); + + /*Replace the newly dodgedburned area (canvas_buf) to the gimage*/ + + paint_core_replace_canvas (paint_core, drawable, ROUND(brush_opacity * 255.0), + OPAQUE_OPACITY, PRESSURE, CONSTANT); + + g_free (temp_data); +} + +static void * +dodgeburn_non_gui_paint_func (PaintCore *paint_core, + GimpDrawable *drawable, + int state) +{ + dodgeburn_motion (paint_core, drawable); + + return NULL; +} + +gboolean +dodgeburn_non_gui (GimpDrawable *drawable, + double pressure, + int num_strokes, + double *stroke_array) +{ + int 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 = dodgeburn_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) + dodgeburn_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; +} + +static gfloat +dodgeburn_highlights_lut_func(void * user_data, + int nchannels, + int channel, + gfloat value) +{ + gfloat * exposure_ptr = (gfloat *)user_data; + gfloat exposure = *exposure_ptr; + gfloat factor = 1.0 + exposure * (.333333); + + if ( (nchannels == 2 && channel == 1) || + (nchannels == 4 && channel == 3)) + return value; + + return factor * value; +} + +static gfloat +dodgeburn_midtones_lut_func(void * user_data, + int nchannels, + int channel, + gfloat value) +{ + gfloat * exposure_ptr = (gfloat *)user_data; + gfloat exposure = *exposure_ptr; + gfloat factor; + + if ( (nchannels == 2 && channel == 1) || + (nchannels == 4 && channel == 3)) + return value; + + if (exposure < 0) + factor = 1.0 - exposure * (.333333); + else + factor = 1/(1.0 + exposure); + return pow (value, factor); +} + +static gfloat +dodgeburn_shadows_lut_func(void * user_data, + int nchannels, + int channel, + gfloat value) +{ + gfloat * exposure_ptr = (gfloat *)user_data; + gfloat exposure = *exposure_ptr; + gfloat new_value; + gfloat factor; + + if ( (nchannels == 2 && channel == 1) || + (nchannels == 4 && channel == 3)) + return value; + + if (exposure >= 0) + { + factor = .333333 * exposure; + new_value = factor + value - factor * value; + } + else /* exposure < 0 */ + { + factor = -.333333 * exposure; + if (value < factor) + new_value = 0; + else /*factor <= value <=1*/ + new_value = (value - factor)/(1 - factor); + } + return new_value; +} diff --git a/app/paint/gimpdodgeburn.h b/app/paint/gimpdodgeburn.h new file mode 100644 index 0000000000..b394206424 --- /dev/null +++ b/app/paint/gimpdodgeburn.h @@ -0,0 +1,42 @@ +/* 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. + */ +#ifndef __DODGEBURN_H__ +#define __DODGEBURN_H__ + +#include "paint_core.h" +#include "tools.h" + +typedef enum +{ + DODGE, + BURN +} DodgeBurnType; + +typedef enum +{ + DODGEBURN_HIGHLIGHTS, + DODGEBURN_MIDTONES, + DODGEBURN_SHADOWS +} DodgeBurnMode; + +void * dodgeburn_paint_func (PaintCore *, GimpDrawable *, int); +gboolean dodgeburn_non_gui (GimpDrawable *, double, int, double *); +Tool * tools_new_dodgeburn (void); +void tools_free_dodgeburn (Tool *); + +#endif /* __DODGEBURN_H__ */ diff --git a/app/paint/gimpsmudge.c b/app/paint/gimpsmudge.c new file mode 100644 index 0000000000..06d4ee2631 --- /dev/null +++ b/app/paint/gimpsmudge.c @@ -0,0 +1,423 @@ +/* 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 "gdk/gdkkeysyms.h" +#include "appenv.h" +#include "drawable.h" +#include "errors.h" +#include "smudge.h" +#include "gdisplay.h" +#include "gimplut.h" +#include "paint_funcs.h" +#include "paint_core.h" +#include "paint_options.h" +#include "selection.h" +#include "tool_options_ui.h" +#include "tools.h" +#include "gimage.h" + +#define ROUND(x) (int)((x) + .5) + +#include "libgimp/gimpintl.h" + +/* the smudge structures */ + +typedef struct _SmudgeOptions SmudgeOptions; +struct _SmudgeOptions +{ + PaintOptions paint_options; + + double pressure; + double pressure_d; + GtkObject *pressure_w; + +}; + +static PixelRegion accumPR; +static unsigned char *accum_data; + +/* the smudge tool options */ +static SmudgeOptions * smudge_options = NULL; + +static void smudge_motion (PaintCore *, GimpDrawable *); +static void smudge_init (PaintCore *, GimpDrawable *); +static void smudge_finish (PaintCore *, GimpDrawable *); + +static void +smudge_nonclipped_painthit_coords (PaintCore *paint_core, + gint * x, gint* y, gint* w, gint *h); +static void +smudge_allocate_accum_buffer ( gint w, gint h, + gint bytes, gint do_fill); + +/* functions */ + +static void +smudge_options_reset (void) +{ + SmudgeOptions *options = smudge_options; + + paint_options_reset ((PaintOptions *) options); + + gtk_adjustment_set_value (GTK_ADJUSTMENT (options->pressure_w), + options->pressure_d); +} + +static SmudgeOptions * +smudge_options_new (void) +{ + SmudgeOptions *options; + + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *scale; + + /* the new smudge tool options structure */ + options = (SmudgeOptions *) g_malloc (sizeof (SmudgeOptions)); + paint_options_init ((PaintOptions *) options, + SMUDGE, + smudge_options_reset); + + options->pressure = options->pressure_d = 50.0; + + /* the main vbox */ + vbox = ((ToolOptions *) options)->main_vbox; + + /* the pressure scale */ + hbox = gtk_hbox_new (FALSE, 4); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + + label = gtk_label_new (_("Pressure:")); + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + 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_box_pack_start (GTK_BOX (hbox), scale, TRUE, TRUE, 0); + 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", + (GtkSignalFunc) tool_options_double_adjustment_update, + &options->pressure); + gtk_widget_show (scale); + gtk_widget_show (hbox); + + return options; +} + +void * +smudge_paint_func (PaintCore *paint_core, + GimpDrawable *drawable, + int state) +{ + switch (state) + { + case INIT_PAINT: + smudge_init (paint_core, drawable); + break; + case MOTION_PAINT: + smudge_motion (paint_core, drawable); + break; + case FINISH_PAINT: + smudge_finish (paint_core, drawable); + break; + } + + return NULL; +} + +static void +smudge_finish ( PaintCore *paint_core, + GimpDrawable * drawable) +{ + if (accum_data) + { + g_free (accum_data); + accum_data = NULL; + } +} + +static void +smudge_nonclipped_painthit_coords (PaintCore *paint_core, + gint * x, gint* y, gint* w, gint *h) +{ + /* Note: these are the brush mask size plus a border of 1 pixel */ + *x = (gint) paint_core->curx - paint_core->brush->mask->width/2 - 1; + *y = (gint) paint_core->cury - paint_core->brush->mask->height/2 - 1; + *w = paint_core->brush->mask->width + 2; + *h = paint_core->brush->mask->height + 2; +} + +static void +smudge_init ( PaintCore *paint_core, + GimpDrawable * drawable) +{ + GImage *gimage; + TempBuf * area; + PixelRegion srcPR; + gint x,y,w,h; + gint was_clipped; + + /* adjust the x and y coordinates to the upper left corner of the brush */ + smudge_nonclipped_painthit_coords (paint_core, &x, &y, &w, &h); + + if (! (gimage = drawable_gimage (drawable))) + return; + + /* If the image type is indexed, don't smudge */ + if ((drawable_type (drawable) == INDEXED_GIMAGE) || + (drawable_type (drawable) == INDEXEDA_GIMAGE)) + return; + + area = paint_core_get_paint_area (paint_core, drawable); + + if (!area) + was_clipped = TRUE; + else if (x != area->x || y != area->y || w != area->width || h != area->height) + was_clipped = TRUE; + else + was_clipped = FALSE; + + smudge_allocate_accum_buffer (w,h, + drawable_bytes(drawable), was_clipped); + + if (!area) return; + + accumPR.x = area->x - x; + accumPR.y = area->y - y; + accumPR.w = area->width; + accumPR.h = area->height; + accumPR.rowstride = accumPR.bytes * w; + accumPR.data = accum_data + + accumPR.rowstride * accumPR.y + + accumPR.x * accumPR.bytes; + + pixel_region_init (&srcPR, drawable_data (drawable), + area->x, area->y, area->width, area->height, FALSE); + + /* copy the region under the original painthit. */ + copy_region (&srcPR, &accumPR); + + accumPR.x = 0; + accumPR.y = 0; + accumPR.w = area->width; + accumPR.h = area->height; + accumPR.rowstride = accumPR.bytes * accumPR.w; + accumPR.data = accum_data; +} + +static void +smudge_allocate_accum_buffer ( + gint w, + gint h, + gint bytes, + gint do_fill + ) +{ + /* Allocate the accumulation buffer */ + accumPR.bytes = bytes; + accum_data = g_malloc (w * h * bytes); + + if (do_fill) + { + guchar color[3] = {0,0,0}; + accumPR.x = 0; + accumPR.y = 0; + accumPR.w = w; + accumPR.h = h; + accumPR.rowstride = accumPR.bytes * w; + accumPR.data = accum_data; + color_region (&accumPR, (const guchar*)&color); + } +} + +Tool * +tools_new_smudge () +{ + Tool * tool; + PaintCore * private; + + /* The tool options */ + if (! smudge_options) + { + smudge_options = smudge_options_new (); + tools_register (SMUDGE, (ToolOptions *) smudge_options); + + /* press all default buttons */ + smudge_options_reset (); + } + + tool = paint_core_new (SMUDGE); + /*tool->modifier_key_func = smudge_modifier_key_func;*/ + + private = (PaintCore *) tool->private; + private->paint_func = smudge_paint_func; + + return tool; +} + +void +tools_free_smudge (Tool *tool) +{ + paint_core_free (tool); +} + +static void +smudge_motion (PaintCore *paint_core, + GimpDrawable *drawable) +{ + GImage *gimage; + TempBuf * area; + PixelRegion srcPR, destPR, tempPR; + gfloat pressure; + gfloat brush_opacity; + gint x,y,w,h; + + if (! (gimage = drawable_gimage (drawable))) + return; + + /* If the image type is indexed, don't smudge */ + if ((drawable_type (drawable) == INDEXED_GIMAGE) || + (drawable_type (drawable) == INDEXEDA_GIMAGE)) + return; + + smudge_nonclipped_painthit_coords (paint_core, &x, &y, &w, &h); + + /* Get the paint area */ + if (! (area = paint_core_get_paint_area (paint_core, drawable))) + return; + + /* srcPR will be the pixels under the current painthit from + the drawable*/ + + pixel_region_init (&srcPR, drawable_data (drawable), + area->x, area->y, area->width, area->height, FALSE); + + brush_opacity = PAINT_OPTIONS_GET_OPACITY (smudge_options); + pressure = (smudge_options->pressure)/100.0; + + /* The tempPR will be the built up buffer (for smudge) */ + tempPR.bytes = accumPR.bytes; + tempPR.rowstride = accumPR.rowstride; + tempPR.x = area->x - x; + tempPR.y = area->y - y; + tempPR.w = area->width; + tempPR.h = area->height; + tempPR.data = accum_data + + tempPR.rowstride * tempPR.y + + tempPR.x * tempPR.bytes; + + /* The dest will be the paint area we got above (= canvas_buf) */ + + destPR.bytes = area->bytes; + destPR.x = 0; destPR.y = 0; + destPR.w = area->width; + destPR.h = area->height; + destPR.rowstride = area->width * area->bytes; + destPR.data = temp_buf_data (area); + + /* + Smudge uses the buffer Accum. + For each successive painthit Accum is built like this + Accum = pressure*Accum + (1-pressure)*I. + where I is the pixels under the current painthit. + Then the paint area (canvas_buf) is built as + (Accum,1) (if no alpha), + */ + + blend_region (&srcPR, &tempPR, &tempPR, ROUND(pressure * 255.0)); + + /* re-init the tempPR */ + + tempPR.bytes = accumPR.bytes; + tempPR.rowstride = accumPR.rowstride; + tempPR.x = area->x - x; + tempPR.y = area->y - y; + tempPR.w = area->width; + tempPR.h = area->height; + tempPR.data = accum_data + + tempPR.rowstride * tempPR.y + + tempPR.x * tempPR.bytes; + + if (!drawable_has_alpha (drawable)) + add_alpha_region (&tempPR, &destPR); + else + copy_region(&tempPR, &destPR); + + /*Replace the newly made paint area to the gimage*/ + paint_core_replace_canvas (paint_core, drawable, ROUND(brush_opacity * 255.0), + OPAQUE_OPACITY, PRESSURE, INCREMENTAL); + +} + +static void * +smudge_non_gui_paint_func (PaintCore *paint_core, + GimpDrawable *drawable, + int state) +{ + smudge_motion (paint_core, drawable); + + return NULL; +} + +gboolean +smudge_non_gui (GimpDrawable *drawable, + double pressure, + int num_strokes, + double *stroke_array) +{ + int 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 = smudge_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) + smudge_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; +} diff --git a/app/paint/gimpsmudge.h b/app/paint/gimpsmudge.h new file mode 100644 index 0000000000..ab7fb82634 --- /dev/null +++ b/app/paint/gimpsmudge.h @@ -0,0 +1,42 @@ +/* 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. + */ +#ifndef __SMUDGE_H__ +#define __SMUDGE_H__ + +#include "paint_core.h" +#include "tools.h" + +typedef enum +{ + SMUDGE_TYPE_SMUDGE, + SMUDGE_TYPE_STREAK +} SmudgeType; + +typedef enum +{ + SMUDGE_MODE_HIGHLIGHTS, + SMUDGE_MODE_MIDTONES, + SMUDGE_MODE_SHADOWS +} SmudgeMode; + +void * smudge_paint_func (PaintCore *, GimpDrawable *, int); +gboolean smudge_non_gui (GimpDrawable *, double, int, double *); +Tool * tools_new_smudge (void); +void tools_free_smudge (Tool *); + +#endif /* __SMUDGE_H__ */ diff --git a/app/paint_core.c b/app/paint_core.c index 5f7a6243ad..e54b5b0345 100644 --- a/app/paint_core.c +++ b/app/paint_core.c @@ -58,8 +58,9 @@ static void paint_core_paste (PaintCore *, MaskBuf *, GimpDrawable *, int, int, int, int); static void paint_core_replace (PaintCore *, MaskBuf *, GimpDrawable *, int, int, int); -static void paint_to_canvas_tiles (PaintCore *, MaskBuf *, int); -static void paint_to_canvas_buf (PaintCore *, MaskBuf *, int); +static void brush_to_canvas_tiles (PaintCore *, MaskBuf *, int); +static void canvas_tiles_to_canvas_buf (PaintCore *); +static void brush_to_canvas_buf (PaintCore *, MaskBuf *, int); static void set_undo_tiles (GimpDrawable *, int, int, int, int); static void set_canvas_tiles (int, int, int, int); static int paint_core_invalidate_cache (GimpBrush *brush, gpointer *blah); @@ -1193,6 +1194,7 @@ paint_core_solidify_mask (MaskBuf *brush_mask) last_brush = brush_mask; if (solid_brush) mask_buf_free (solid_brush); + solid_brush = mask_buf_new (brush_mask->width + 2, brush_mask->height + 2); /* get the data and advance one line into it */ @@ -1268,14 +1270,15 @@ paint_core_paste (PaintCore *paint_core, set_canvas_tiles (canvas_buf->x, canvas_buf->y, canvas_buf->width, canvas_buf->height); - paint_to_canvas_tiles (paint_core, brush_mask, brush_opacity); + brush_to_canvas_tiles (paint_core, brush_mask, brush_opacity); + canvas_tiles_to_canvas_buf (paint_core); alt = undo_tiles; } /* Otherwise: * combine the canvas buf and the brush mask to the canvas buf */ else /* mode != CONSTANT */ - paint_to_canvas_buf (paint_core, brush_mask, brush_opacity); + brush_to_canvas_buf (paint_core, brush_mask, brush_opacity); /* intialize canvas buf source pixel regions */ srcPR.bytes = canvas_buf->bytes; @@ -1324,6 +1327,7 @@ paint_core_replace (PaintCore *paint_core, { GImage *gimage; PixelRegion srcPR, maskPR; + TileManager *alt = NULL; int offx, offy; if (!drawable_has_alpha (drawable)) @@ -1334,12 +1338,6 @@ paint_core_replace (PaintCore *paint_core, return; } - if (mode != INCREMENTAL) - { - g_message (_("paint_core_replace only works in INCREMENTAL mode")); - return; - } - if (! (gimage = drawable_gimage (drawable))) return; @@ -1348,12 +1346,33 @@ paint_core_replace (PaintCore *paint_core, canvas_buf->x, canvas_buf->y, canvas_buf->width, canvas_buf->height); - maskPR.bytes = 1; - maskPR.x = 0; maskPR.y = 0; - maskPR.w = canvas_buf->width; - maskPR.h = canvas_buf->height; - maskPR.rowstride = maskPR.bytes * brush_mask->width; - maskPR.data = mask_buf_data (brush_mask); + if (mode == CONSTANT) + { + /* initialize any invalid canvas tiles */ + set_canvas_tiles (canvas_buf->x, canvas_buf->y, + canvas_buf->width, canvas_buf->height); + + /* combine the brush mask and the canvas tiles */ + brush_to_canvas_tiles (paint_core, brush_mask, brush_opacity); + + /* set the alt source as the unaltered undo_tiles */ + alt = undo_tiles; + + /* initialize the maskPR from the canvas tiles */ + pixel_region_init (&maskPR, canvas_tiles, + canvas_buf->x, canvas_buf->y, + canvas_buf->width, canvas_buf->height, FALSE); + } + else + { + /* The mask is just the brush mask */ + maskPR.bytes = 1; + maskPR.x = 0; maskPR.y = 0; + maskPR.w = canvas_buf->width; + maskPR.h = canvas_buf->height; + maskPR.rowstride = maskPR.bytes * brush_mask->width; + maskPR.data = mask_buf_data (brush_mask); + } /* intialize canvas buf source pixel regions */ srcPR.bytes = canvas_buf->bytes; @@ -1366,7 +1385,7 @@ paint_core_replace (PaintCore *paint_core, /* apply the paint area to the gimage */ gimage_replace_image (gimage, drawable, &srcPR, FALSE, image_opacity, - &maskPR, + &maskPR, canvas_buf->x, canvas_buf->y); /* Update the undo extents */ @@ -1384,6 +1403,95 @@ paint_core_replace (PaintCore *paint_core, canvas_buf->width, canvas_buf->height); } +static void +canvas_tiles_to_canvas_buf(PaintCore *paint_core) +{ + PixelRegion srcPR, maskPR; + + /* combine the canvas tiles and the canvas buf */ + srcPR.bytes = canvas_buf->bytes; + srcPR.x = 0; srcPR.y = 0; + srcPR.w = canvas_buf->width; + srcPR.h = canvas_buf->height; + srcPR.rowstride = canvas_buf->width * canvas_buf->bytes; + srcPR.data = temp_buf_data (canvas_buf); + + pixel_region_init (&maskPR, canvas_tiles, + canvas_buf->x, canvas_buf->y, + canvas_buf->width, canvas_buf->height, FALSE); + + /* apply the canvas tiles to the canvas buf */ + apply_mask_to_region (&srcPR, &maskPR, OPAQUE_OPACITY); +} + +static void +brush_to_canvas_tiles ( + PaintCore *paint_core, + MaskBuf *brush_mask, + int brush_opacity + ) +{ + PixelRegion srcPR, maskPR; + int x, y; + int xoff, yoff; + + /* combine the brush mask and the canvas tiles */ + pixel_region_init (&srcPR, canvas_tiles, + canvas_buf->x, canvas_buf->y, + canvas_buf->width, canvas_buf->height, TRUE); + + x = (int) paint_core->curx - (brush_mask->width >> 1); + y = (int) paint_core->cury - (brush_mask->height >> 1); + xoff = (x < 0) ? -x : 0; + yoff = (y < 0) ? -y : 0; + + maskPR.bytes = 1; + maskPR.x = 0; maskPR.y = 0; + maskPR.w = srcPR.w; + maskPR.h = srcPR.h; + maskPR.rowstride = maskPR.bytes * brush_mask->width; + maskPR.data = mask_buf_data (brush_mask) + yoff * maskPR.rowstride + xoff * maskPR.bytes; + + /* combine the mask to the canvas tiles */ + combine_mask_and_region (&srcPR, &maskPR, brush_opacity); +} + +static void +brush_to_canvas_buf ( + PaintCore *paint_core, + MaskBuf *brush_mask, + int brush_opacity + ) +{ + PixelRegion srcPR, maskPR; + int x, y; + int xoff, yoff; + + x = (int) paint_core->curx - (brush_mask->width >> 1); + y = (int) paint_core->cury - (brush_mask->height >> 1); + xoff = (x < 0) ? -x : 0; + yoff = (y < 0) ? -y : 0; + + /* combine the canvas buf and the brush mask to the canvas buf */ + srcPR.bytes = canvas_buf->bytes; + srcPR.x = 0; srcPR.y = 0; + srcPR.w = canvas_buf->width; + srcPR.h = canvas_buf->height; + srcPR.rowstride = canvas_buf->width * canvas_buf->bytes; + srcPR.data = temp_buf_data (canvas_buf); + + maskPR.bytes = 1; + maskPR.x = 0; maskPR.y = 0; + maskPR.w = srcPR.w; + maskPR.h = srcPR.h; + maskPR.rowstride = maskPR.bytes * brush_mask->width; + maskPR.data = mask_buf_data (brush_mask) + yoff * maskPR.rowstride + xoff * maskPR.bytes; + + /* apply the mask */ + apply_mask_to_region (&srcPR, &maskPR, brush_opacity); +} + +#if 0 static void paint_to_canvas_tiles (PaintCore *paint_core, MaskBuf *brush_mask, @@ -1462,6 +1570,7 @@ paint_to_canvas_buf (PaintCore *paint_core, /* apply the mask */ apply_mask_to_region (&srcPR, &maskPR, brush_opacity); } +#endif static void set_undo_tiles (GimpDrawable *drawable, diff --git a/app/paint_funcs.c b/app/paint_funcs.c index c33b46f888..f1af10d3ca 100644 --- a/app/paint_funcs.c +++ b/app/paint_funcs.c @@ -672,15 +672,11 @@ blend_pixels (const unsigned char *src1, int alpha, b; unsigned char blend2 = (255 - blend); - alpha = (has_alpha) ? bytes - 1 : bytes; while (w --) { - for (b = 0; b < alpha; b++) + for (b = 0; b < bytes; b++) dest[b] = (src1[b] * blend2 + src2[b] * blend) / 255; - if (has_alpha) - dest[alpha] = src1[alpha]; /* alpha channel--assume src2 has none */ - src1 += bytes; src2 += bytes; dest += bytes; @@ -3102,19 +3098,23 @@ blend_region (PixelRegion *src1, int blend) { int h; - unsigned char * s1, * s2, * d; + unsigned char *s1, *s2, * d; + void * pr; - s1 = src1->data; - s2 = src2->data; - d = dest->data; - h = src1->h; - - while (h --) + for (pr = pixel_regions_register (3, src1, src2, dest); pr != NULL; pr = pixel_regions_process (pr)) { -/* blend_pixels (s1, s2, d, blend, src1->w, src1->bytes);*/ - s1 += src1->rowstride; - s2 += src2->rowstride; - d += dest->rowstride; + s1 = src1->data; + s2 = src2->data; + d = dest->data; + h = src1->h; + + while (h --) + { + blend_pixels (s1, s2, d, blend, src1->w, src1->bytes, FALSE); + s1 += src1->rowstride; + s2 += src2->rowstride; + d += dest->rowstride; + } } } diff --git a/app/pixmaps2.h b/app/pixmaps2.h index 25e20186d9..78c09c8ff6 100644 --- a/app/pixmaps2.h +++ b/app/pixmaps2.h @@ -879,3 +879,63 @@ static char *ink_bits [] = "......................" }; +#define dodge_width 22 +#define dodge_height 22 +static char *dodge_bits [] = +{ + "......................", + "......................", + "......................", + "......................", + "......................", + ".......aaa............", + ".....aaaaaaa..........", + ".....aaaaaaa..........", + "....aaaaaaaaa.........", + "....aaaaaaaaa.........", + "....aaaaaaaaa.........", + ".....aaaaaaa..........", + ".....aaaaaaa..........", + ".......aaa..a.........", + ".............a........", + "..............a.......", + "...............a......", + "................a.....", + "......................", + "......................", + "......................", + "......................" +}; + + +/* GIMP icon image format -- S. Kimball, P. Mattis */ +/* Image name: smudge */ + + +#define smudge_width 22 +#define smudge_height 22 +static char *smudge_bits [] = +{ + "......................", + "......................", + "......................", + ".........a......a.....", + "........a.......a.....", + ".......a........a.....", + "......a.a........a....", + ".....a.a.a.......a....", + ".....aa.a.a......a....", + ".....a.a.a...a...a....", + ".....aa.a...aa...a....", + "......a.a..a.a...a....", + "......aa..aaa...a.....", + ".......a..a....a......", + "......a..aaaaaa.......", + "......a..a............", + ".....a..a.............", + "....a..a..............", + "....aaa...............", + "......................", + "......................", + "......................" +}; diff --git a/app/smudge.c b/app/smudge.c new file mode 100644 index 0000000000..06d4ee2631 --- /dev/null +++ b/app/smudge.c @@ -0,0 +1,423 @@ +/* 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 "gdk/gdkkeysyms.h" +#include "appenv.h" +#include "drawable.h" +#include "errors.h" +#include "smudge.h" +#include "gdisplay.h" +#include "gimplut.h" +#include "paint_funcs.h" +#include "paint_core.h" +#include "paint_options.h" +#include "selection.h" +#include "tool_options_ui.h" +#include "tools.h" +#include "gimage.h" + +#define ROUND(x) (int)((x) + .5) + +#include "libgimp/gimpintl.h" + +/* the smudge structures */ + +typedef struct _SmudgeOptions SmudgeOptions; +struct _SmudgeOptions +{ + PaintOptions paint_options; + + double pressure; + double pressure_d; + GtkObject *pressure_w; + +}; + +static PixelRegion accumPR; +static unsigned char *accum_data; + +/* the smudge tool options */ +static SmudgeOptions * smudge_options = NULL; + +static void smudge_motion (PaintCore *, GimpDrawable *); +static void smudge_init (PaintCore *, GimpDrawable *); +static void smudge_finish (PaintCore *, GimpDrawable *); + +static void +smudge_nonclipped_painthit_coords (PaintCore *paint_core, + gint * x, gint* y, gint* w, gint *h); +static void +smudge_allocate_accum_buffer ( gint w, gint h, + gint bytes, gint do_fill); + +/* functions */ + +static void +smudge_options_reset (void) +{ + SmudgeOptions *options = smudge_options; + + paint_options_reset ((PaintOptions *) options); + + gtk_adjustment_set_value (GTK_ADJUSTMENT (options->pressure_w), + options->pressure_d); +} + +static SmudgeOptions * +smudge_options_new (void) +{ + SmudgeOptions *options; + + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *scale; + + /* the new smudge tool options structure */ + options = (SmudgeOptions *) g_malloc (sizeof (SmudgeOptions)); + paint_options_init ((PaintOptions *) options, + SMUDGE, + smudge_options_reset); + + options->pressure = options->pressure_d = 50.0; + + /* the main vbox */ + vbox = ((ToolOptions *) options)->main_vbox; + + /* the pressure scale */ + hbox = gtk_hbox_new (FALSE, 4); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + + label = gtk_label_new (_("Pressure:")); + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + 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_box_pack_start (GTK_BOX (hbox), scale, TRUE, TRUE, 0); + 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", + (GtkSignalFunc) tool_options_double_adjustment_update, + &options->pressure); + gtk_widget_show (scale); + gtk_widget_show (hbox); + + return options; +} + +void * +smudge_paint_func (PaintCore *paint_core, + GimpDrawable *drawable, + int state) +{ + switch (state) + { + case INIT_PAINT: + smudge_init (paint_core, drawable); + break; + case MOTION_PAINT: + smudge_motion (paint_core, drawable); + break; + case FINISH_PAINT: + smudge_finish (paint_core, drawable); + break; + } + + return NULL; +} + +static void +smudge_finish ( PaintCore *paint_core, + GimpDrawable * drawable) +{ + if (accum_data) + { + g_free (accum_data); + accum_data = NULL; + } +} + +static void +smudge_nonclipped_painthit_coords (PaintCore *paint_core, + gint * x, gint* y, gint* w, gint *h) +{ + /* Note: these are the brush mask size plus a border of 1 pixel */ + *x = (gint) paint_core->curx - paint_core->brush->mask->width/2 - 1; + *y = (gint) paint_core->cury - paint_core->brush->mask->height/2 - 1; + *w = paint_core->brush->mask->width + 2; + *h = paint_core->brush->mask->height + 2; +} + +static void +smudge_init ( PaintCore *paint_core, + GimpDrawable * drawable) +{ + GImage *gimage; + TempBuf * area; + PixelRegion srcPR; + gint x,y,w,h; + gint was_clipped; + + /* adjust the x and y coordinates to the upper left corner of the brush */ + smudge_nonclipped_painthit_coords (paint_core, &x, &y, &w, &h); + + if (! (gimage = drawable_gimage (drawable))) + return; + + /* If the image type is indexed, don't smudge */ + if ((drawable_type (drawable) == INDEXED_GIMAGE) || + (drawable_type (drawable) == INDEXEDA_GIMAGE)) + return; + + area = paint_core_get_paint_area (paint_core, drawable); + + if (!area) + was_clipped = TRUE; + else if (x != area->x || y != area->y || w != area->width || h != area->height) + was_clipped = TRUE; + else + was_clipped = FALSE; + + smudge_allocate_accum_buffer (w,h, + drawable_bytes(drawable), was_clipped); + + if (!area) return; + + accumPR.x = area->x - x; + accumPR.y = area->y - y; + accumPR.w = area->width; + accumPR.h = area->height; + accumPR.rowstride = accumPR.bytes * w; + accumPR.data = accum_data + + accumPR.rowstride * accumPR.y + + accumPR.x * accumPR.bytes; + + pixel_region_init (&srcPR, drawable_data (drawable), + area->x, area->y, area->width, area->height, FALSE); + + /* copy the region under the original painthit. */ + copy_region (&srcPR, &accumPR); + + accumPR.x = 0; + accumPR.y = 0; + accumPR.w = area->width; + accumPR.h = area->height; + accumPR.rowstride = accumPR.bytes * accumPR.w; + accumPR.data = accum_data; +} + +static void +smudge_allocate_accum_buffer ( + gint w, + gint h, + gint bytes, + gint do_fill + ) +{ + /* Allocate the accumulation buffer */ + accumPR.bytes = bytes; + accum_data = g_malloc (w * h * bytes); + + if (do_fill) + { + guchar color[3] = {0,0,0}; + accumPR.x = 0; + accumPR.y = 0; + accumPR.w = w; + accumPR.h = h; + accumPR.rowstride = accumPR.bytes * w; + accumPR.data = accum_data; + color_region (&accumPR, (const guchar*)&color); + } +} + +Tool * +tools_new_smudge () +{ + Tool * tool; + PaintCore * private; + + /* The tool options */ + if (! smudge_options) + { + smudge_options = smudge_options_new (); + tools_register (SMUDGE, (ToolOptions *) smudge_options); + + /* press all default buttons */ + smudge_options_reset (); + } + + tool = paint_core_new (SMUDGE); + /*tool->modifier_key_func = smudge_modifier_key_func;*/ + + private = (PaintCore *) tool->private; + private->paint_func = smudge_paint_func; + + return tool; +} + +void +tools_free_smudge (Tool *tool) +{ + paint_core_free (tool); +} + +static void +smudge_motion (PaintCore *paint_core, + GimpDrawable *drawable) +{ + GImage *gimage; + TempBuf * area; + PixelRegion srcPR, destPR, tempPR; + gfloat pressure; + gfloat brush_opacity; + gint x,y,w,h; + + if (! (gimage = drawable_gimage (drawable))) + return; + + /* If the image type is indexed, don't smudge */ + if ((drawable_type (drawable) == INDEXED_GIMAGE) || + (drawable_type (drawable) == INDEXEDA_GIMAGE)) + return; + + smudge_nonclipped_painthit_coords (paint_core, &x, &y, &w, &h); + + /* Get the paint area */ + if (! (area = paint_core_get_paint_area (paint_core, drawable))) + return; + + /* srcPR will be the pixels under the current painthit from + the drawable*/ + + pixel_region_init (&srcPR, drawable_data (drawable), + area->x, area->y, area->width, area->height, FALSE); + + brush_opacity = PAINT_OPTIONS_GET_OPACITY (smudge_options); + pressure = (smudge_options->pressure)/100.0; + + /* The tempPR will be the built up buffer (for smudge) */ + tempPR.bytes = accumPR.bytes; + tempPR.rowstride = accumPR.rowstride; + tempPR.x = area->x - x; + tempPR.y = area->y - y; + tempPR.w = area->width; + tempPR.h = area->height; + tempPR.data = accum_data + + tempPR.rowstride * tempPR.y + + tempPR.x * tempPR.bytes; + + /* The dest will be the paint area we got above (= canvas_buf) */ + + destPR.bytes = area->bytes; + destPR.x = 0; destPR.y = 0; + destPR.w = area->width; + destPR.h = area->height; + destPR.rowstride = area->width * area->bytes; + destPR.data = temp_buf_data (area); + + /* + Smudge uses the buffer Accum. + For each successive painthit Accum is built like this + Accum = pressure*Accum + (1-pressure)*I. + where I is the pixels under the current painthit. + Then the paint area (canvas_buf) is built as + (Accum,1) (if no alpha), + */ + + blend_region (&srcPR, &tempPR, &tempPR, ROUND(pressure * 255.0)); + + /* re-init the tempPR */ + + tempPR.bytes = accumPR.bytes; + tempPR.rowstride = accumPR.rowstride; + tempPR.x = area->x - x; + tempPR.y = area->y - y; + tempPR.w = area->width; + tempPR.h = area->height; + tempPR.data = accum_data + + tempPR.rowstride * tempPR.y + + tempPR.x * tempPR.bytes; + + if (!drawable_has_alpha (drawable)) + add_alpha_region (&tempPR, &destPR); + else + copy_region(&tempPR, &destPR); + + /*Replace the newly made paint area to the gimage*/ + paint_core_replace_canvas (paint_core, drawable, ROUND(brush_opacity * 255.0), + OPAQUE_OPACITY, PRESSURE, INCREMENTAL); + +} + +static void * +smudge_non_gui_paint_func (PaintCore *paint_core, + GimpDrawable *drawable, + int state) +{ + smudge_motion (paint_core, drawable); + + return NULL; +} + +gboolean +smudge_non_gui (GimpDrawable *drawable, + double pressure, + int num_strokes, + double *stroke_array) +{ + int 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 = smudge_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) + smudge_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; +} diff --git a/app/smudge.h b/app/smudge.h new file mode 100644 index 0000000000..ab7fb82634 --- /dev/null +++ b/app/smudge.h @@ -0,0 +1,42 @@ +/* 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. + */ +#ifndef __SMUDGE_H__ +#define __SMUDGE_H__ + +#include "paint_core.h" +#include "tools.h" + +typedef enum +{ + SMUDGE_TYPE_SMUDGE, + SMUDGE_TYPE_STREAK +} SmudgeType; + +typedef enum +{ + SMUDGE_MODE_HIGHLIGHTS, + SMUDGE_MODE_MIDTONES, + SMUDGE_MODE_SHADOWS +} SmudgeMode; + +void * smudge_paint_func (PaintCore *, GimpDrawable *, int); +gboolean smudge_non_gui (GimpDrawable *, double, int, double *); +Tool * tools_new_smudge (void); +void tools_free_smudge (Tool *); + +#endif /* __SMUDGE_H__ */ diff --git a/app/tool_options.c b/app/tool_options.c index 7d7746b9cd..50298fbd88 100644 --- a/app/tool_options.c +++ b/app/tool_options.c @@ -570,7 +570,11 @@ paint_options_init (PaintOptions *options, N_("Convolver Options") : ((tool_type == INK) ? N_("Ink Options") : - N_("ERROR: Unknown Paint Type")))))))))), + ((tool_type == DODGEBURN) ? + N_("Dodge or Burn Options") : + ((tool_type == SMUDGE) ? + N_("Smudge Options") : + N_("ERROR: Unknown Paint Type")))))))))))), reset_func); /* initialize the paint options structure */ diff --git a/app/tools.c b/app/tools.c index 37de4c3761..4cc92235a6 100644 --- a/app/tools.c +++ b/app/tools.c @@ -31,6 +31,7 @@ #include "crop.h" #include "curves.h" #include "devices.h" +#include "dodgeburn.h" #include "eraser.h" #include "gdisplay.h" #include "hue_saturation.h" @@ -49,6 +50,7 @@ #include "posterize.h" #include "rect_select.h" #include "session.h" +#include "smudge.h" #include "text_tool.h" #include "threshold.h" #include "tools.h" @@ -448,11 +450,41 @@ ToolInfo tool_info[] = NULL }, + { + NULL, + N_("Dodge or Burn"), + 22, + N_("/Tools/DodgeBurn"), + "D", + (char **) dodge_bits, + N_("Dodge or Burn"), + "ContextHelp/dodgeburn", + DODGEBURN, + tools_new_dodgeburn, + tools_free_dodgeburn, + NULL + }, + + { + NULL, + N_("Smudge"), + 23, + N_("/Tools/Smudge"), + "S", + (char **) smudge_bits, + N_("Smudge"), + "ContextHelp/smudge", + SMUDGE, + tools_new_smudge, + tools_free_smudge, + NULL + }, + /* Non-toolbox tools */ { NULL, N_("By Color Select"), - 22, + 24, N_("/Select/By Color..."), NULL, NULL, @@ -467,7 +499,7 @@ ToolInfo tool_info[] = { NULL, N_("Color Balance"), - 23, + 25, N_("/Image/Colors/Color Balance"), NULL, NULL, @@ -482,7 +514,7 @@ ToolInfo tool_info[] = { NULL, N_("Brightness-Contrast"), - 24, + 26, N_("/Image/Colors/Brightness-Contrast"), NULL, NULL, @@ -497,7 +529,7 @@ ToolInfo tool_info[] = { NULL, N_("Hue-Saturation"), - 25, + 27, N_("/Image/Colors/Hue-Saturation"), NULL, NULL, @@ -512,7 +544,7 @@ ToolInfo tool_info[] = { NULL, N_("Posterize"), - 26, + 28, N_("/Image/Colors/Posterize"), NULL, NULL, @@ -527,7 +559,7 @@ ToolInfo tool_info[] = { NULL, N_("Threshold"), - 27, + 29, N_("/Image/Colors/Threshold"), NULL, NULL, @@ -542,7 +574,7 @@ ToolInfo tool_info[] = { NULL, N_("Curves"), - 28, + 30, N_("/Image/Colors/Curves"), NULL, NULL, @@ -557,7 +589,7 @@ ToolInfo tool_info[] = { NULL, N_("Levels"), - 29, + 31, N_("/Image/Colors/Levels"), NULL, NULL, @@ -572,7 +604,7 @@ ToolInfo tool_info[] = { NULL, N_("Histogram"), - 30, + 32, N_("/Image/Histogram"), NULL, NULL, diff --git a/app/tools/dodgeburn.c b/app/tools/dodgeburn.c new file mode 100644 index 0000000000..62399c7ceb --- /dev/null +++ b/app/tools/dodgeburn.c @@ -0,0 +1,521 @@ +/* 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 "gdk/gdkkeysyms.h" +#include "appenv.h" +#include "drawable.h" +#include "errors.h" +#include "dodgeburn.h" +#include "gdisplay.h" +#include "gimplut.h" +#include "paint_funcs.h" +#include "paint_core.h" +#include "paint_options.h" +#include "selection.h" +#include "tool_options_ui.h" +#include "tools.h" +#include "gimage.h" + +#include "libgimp/gimpintl.h" +#define ROUND(x) (int)((x) + .5) + +/* the dodgeburn structures */ + +typedef struct _DodgeBurnOptions DodgeBurnOptions; +struct _DodgeBurnOptions +{ + PaintOptions paint_options; + + DodgeBurnType type; + DodgeBurnType type_d; + GtkWidget *type_w[2]; + + DodgeBurnMode mode; /*highlights,midtones,shadows*/ + DodgeBurnMode mode_d; + GtkWidget *mode_w[3]; + + double exposure; + double exposure_d; + GtkObject *exposure_w; + + GimpLut *lut; +}; + +static void dodgeburn_make_luts ( PaintCore *, GimpDrawable *); + +static gfloat dodgeburn_highlights_lut_func(void *, int, int, gfloat); +static gfloat dodgeburn_midtones_lut_func(void *, int, int, gfloat); +static gfloat dodgeburn_shadows_lut_func(void *, int, int, gfloat); + +/* The dodge burn lookup tables */ +gfloat dodgeburn_highlights(void *, int, int, gfloat); +gfloat dodgeburn_midtones(void *, int, int, gfloat); +gfloat dodgeburn_shadows(void *, int, int, gfloat); + +/* the dodgeburn tool options */ +static DodgeBurnOptions * dodgeburn_options = NULL; + +static void dodgeburn_motion (PaintCore *, GimpDrawable *); +static void dodgeburn_init (PaintCore *, GimpDrawable *); +static void dodgeburn_finish (PaintCore *, GimpDrawable *); + +/* functions */ + +static void +dodgeburn_options_reset (void) +{ + DodgeBurnOptions *options = dodgeburn_options; + + paint_options_reset ((PaintOptions *) options); + + gtk_adjustment_set_value (GTK_ADJUSTMENT (options->exposure_w), + options->exposure_d); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->type_w[options->type_d]), TRUE); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->mode_w[options->mode_d]), TRUE); +} + +static DodgeBurnOptions * +dodgeburn_options_new (void) +{ + DodgeBurnOptions *options; + + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *scale; + GtkWidget *frame; + + gchar* type_label[2] = { N_("Dodge"), N_("Burn") }; + gint type_value[2] = { DODGE, BURN }; + + gchar* mode_label[3] = { N_("Highlights"), + N_("Midtones"), + N_("Shadows") }; + + gint mode_value[3] = { DODGEBURN_HIGHLIGHTS, + DODGEBURN_MIDTONES, + DODGEBURN_SHADOWS }; + + /* the new dodgeburn tool options structure */ + options = (DodgeBurnOptions *) g_malloc (sizeof (DodgeBurnOptions)); + paint_options_init ((PaintOptions *) options, + DODGEBURN, + dodgeburn_options_reset); + + options->type = options->type_d = DODGE; + options->exposure = options->exposure_d = 50.0; + options->mode = options->mode_d = DODGEBURN_HIGHLIGHTS; + + /* the main vbox */ + vbox = ((ToolOptions *) options)->main_vbox; + + /* the exposure scale */ + hbox = gtk_hbox_new (FALSE, 4); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + + label = gtk_label_new (_("Exposure:")); + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + options->exposure_w = + gtk_adjustment_new (options->exposure_d, 0.0, 100.0, 1.0, 1.0, 0.0); + scale = gtk_hscale_new (GTK_ADJUSTMENT (options->exposure_w)); + gtk_box_pack_start (GTK_BOX (hbox), scale, TRUE, TRUE, 0); + 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->exposure_w), "value_changed", + (GtkSignalFunc) tool_options_double_adjustment_update, + &options->exposure); + + gtk_widget_show (scale); + gtk_widget_show (hbox); + + /* the type (dodge or burn) */ + frame = tool_options_radio_buttons_new (_("Type"), + &options->type, + options->type_w, + type_label, + type_value, + 2); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->type_w[options->type_d]), TRUE); + gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + /* mode (highlights, midtones, or shadows) */ + frame = tool_options_radio_buttons_new (_("Mode"), + &options->mode, + options->mode_w, + mode_label, + mode_value, + 3); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->mode_w[options->mode_d]), TRUE); + gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + return options; +} + +void * +dodgeburn_paint_func (PaintCore *paint_core, + GimpDrawable *drawable, + int state) +{ + switch (state) + { + case INIT_PAINT: + dodgeburn_init (paint_core, drawable); + break; + case MOTION_PAINT: + dodgeburn_motion (paint_core, drawable); + break; + case FINISH_PAINT: + dodgeburn_finish (paint_core, drawable); + break; + } + + return NULL; +} + +static void +dodgeburn_finish ( PaintCore *paint_core, + GimpDrawable * drawable) +{ + /* Here we destroy the luts to do the painting with.*/ + if (dodgeburn_options->lut) + { + gimp_lut_free (dodgeburn_options->lut); + dodgeburn_options->lut = NULL; + } +} + +static void +dodgeburn_init ( PaintCore *paint_core, + GimpDrawable * drawable) +{ + /* Here we create the luts to do the painting with.*/ + dodgeburn_make_luts (paint_core, drawable); +} + +static void +dodgeburn_make_luts ( PaintCore *paint_core, + GimpDrawable * drawable) +{ + GimpLutFunc lut_func; + int nchannels = gimp_drawable_bytes (drawable); + gfloat exposure = (dodgeburn_options->exposure)/100.0; + + /* make the exposure negative if burn for luts*/ + if (dodgeburn_options->type == BURN) + exposure = -exposure; + + dodgeburn_options->lut = gimp_lut_new(); + + switch (dodgeburn_options->mode) + { + case DODGEBURN_HIGHLIGHTS: + lut_func = dodgeburn_highlights_lut_func; + break; + case DODGEBURN_MIDTONES: + lut_func = dodgeburn_midtones_lut_func; + break; + case DODGEBURN_SHADOWS: + lut_func = dodgeburn_shadows_lut_func; + break; + default: + lut_func = NULL; + break; + } + + gimp_lut_setup_exact (dodgeburn_options->lut, + lut_func, (void *)&exposure, + nchannels); + +} + +static void +dodgeburn_modifier_key_func (Tool *tool, + GdkEventKey *kevent, + gpointer gdisp_ptr) +{ + switch (kevent->keyval) + { + case GDK_Alt_L: case GDK_Alt_R: + break; + case GDK_Shift_L: case GDK_Shift_R: + switch (dodgeburn_options->type) + { + case BURN: + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dodgeburn_options->type_w[BURN]), TRUE); + break; + case DODGE: + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dodgeburn_options->type_w[DODGE]), TRUE); + break; + default: + break; + } + break; + case GDK_Control_L: case GDK_Control_R: + break; + } +} + +Tool * +tools_new_dodgeburn () +{ + Tool * tool; + PaintCore * private; + + /* The tool options */ + if (! dodgeburn_options) + { + dodgeburn_options = dodgeburn_options_new (); + tools_register (DODGEBURN, (ToolOptions *) dodgeburn_options); + + /* press all default buttons */ + dodgeburn_options_reset (); + } + + tool = paint_core_new (DODGEBURN); + tool->modifier_key_func = dodgeburn_modifier_key_func; + + private = (PaintCore *) tool->private; + private->paint_func = dodgeburn_paint_func; + + return tool; +} + +void +tools_free_dodgeburn (Tool *tool) +{ + /* delete any luts here */ + paint_core_free (tool); +} + +static void +dodgeburn_motion (PaintCore *paint_core, + GimpDrawable *drawable) +{ + GImage *gimage; + TempBuf * area; + TempBuf * orig; + PixelRegion srcPR, destPR, tempPR; + guchar *temp_data; + gfloat exposure; + gfloat brush_opacity; + + if (! (gimage = drawable_gimage (drawable))) + return; + + /* If the image type is indexed, don't dodgeburn */ + if ((drawable_type (drawable) == INDEXED_GIMAGE) || + (drawable_type (drawable) == INDEXEDA_GIMAGE)) + return; + + /* Get a region which can be used to paint to */ + if (! (area = paint_core_get_paint_area (paint_core, drawable))) + return; + + /* Constant painting --get a copy of the orig drawable (with + no paint from this stroke yet) */ + + { + gint x1, y1, x2, y2; + x1 = BOUNDS (area->x, 0, drawable_width (drawable)); + y1 = BOUNDS (area->y, 0, drawable_height (drawable)); + x2 = BOUNDS (area->x + area->width, 0, drawable_width (drawable)); + y2 = BOUNDS (area->y + area->height, 0, drawable_height (drawable)); + + if (!(x2 - x1) || !(y2 - y1)) + return; + + /* get the original untouched image */ + orig = paint_core_get_orig_image (paint_core, drawable, x1, y1, x2, y2); + srcPR.bytes = orig->bytes; + srcPR.x = 0; + srcPR.y = 0; + srcPR.w = x2 - x1; + srcPR.h = y2 - y1; + srcPR.rowstride = srcPR.bytes * orig->width; + srcPR.data = temp_buf_data (orig); + } + + /* tempPR will hold the dodgeburned region*/ + tempPR.bytes = srcPR.bytes; + tempPR.x = srcPR.x; + tempPR.y = srcPR.y; + tempPR.w = srcPR.w; + tempPR.h = srcPR.h; + tempPR.rowstride = tempPR.bytes * tempPR.w; + temp_data = g_malloc (tempPR.h * tempPR.rowstride); + tempPR.data = temp_data; + + brush_opacity = PAINT_OPTIONS_GET_OPACITY (dodgeburn_options); + exposure = (dodgeburn_options->exposure)/100.0; + + /* DodgeBurn the region */ + gimp_lut_process (dodgeburn_options->lut, &srcPR, &tempPR); + + /* The dest is the paint area we got above (= canvas_buf) */ + destPR.bytes = area->bytes; + destPR.x = 0; destPR.y = 0; + destPR.w = area->width; + destPR.h = area->height; + destPR.rowstride = area->width * destPR.bytes; + destPR.data = temp_buf_data (area); + + /* Now add an alpha to the dodgeburned region + and put this in area = canvas_buf */ + if (!drawable_has_alpha (drawable)) + add_alpha_region (&tempPR, &destPR); + else + copy_region(&tempPR, &destPR); + + /*Replace the newly dodgedburned area (canvas_buf) to the gimage*/ + + paint_core_replace_canvas (paint_core, drawable, ROUND(brush_opacity * 255.0), + OPAQUE_OPACITY, PRESSURE, CONSTANT); + + g_free (temp_data); +} + +static void * +dodgeburn_non_gui_paint_func (PaintCore *paint_core, + GimpDrawable *drawable, + int state) +{ + dodgeburn_motion (paint_core, drawable); + + return NULL; +} + +gboolean +dodgeburn_non_gui (GimpDrawable *drawable, + double pressure, + int num_strokes, + double *stroke_array) +{ + int 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 = dodgeburn_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) + dodgeburn_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; +} + +static gfloat +dodgeburn_highlights_lut_func(void * user_data, + int nchannels, + int channel, + gfloat value) +{ + gfloat * exposure_ptr = (gfloat *)user_data; + gfloat exposure = *exposure_ptr; + gfloat factor = 1.0 + exposure * (.333333); + + if ( (nchannels == 2 && channel == 1) || + (nchannels == 4 && channel == 3)) + return value; + + return factor * value; +} + +static gfloat +dodgeburn_midtones_lut_func(void * user_data, + int nchannels, + int channel, + gfloat value) +{ + gfloat * exposure_ptr = (gfloat *)user_data; + gfloat exposure = *exposure_ptr; + gfloat factor; + + if ( (nchannels == 2 && channel == 1) || + (nchannels == 4 && channel == 3)) + return value; + + if (exposure < 0) + factor = 1.0 - exposure * (.333333); + else + factor = 1/(1.0 + exposure); + return pow (value, factor); +} + +static gfloat +dodgeburn_shadows_lut_func(void * user_data, + int nchannels, + int channel, + gfloat value) +{ + gfloat * exposure_ptr = (gfloat *)user_data; + gfloat exposure = *exposure_ptr; + gfloat new_value; + gfloat factor; + + if ( (nchannels == 2 && channel == 1) || + (nchannels == 4 && channel == 3)) + return value; + + if (exposure >= 0) + { + factor = .333333 * exposure; + new_value = factor + value - factor * value; + } + else /* exposure < 0 */ + { + factor = -.333333 * exposure; + if (value < factor) + new_value = 0; + else /*factor <= value <=1*/ + new_value = (value - factor)/(1 - factor); + } + return new_value; +} diff --git a/app/tools/dodgeburn.h b/app/tools/dodgeburn.h new file mode 100644 index 0000000000..b394206424 --- /dev/null +++ b/app/tools/dodgeburn.h @@ -0,0 +1,42 @@ +/* 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. + */ +#ifndef __DODGEBURN_H__ +#define __DODGEBURN_H__ + +#include "paint_core.h" +#include "tools.h" + +typedef enum +{ + DODGE, + BURN +} DodgeBurnType; + +typedef enum +{ + DODGEBURN_HIGHLIGHTS, + DODGEBURN_MIDTONES, + DODGEBURN_SHADOWS +} DodgeBurnMode; + +void * dodgeburn_paint_func (PaintCore *, GimpDrawable *, int); +gboolean dodgeburn_non_gui (GimpDrawable *, double, int, double *); +Tool * tools_new_dodgeburn (void); +void tools_free_dodgeburn (Tool *); + +#endif /* __DODGEBURN_H__ */ diff --git a/app/tools/gimpdodgeburntool.c b/app/tools/gimpdodgeburntool.c new file mode 100644 index 0000000000..62399c7ceb --- /dev/null +++ b/app/tools/gimpdodgeburntool.c @@ -0,0 +1,521 @@ +/* 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 "gdk/gdkkeysyms.h" +#include "appenv.h" +#include "drawable.h" +#include "errors.h" +#include "dodgeburn.h" +#include "gdisplay.h" +#include "gimplut.h" +#include "paint_funcs.h" +#include "paint_core.h" +#include "paint_options.h" +#include "selection.h" +#include "tool_options_ui.h" +#include "tools.h" +#include "gimage.h" + +#include "libgimp/gimpintl.h" +#define ROUND(x) (int)((x) + .5) + +/* the dodgeburn structures */ + +typedef struct _DodgeBurnOptions DodgeBurnOptions; +struct _DodgeBurnOptions +{ + PaintOptions paint_options; + + DodgeBurnType type; + DodgeBurnType type_d; + GtkWidget *type_w[2]; + + DodgeBurnMode mode; /*highlights,midtones,shadows*/ + DodgeBurnMode mode_d; + GtkWidget *mode_w[3]; + + double exposure; + double exposure_d; + GtkObject *exposure_w; + + GimpLut *lut; +}; + +static void dodgeburn_make_luts ( PaintCore *, GimpDrawable *); + +static gfloat dodgeburn_highlights_lut_func(void *, int, int, gfloat); +static gfloat dodgeburn_midtones_lut_func(void *, int, int, gfloat); +static gfloat dodgeburn_shadows_lut_func(void *, int, int, gfloat); + +/* The dodge burn lookup tables */ +gfloat dodgeburn_highlights(void *, int, int, gfloat); +gfloat dodgeburn_midtones(void *, int, int, gfloat); +gfloat dodgeburn_shadows(void *, int, int, gfloat); + +/* the dodgeburn tool options */ +static DodgeBurnOptions * dodgeburn_options = NULL; + +static void dodgeburn_motion (PaintCore *, GimpDrawable *); +static void dodgeburn_init (PaintCore *, GimpDrawable *); +static void dodgeburn_finish (PaintCore *, GimpDrawable *); + +/* functions */ + +static void +dodgeburn_options_reset (void) +{ + DodgeBurnOptions *options = dodgeburn_options; + + paint_options_reset ((PaintOptions *) options); + + gtk_adjustment_set_value (GTK_ADJUSTMENT (options->exposure_w), + options->exposure_d); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->type_w[options->type_d]), TRUE); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->mode_w[options->mode_d]), TRUE); +} + +static DodgeBurnOptions * +dodgeburn_options_new (void) +{ + DodgeBurnOptions *options; + + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *scale; + GtkWidget *frame; + + gchar* type_label[2] = { N_("Dodge"), N_("Burn") }; + gint type_value[2] = { DODGE, BURN }; + + gchar* mode_label[3] = { N_("Highlights"), + N_("Midtones"), + N_("Shadows") }; + + gint mode_value[3] = { DODGEBURN_HIGHLIGHTS, + DODGEBURN_MIDTONES, + DODGEBURN_SHADOWS }; + + /* the new dodgeburn tool options structure */ + options = (DodgeBurnOptions *) g_malloc (sizeof (DodgeBurnOptions)); + paint_options_init ((PaintOptions *) options, + DODGEBURN, + dodgeburn_options_reset); + + options->type = options->type_d = DODGE; + options->exposure = options->exposure_d = 50.0; + options->mode = options->mode_d = DODGEBURN_HIGHLIGHTS; + + /* the main vbox */ + vbox = ((ToolOptions *) options)->main_vbox; + + /* the exposure scale */ + hbox = gtk_hbox_new (FALSE, 4); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + + label = gtk_label_new (_("Exposure:")); + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + options->exposure_w = + gtk_adjustment_new (options->exposure_d, 0.0, 100.0, 1.0, 1.0, 0.0); + scale = gtk_hscale_new (GTK_ADJUSTMENT (options->exposure_w)); + gtk_box_pack_start (GTK_BOX (hbox), scale, TRUE, TRUE, 0); + 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->exposure_w), "value_changed", + (GtkSignalFunc) tool_options_double_adjustment_update, + &options->exposure); + + gtk_widget_show (scale); + gtk_widget_show (hbox); + + /* the type (dodge or burn) */ + frame = tool_options_radio_buttons_new (_("Type"), + &options->type, + options->type_w, + type_label, + type_value, + 2); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->type_w[options->type_d]), TRUE); + gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + /* mode (highlights, midtones, or shadows) */ + frame = tool_options_radio_buttons_new (_("Mode"), + &options->mode, + options->mode_w, + mode_label, + mode_value, + 3); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->mode_w[options->mode_d]), TRUE); + gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + return options; +} + +void * +dodgeburn_paint_func (PaintCore *paint_core, + GimpDrawable *drawable, + int state) +{ + switch (state) + { + case INIT_PAINT: + dodgeburn_init (paint_core, drawable); + break; + case MOTION_PAINT: + dodgeburn_motion (paint_core, drawable); + break; + case FINISH_PAINT: + dodgeburn_finish (paint_core, drawable); + break; + } + + return NULL; +} + +static void +dodgeburn_finish ( PaintCore *paint_core, + GimpDrawable * drawable) +{ + /* Here we destroy the luts to do the painting with.*/ + if (dodgeburn_options->lut) + { + gimp_lut_free (dodgeburn_options->lut); + dodgeburn_options->lut = NULL; + } +} + +static void +dodgeburn_init ( PaintCore *paint_core, + GimpDrawable * drawable) +{ + /* Here we create the luts to do the painting with.*/ + dodgeburn_make_luts (paint_core, drawable); +} + +static void +dodgeburn_make_luts ( PaintCore *paint_core, + GimpDrawable * drawable) +{ + GimpLutFunc lut_func; + int nchannels = gimp_drawable_bytes (drawable); + gfloat exposure = (dodgeburn_options->exposure)/100.0; + + /* make the exposure negative if burn for luts*/ + if (dodgeburn_options->type == BURN) + exposure = -exposure; + + dodgeburn_options->lut = gimp_lut_new(); + + switch (dodgeburn_options->mode) + { + case DODGEBURN_HIGHLIGHTS: + lut_func = dodgeburn_highlights_lut_func; + break; + case DODGEBURN_MIDTONES: + lut_func = dodgeburn_midtones_lut_func; + break; + case DODGEBURN_SHADOWS: + lut_func = dodgeburn_shadows_lut_func; + break; + default: + lut_func = NULL; + break; + } + + gimp_lut_setup_exact (dodgeburn_options->lut, + lut_func, (void *)&exposure, + nchannels); + +} + +static void +dodgeburn_modifier_key_func (Tool *tool, + GdkEventKey *kevent, + gpointer gdisp_ptr) +{ + switch (kevent->keyval) + { + case GDK_Alt_L: case GDK_Alt_R: + break; + case GDK_Shift_L: case GDK_Shift_R: + switch (dodgeburn_options->type) + { + case BURN: + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dodgeburn_options->type_w[BURN]), TRUE); + break; + case DODGE: + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dodgeburn_options->type_w[DODGE]), TRUE); + break; + default: + break; + } + break; + case GDK_Control_L: case GDK_Control_R: + break; + } +} + +Tool * +tools_new_dodgeburn () +{ + Tool * tool; + PaintCore * private; + + /* The tool options */ + if (! dodgeburn_options) + { + dodgeburn_options = dodgeburn_options_new (); + tools_register (DODGEBURN, (ToolOptions *) dodgeburn_options); + + /* press all default buttons */ + dodgeburn_options_reset (); + } + + tool = paint_core_new (DODGEBURN); + tool->modifier_key_func = dodgeburn_modifier_key_func; + + private = (PaintCore *) tool->private; + private->paint_func = dodgeburn_paint_func; + + return tool; +} + +void +tools_free_dodgeburn (Tool *tool) +{ + /* delete any luts here */ + paint_core_free (tool); +} + +static void +dodgeburn_motion (PaintCore *paint_core, + GimpDrawable *drawable) +{ + GImage *gimage; + TempBuf * area; + TempBuf * orig; + PixelRegion srcPR, destPR, tempPR; + guchar *temp_data; + gfloat exposure; + gfloat brush_opacity; + + if (! (gimage = drawable_gimage (drawable))) + return; + + /* If the image type is indexed, don't dodgeburn */ + if ((drawable_type (drawable) == INDEXED_GIMAGE) || + (drawable_type (drawable) == INDEXEDA_GIMAGE)) + return; + + /* Get a region which can be used to paint to */ + if (! (area = paint_core_get_paint_area (paint_core, drawable))) + return; + + /* Constant painting --get a copy of the orig drawable (with + no paint from this stroke yet) */ + + { + gint x1, y1, x2, y2; + x1 = BOUNDS (area->x, 0, drawable_width (drawable)); + y1 = BOUNDS (area->y, 0, drawable_height (drawable)); + x2 = BOUNDS (area->x + area->width, 0, drawable_width (drawable)); + y2 = BOUNDS (area->y + area->height, 0, drawable_height (drawable)); + + if (!(x2 - x1) || !(y2 - y1)) + return; + + /* get the original untouched image */ + orig = paint_core_get_orig_image (paint_core, drawable, x1, y1, x2, y2); + srcPR.bytes = orig->bytes; + srcPR.x = 0; + srcPR.y = 0; + srcPR.w = x2 - x1; + srcPR.h = y2 - y1; + srcPR.rowstride = srcPR.bytes * orig->width; + srcPR.data = temp_buf_data (orig); + } + + /* tempPR will hold the dodgeburned region*/ + tempPR.bytes = srcPR.bytes; + tempPR.x = srcPR.x; + tempPR.y = srcPR.y; + tempPR.w = srcPR.w; + tempPR.h = srcPR.h; + tempPR.rowstride = tempPR.bytes * tempPR.w; + temp_data = g_malloc (tempPR.h * tempPR.rowstride); + tempPR.data = temp_data; + + brush_opacity = PAINT_OPTIONS_GET_OPACITY (dodgeburn_options); + exposure = (dodgeburn_options->exposure)/100.0; + + /* DodgeBurn the region */ + gimp_lut_process (dodgeburn_options->lut, &srcPR, &tempPR); + + /* The dest is the paint area we got above (= canvas_buf) */ + destPR.bytes = area->bytes; + destPR.x = 0; destPR.y = 0; + destPR.w = area->width; + destPR.h = area->height; + destPR.rowstride = area->width * destPR.bytes; + destPR.data = temp_buf_data (area); + + /* Now add an alpha to the dodgeburned region + and put this in area = canvas_buf */ + if (!drawable_has_alpha (drawable)) + add_alpha_region (&tempPR, &destPR); + else + copy_region(&tempPR, &destPR); + + /*Replace the newly dodgedburned area (canvas_buf) to the gimage*/ + + paint_core_replace_canvas (paint_core, drawable, ROUND(brush_opacity * 255.0), + OPAQUE_OPACITY, PRESSURE, CONSTANT); + + g_free (temp_data); +} + +static void * +dodgeburn_non_gui_paint_func (PaintCore *paint_core, + GimpDrawable *drawable, + int state) +{ + dodgeburn_motion (paint_core, drawable); + + return NULL; +} + +gboolean +dodgeburn_non_gui (GimpDrawable *drawable, + double pressure, + int num_strokes, + double *stroke_array) +{ + int 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 = dodgeburn_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) + dodgeburn_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; +} + +static gfloat +dodgeburn_highlights_lut_func(void * user_data, + int nchannels, + int channel, + gfloat value) +{ + gfloat * exposure_ptr = (gfloat *)user_data; + gfloat exposure = *exposure_ptr; + gfloat factor = 1.0 + exposure * (.333333); + + if ( (nchannels == 2 && channel == 1) || + (nchannels == 4 && channel == 3)) + return value; + + return factor * value; +} + +static gfloat +dodgeburn_midtones_lut_func(void * user_data, + int nchannels, + int channel, + gfloat value) +{ + gfloat * exposure_ptr = (gfloat *)user_data; + gfloat exposure = *exposure_ptr; + gfloat factor; + + if ( (nchannels == 2 && channel == 1) || + (nchannels == 4 && channel == 3)) + return value; + + if (exposure < 0) + factor = 1.0 - exposure * (.333333); + else + factor = 1/(1.0 + exposure); + return pow (value, factor); +} + +static gfloat +dodgeburn_shadows_lut_func(void * user_data, + int nchannels, + int channel, + gfloat value) +{ + gfloat * exposure_ptr = (gfloat *)user_data; + gfloat exposure = *exposure_ptr; + gfloat new_value; + gfloat factor; + + if ( (nchannels == 2 && channel == 1) || + (nchannels == 4 && channel == 3)) + return value; + + if (exposure >= 0) + { + factor = .333333 * exposure; + new_value = factor + value - factor * value; + } + else /* exposure < 0 */ + { + factor = -.333333 * exposure; + if (value < factor) + new_value = 0; + else /*factor <= value <=1*/ + new_value = (value - factor)/(1 - factor); + } + return new_value; +} diff --git a/app/tools/gimpdodgeburntool.h b/app/tools/gimpdodgeburntool.h new file mode 100644 index 0000000000..b394206424 --- /dev/null +++ b/app/tools/gimpdodgeburntool.h @@ -0,0 +1,42 @@ +/* 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. + */ +#ifndef __DODGEBURN_H__ +#define __DODGEBURN_H__ + +#include "paint_core.h" +#include "tools.h" + +typedef enum +{ + DODGE, + BURN +} DodgeBurnType; + +typedef enum +{ + DODGEBURN_HIGHLIGHTS, + DODGEBURN_MIDTONES, + DODGEBURN_SHADOWS +} DodgeBurnMode; + +void * dodgeburn_paint_func (PaintCore *, GimpDrawable *, int); +gboolean dodgeburn_non_gui (GimpDrawable *, double, int, double *); +Tool * tools_new_dodgeburn (void); +void tools_free_dodgeburn (Tool *); + +#endif /* __DODGEBURN_H__ */ diff --git a/app/tools/gimpsmudgetool.c b/app/tools/gimpsmudgetool.c new file mode 100644 index 0000000000..06d4ee2631 --- /dev/null +++ b/app/tools/gimpsmudgetool.c @@ -0,0 +1,423 @@ +/* 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 "gdk/gdkkeysyms.h" +#include "appenv.h" +#include "drawable.h" +#include "errors.h" +#include "smudge.h" +#include "gdisplay.h" +#include "gimplut.h" +#include "paint_funcs.h" +#include "paint_core.h" +#include "paint_options.h" +#include "selection.h" +#include "tool_options_ui.h" +#include "tools.h" +#include "gimage.h" + +#define ROUND(x) (int)((x) + .5) + +#include "libgimp/gimpintl.h" + +/* the smudge structures */ + +typedef struct _SmudgeOptions SmudgeOptions; +struct _SmudgeOptions +{ + PaintOptions paint_options; + + double pressure; + double pressure_d; + GtkObject *pressure_w; + +}; + +static PixelRegion accumPR; +static unsigned char *accum_data; + +/* the smudge tool options */ +static SmudgeOptions * smudge_options = NULL; + +static void smudge_motion (PaintCore *, GimpDrawable *); +static void smudge_init (PaintCore *, GimpDrawable *); +static void smudge_finish (PaintCore *, GimpDrawable *); + +static void +smudge_nonclipped_painthit_coords (PaintCore *paint_core, + gint * x, gint* y, gint* w, gint *h); +static void +smudge_allocate_accum_buffer ( gint w, gint h, + gint bytes, gint do_fill); + +/* functions */ + +static void +smudge_options_reset (void) +{ + SmudgeOptions *options = smudge_options; + + paint_options_reset ((PaintOptions *) options); + + gtk_adjustment_set_value (GTK_ADJUSTMENT (options->pressure_w), + options->pressure_d); +} + +static SmudgeOptions * +smudge_options_new (void) +{ + SmudgeOptions *options; + + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *scale; + + /* the new smudge tool options structure */ + options = (SmudgeOptions *) g_malloc (sizeof (SmudgeOptions)); + paint_options_init ((PaintOptions *) options, + SMUDGE, + smudge_options_reset); + + options->pressure = options->pressure_d = 50.0; + + /* the main vbox */ + vbox = ((ToolOptions *) options)->main_vbox; + + /* the pressure scale */ + hbox = gtk_hbox_new (FALSE, 4); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + + label = gtk_label_new (_("Pressure:")); + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + 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_box_pack_start (GTK_BOX (hbox), scale, TRUE, TRUE, 0); + 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", + (GtkSignalFunc) tool_options_double_adjustment_update, + &options->pressure); + gtk_widget_show (scale); + gtk_widget_show (hbox); + + return options; +} + +void * +smudge_paint_func (PaintCore *paint_core, + GimpDrawable *drawable, + int state) +{ + switch (state) + { + case INIT_PAINT: + smudge_init (paint_core, drawable); + break; + case MOTION_PAINT: + smudge_motion (paint_core, drawable); + break; + case FINISH_PAINT: + smudge_finish (paint_core, drawable); + break; + } + + return NULL; +} + +static void +smudge_finish ( PaintCore *paint_core, + GimpDrawable * drawable) +{ + if (accum_data) + { + g_free (accum_data); + accum_data = NULL; + } +} + +static void +smudge_nonclipped_painthit_coords (PaintCore *paint_core, + gint * x, gint* y, gint* w, gint *h) +{ + /* Note: these are the brush mask size plus a border of 1 pixel */ + *x = (gint) paint_core->curx - paint_core->brush->mask->width/2 - 1; + *y = (gint) paint_core->cury - paint_core->brush->mask->height/2 - 1; + *w = paint_core->brush->mask->width + 2; + *h = paint_core->brush->mask->height + 2; +} + +static void +smudge_init ( PaintCore *paint_core, + GimpDrawable * drawable) +{ + GImage *gimage; + TempBuf * area; + PixelRegion srcPR; + gint x,y,w,h; + gint was_clipped; + + /* adjust the x and y coordinates to the upper left corner of the brush */ + smudge_nonclipped_painthit_coords (paint_core, &x, &y, &w, &h); + + if (! (gimage = drawable_gimage (drawable))) + return; + + /* If the image type is indexed, don't smudge */ + if ((drawable_type (drawable) == INDEXED_GIMAGE) || + (drawable_type (drawable) == INDEXEDA_GIMAGE)) + return; + + area = paint_core_get_paint_area (paint_core, drawable); + + if (!area) + was_clipped = TRUE; + else if (x != area->x || y != area->y || w != area->width || h != area->height) + was_clipped = TRUE; + else + was_clipped = FALSE; + + smudge_allocate_accum_buffer (w,h, + drawable_bytes(drawable), was_clipped); + + if (!area) return; + + accumPR.x = area->x - x; + accumPR.y = area->y - y; + accumPR.w = area->width; + accumPR.h = area->height; + accumPR.rowstride = accumPR.bytes * w; + accumPR.data = accum_data + + accumPR.rowstride * accumPR.y + + accumPR.x * accumPR.bytes; + + pixel_region_init (&srcPR, drawable_data (drawable), + area->x, area->y, area->width, area->height, FALSE); + + /* copy the region under the original painthit. */ + copy_region (&srcPR, &accumPR); + + accumPR.x = 0; + accumPR.y = 0; + accumPR.w = area->width; + accumPR.h = area->height; + accumPR.rowstride = accumPR.bytes * accumPR.w; + accumPR.data = accum_data; +} + +static void +smudge_allocate_accum_buffer ( + gint w, + gint h, + gint bytes, + gint do_fill + ) +{ + /* Allocate the accumulation buffer */ + accumPR.bytes = bytes; + accum_data = g_malloc (w * h * bytes); + + if (do_fill) + { + guchar color[3] = {0,0,0}; + accumPR.x = 0; + accumPR.y = 0; + accumPR.w = w; + accumPR.h = h; + accumPR.rowstride = accumPR.bytes * w; + accumPR.data = accum_data; + color_region (&accumPR, (const guchar*)&color); + } +} + +Tool * +tools_new_smudge () +{ + Tool * tool; + PaintCore * private; + + /* The tool options */ + if (! smudge_options) + { + smudge_options = smudge_options_new (); + tools_register (SMUDGE, (ToolOptions *) smudge_options); + + /* press all default buttons */ + smudge_options_reset (); + } + + tool = paint_core_new (SMUDGE); + /*tool->modifier_key_func = smudge_modifier_key_func;*/ + + private = (PaintCore *) tool->private; + private->paint_func = smudge_paint_func; + + return tool; +} + +void +tools_free_smudge (Tool *tool) +{ + paint_core_free (tool); +} + +static void +smudge_motion (PaintCore *paint_core, + GimpDrawable *drawable) +{ + GImage *gimage; + TempBuf * area; + PixelRegion srcPR, destPR, tempPR; + gfloat pressure; + gfloat brush_opacity; + gint x,y,w,h; + + if (! (gimage = drawable_gimage (drawable))) + return; + + /* If the image type is indexed, don't smudge */ + if ((drawable_type (drawable) == INDEXED_GIMAGE) || + (drawable_type (drawable) == INDEXEDA_GIMAGE)) + return; + + smudge_nonclipped_painthit_coords (paint_core, &x, &y, &w, &h); + + /* Get the paint area */ + if (! (area = paint_core_get_paint_area (paint_core, drawable))) + return; + + /* srcPR will be the pixels under the current painthit from + the drawable*/ + + pixel_region_init (&srcPR, drawable_data (drawable), + area->x, area->y, area->width, area->height, FALSE); + + brush_opacity = PAINT_OPTIONS_GET_OPACITY (smudge_options); + pressure = (smudge_options->pressure)/100.0; + + /* The tempPR will be the built up buffer (for smudge) */ + tempPR.bytes = accumPR.bytes; + tempPR.rowstride = accumPR.rowstride; + tempPR.x = area->x - x; + tempPR.y = area->y - y; + tempPR.w = area->width; + tempPR.h = area->height; + tempPR.data = accum_data + + tempPR.rowstride * tempPR.y + + tempPR.x * tempPR.bytes; + + /* The dest will be the paint area we got above (= canvas_buf) */ + + destPR.bytes = area->bytes; + destPR.x = 0; destPR.y = 0; + destPR.w = area->width; + destPR.h = area->height; + destPR.rowstride = area->width * area->bytes; + destPR.data = temp_buf_data (area); + + /* + Smudge uses the buffer Accum. + For each successive painthit Accum is built like this + Accum = pressure*Accum + (1-pressure)*I. + where I is the pixels under the current painthit. + Then the paint area (canvas_buf) is built as + (Accum,1) (if no alpha), + */ + + blend_region (&srcPR, &tempPR, &tempPR, ROUND(pressure * 255.0)); + + /* re-init the tempPR */ + + tempPR.bytes = accumPR.bytes; + tempPR.rowstride = accumPR.rowstride; + tempPR.x = area->x - x; + tempPR.y = area->y - y; + tempPR.w = area->width; + tempPR.h = area->height; + tempPR.data = accum_data + + tempPR.rowstride * tempPR.y + + tempPR.x * tempPR.bytes; + + if (!drawable_has_alpha (drawable)) + add_alpha_region (&tempPR, &destPR); + else + copy_region(&tempPR, &destPR); + + /*Replace the newly made paint area to the gimage*/ + paint_core_replace_canvas (paint_core, drawable, ROUND(brush_opacity * 255.0), + OPAQUE_OPACITY, PRESSURE, INCREMENTAL); + +} + +static void * +smudge_non_gui_paint_func (PaintCore *paint_core, + GimpDrawable *drawable, + int state) +{ + smudge_motion (paint_core, drawable); + + return NULL; +} + +gboolean +smudge_non_gui (GimpDrawable *drawable, + double pressure, + int num_strokes, + double *stroke_array) +{ + int 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 = smudge_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) + smudge_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; +} diff --git a/app/tools/gimpsmudgetool.h b/app/tools/gimpsmudgetool.h new file mode 100644 index 0000000000..ab7fb82634 --- /dev/null +++ b/app/tools/gimpsmudgetool.h @@ -0,0 +1,42 @@ +/* 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. + */ +#ifndef __SMUDGE_H__ +#define __SMUDGE_H__ + +#include "paint_core.h" +#include "tools.h" + +typedef enum +{ + SMUDGE_TYPE_SMUDGE, + SMUDGE_TYPE_STREAK +} SmudgeType; + +typedef enum +{ + SMUDGE_MODE_HIGHLIGHTS, + SMUDGE_MODE_MIDTONES, + SMUDGE_MODE_SHADOWS +} SmudgeMode; + +void * smudge_paint_func (PaintCore *, GimpDrawable *, int); +gboolean smudge_non_gui (GimpDrawable *, double, int, double *); +Tool * tools_new_smudge (void); +void tools_free_smudge (Tool *); + +#endif /* __SMUDGE_H__ */ diff --git a/app/tools/paint_core.c b/app/tools/paint_core.c index 5f7a6243ad..e54b5b0345 100644 --- a/app/tools/paint_core.c +++ b/app/tools/paint_core.c @@ -58,8 +58,9 @@ static void paint_core_paste (PaintCore *, MaskBuf *, GimpDrawable *, int, int, int, int); static void paint_core_replace (PaintCore *, MaskBuf *, GimpDrawable *, int, int, int); -static void paint_to_canvas_tiles (PaintCore *, MaskBuf *, int); -static void paint_to_canvas_buf (PaintCore *, MaskBuf *, int); +static void brush_to_canvas_tiles (PaintCore *, MaskBuf *, int); +static void canvas_tiles_to_canvas_buf (PaintCore *); +static void brush_to_canvas_buf (PaintCore *, MaskBuf *, int); static void set_undo_tiles (GimpDrawable *, int, int, int, int); static void set_canvas_tiles (int, int, int, int); static int paint_core_invalidate_cache (GimpBrush *brush, gpointer *blah); @@ -1193,6 +1194,7 @@ paint_core_solidify_mask (MaskBuf *brush_mask) last_brush = brush_mask; if (solid_brush) mask_buf_free (solid_brush); + solid_brush = mask_buf_new (brush_mask->width + 2, brush_mask->height + 2); /* get the data and advance one line into it */ @@ -1268,14 +1270,15 @@ paint_core_paste (PaintCore *paint_core, set_canvas_tiles (canvas_buf->x, canvas_buf->y, canvas_buf->width, canvas_buf->height); - paint_to_canvas_tiles (paint_core, brush_mask, brush_opacity); + brush_to_canvas_tiles (paint_core, brush_mask, brush_opacity); + canvas_tiles_to_canvas_buf (paint_core); alt = undo_tiles; } /* Otherwise: * combine the canvas buf and the brush mask to the canvas buf */ else /* mode != CONSTANT */ - paint_to_canvas_buf (paint_core, brush_mask, brush_opacity); + brush_to_canvas_buf (paint_core, brush_mask, brush_opacity); /* intialize canvas buf source pixel regions */ srcPR.bytes = canvas_buf->bytes; @@ -1324,6 +1327,7 @@ paint_core_replace (PaintCore *paint_core, { GImage *gimage; PixelRegion srcPR, maskPR; + TileManager *alt = NULL; int offx, offy; if (!drawable_has_alpha (drawable)) @@ -1334,12 +1338,6 @@ paint_core_replace (PaintCore *paint_core, return; } - if (mode != INCREMENTAL) - { - g_message (_("paint_core_replace only works in INCREMENTAL mode")); - return; - } - if (! (gimage = drawable_gimage (drawable))) return; @@ -1348,12 +1346,33 @@ paint_core_replace (PaintCore *paint_core, canvas_buf->x, canvas_buf->y, canvas_buf->width, canvas_buf->height); - maskPR.bytes = 1; - maskPR.x = 0; maskPR.y = 0; - maskPR.w = canvas_buf->width; - maskPR.h = canvas_buf->height; - maskPR.rowstride = maskPR.bytes * brush_mask->width; - maskPR.data = mask_buf_data (brush_mask); + if (mode == CONSTANT) + { + /* initialize any invalid canvas tiles */ + set_canvas_tiles (canvas_buf->x, canvas_buf->y, + canvas_buf->width, canvas_buf->height); + + /* combine the brush mask and the canvas tiles */ + brush_to_canvas_tiles (paint_core, brush_mask, brush_opacity); + + /* set the alt source as the unaltered undo_tiles */ + alt = undo_tiles; + + /* initialize the maskPR from the canvas tiles */ + pixel_region_init (&maskPR, canvas_tiles, + canvas_buf->x, canvas_buf->y, + canvas_buf->width, canvas_buf->height, FALSE); + } + else + { + /* The mask is just the brush mask */ + maskPR.bytes = 1; + maskPR.x = 0; maskPR.y = 0; + maskPR.w = canvas_buf->width; + maskPR.h = canvas_buf->height; + maskPR.rowstride = maskPR.bytes * brush_mask->width; + maskPR.data = mask_buf_data (brush_mask); + } /* intialize canvas buf source pixel regions */ srcPR.bytes = canvas_buf->bytes; @@ -1366,7 +1385,7 @@ paint_core_replace (PaintCore *paint_core, /* apply the paint area to the gimage */ gimage_replace_image (gimage, drawable, &srcPR, FALSE, image_opacity, - &maskPR, + &maskPR, canvas_buf->x, canvas_buf->y); /* Update the undo extents */ @@ -1384,6 +1403,95 @@ paint_core_replace (PaintCore *paint_core, canvas_buf->width, canvas_buf->height); } +static void +canvas_tiles_to_canvas_buf(PaintCore *paint_core) +{ + PixelRegion srcPR, maskPR; + + /* combine the canvas tiles and the canvas buf */ + srcPR.bytes = canvas_buf->bytes; + srcPR.x = 0; srcPR.y = 0; + srcPR.w = canvas_buf->width; + srcPR.h = canvas_buf->height; + srcPR.rowstride = canvas_buf->width * canvas_buf->bytes; + srcPR.data = temp_buf_data (canvas_buf); + + pixel_region_init (&maskPR, canvas_tiles, + canvas_buf->x, canvas_buf->y, + canvas_buf->width, canvas_buf->height, FALSE); + + /* apply the canvas tiles to the canvas buf */ + apply_mask_to_region (&srcPR, &maskPR, OPAQUE_OPACITY); +} + +static void +brush_to_canvas_tiles ( + PaintCore *paint_core, + MaskBuf *brush_mask, + int brush_opacity + ) +{ + PixelRegion srcPR, maskPR; + int x, y; + int xoff, yoff; + + /* combine the brush mask and the canvas tiles */ + pixel_region_init (&srcPR, canvas_tiles, + canvas_buf->x, canvas_buf->y, + canvas_buf->width, canvas_buf->height, TRUE); + + x = (int) paint_core->curx - (brush_mask->width >> 1); + y = (int) paint_core->cury - (brush_mask->height >> 1); + xoff = (x < 0) ? -x : 0; + yoff = (y < 0) ? -y : 0; + + maskPR.bytes = 1; + maskPR.x = 0; maskPR.y = 0; + maskPR.w = srcPR.w; + maskPR.h = srcPR.h; + maskPR.rowstride = maskPR.bytes * brush_mask->width; + maskPR.data = mask_buf_data (brush_mask) + yoff * maskPR.rowstride + xoff * maskPR.bytes; + + /* combine the mask to the canvas tiles */ + combine_mask_and_region (&srcPR, &maskPR, brush_opacity); +} + +static void +brush_to_canvas_buf ( + PaintCore *paint_core, + MaskBuf *brush_mask, + int brush_opacity + ) +{ + PixelRegion srcPR, maskPR; + int x, y; + int xoff, yoff; + + x = (int) paint_core->curx - (brush_mask->width >> 1); + y = (int) paint_core->cury - (brush_mask->height >> 1); + xoff = (x < 0) ? -x : 0; + yoff = (y < 0) ? -y : 0; + + /* combine the canvas buf and the brush mask to the canvas buf */ + srcPR.bytes = canvas_buf->bytes; + srcPR.x = 0; srcPR.y = 0; + srcPR.w = canvas_buf->width; + srcPR.h = canvas_buf->height; + srcPR.rowstride = canvas_buf->width * canvas_buf->bytes; + srcPR.data = temp_buf_data (canvas_buf); + + maskPR.bytes = 1; + maskPR.x = 0; maskPR.y = 0; + maskPR.w = srcPR.w; + maskPR.h = srcPR.h; + maskPR.rowstride = maskPR.bytes * brush_mask->width; + maskPR.data = mask_buf_data (brush_mask) + yoff * maskPR.rowstride + xoff * maskPR.bytes; + + /* apply the mask */ + apply_mask_to_region (&srcPR, &maskPR, brush_opacity); +} + +#if 0 static void paint_to_canvas_tiles (PaintCore *paint_core, MaskBuf *brush_mask, @@ -1462,6 +1570,7 @@ paint_to_canvas_buf (PaintCore *paint_core, /* apply the mask */ apply_mask_to_region (&srcPR, &maskPR, brush_opacity); } +#endif static void set_undo_tiles (GimpDrawable *drawable, diff --git a/app/tools/smudge.c b/app/tools/smudge.c new file mode 100644 index 0000000000..06d4ee2631 --- /dev/null +++ b/app/tools/smudge.c @@ -0,0 +1,423 @@ +/* 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 "gdk/gdkkeysyms.h" +#include "appenv.h" +#include "drawable.h" +#include "errors.h" +#include "smudge.h" +#include "gdisplay.h" +#include "gimplut.h" +#include "paint_funcs.h" +#include "paint_core.h" +#include "paint_options.h" +#include "selection.h" +#include "tool_options_ui.h" +#include "tools.h" +#include "gimage.h" + +#define ROUND(x) (int)((x) + .5) + +#include "libgimp/gimpintl.h" + +/* the smudge structures */ + +typedef struct _SmudgeOptions SmudgeOptions; +struct _SmudgeOptions +{ + PaintOptions paint_options; + + double pressure; + double pressure_d; + GtkObject *pressure_w; + +}; + +static PixelRegion accumPR; +static unsigned char *accum_data; + +/* the smudge tool options */ +static SmudgeOptions * smudge_options = NULL; + +static void smudge_motion (PaintCore *, GimpDrawable *); +static void smudge_init (PaintCore *, GimpDrawable *); +static void smudge_finish (PaintCore *, GimpDrawable *); + +static void +smudge_nonclipped_painthit_coords (PaintCore *paint_core, + gint * x, gint* y, gint* w, gint *h); +static void +smudge_allocate_accum_buffer ( gint w, gint h, + gint bytes, gint do_fill); + +/* functions */ + +static void +smudge_options_reset (void) +{ + SmudgeOptions *options = smudge_options; + + paint_options_reset ((PaintOptions *) options); + + gtk_adjustment_set_value (GTK_ADJUSTMENT (options->pressure_w), + options->pressure_d); +} + +static SmudgeOptions * +smudge_options_new (void) +{ + SmudgeOptions *options; + + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *scale; + + /* the new smudge tool options structure */ + options = (SmudgeOptions *) g_malloc (sizeof (SmudgeOptions)); + paint_options_init ((PaintOptions *) options, + SMUDGE, + smudge_options_reset); + + options->pressure = options->pressure_d = 50.0; + + /* the main vbox */ + vbox = ((ToolOptions *) options)->main_vbox; + + /* the pressure scale */ + hbox = gtk_hbox_new (FALSE, 4); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + + label = gtk_label_new (_("Pressure:")); + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + 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_box_pack_start (GTK_BOX (hbox), scale, TRUE, TRUE, 0); + 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", + (GtkSignalFunc) tool_options_double_adjustment_update, + &options->pressure); + gtk_widget_show (scale); + gtk_widget_show (hbox); + + return options; +} + +void * +smudge_paint_func (PaintCore *paint_core, + GimpDrawable *drawable, + int state) +{ + switch (state) + { + case INIT_PAINT: + smudge_init (paint_core, drawable); + break; + case MOTION_PAINT: + smudge_motion (paint_core, drawable); + break; + case FINISH_PAINT: + smudge_finish (paint_core, drawable); + break; + } + + return NULL; +} + +static void +smudge_finish ( PaintCore *paint_core, + GimpDrawable * drawable) +{ + if (accum_data) + { + g_free (accum_data); + accum_data = NULL; + } +} + +static void +smudge_nonclipped_painthit_coords (PaintCore *paint_core, + gint * x, gint* y, gint* w, gint *h) +{ + /* Note: these are the brush mask size plus a border of 1 pixel */ + *x = (gint) paint_core->curx - paint_core->brush->mask->width/2 - 1; + *y = (gint) paint_core->cury - paint_core->brush->mask->height/2 - 1; + *w = paint_core->brush->mask->width + 2; + *h = paint_core->brush->mask->height + 2; +} + +static void +smudge_init ( PaintCore *paint_core, + GimpDrawable * drawable) +{ + GImage *gimage; + TempBuf * area; + PixelRegion srcPR; + gint x,y,w,h; + gint was_clipped; + + /* adjust the x and y coordinates to the upper left corner of the brush */ + smudge_nonclipped_painthit_coords (paint_core, &x, &y, &w, &h); + + if (! (gimage = drawable_gimage (drawable))) + return; + + /* If the image type is indexed, don't smudge */ + if ((drawable_type (drawable) == INDEXED_GIMAGE) || + (drawable_type (drawable) == INDEXEDA_GIMAGE)) + return; + + area = paint_core_get_paint_area (paint_core, drawable); + + if (!area) + was_clipped = TRUE; + else if (x != area->x || y != area->y || w != area->width || h != area->height) + was_clipped = TRUE; + else + was_clipped = FALSE; + + smudge_allocate_accum_buffer (w,h, + drawable_bytes(drawable), was_clipped); + + if (!area) return; + + accumPR.x = area->x - x; + accumPR.y = area->y - y; + accumPR.w = area->width; + accumPR.h = area->height; + accumPR.rowstride = accumPR.bytes * w; + accumPR.data = accum_data + + accumPR.rowstride * accumPR.y + + accumPR.x * accumPR.bytes; + + pixel_region_init (&srcPR, drawable_data (drawable), + area->x, area->y, area->width, area->height, FALSE); + + /* copy the region under the original painthit. */ + copy_region (&srcPR, &accumPR); + + accumPR.x = 0; + accumPR.y = 0; + accumPR.w = area->width; + accumPR.h = area->height; + accumPR.rowstride = accumPR.bytes * accumPR.w; + accumPR.data = accum_data; +} + +static void +smudge_allocate_accum_buffer ( + gint w, + gint h, + gint bytes, + gint do_fill + ) +{ + /* Allocate the accumulation buffer */ + accumPR.bytes = bytes; + accum_data = g_malloc (w * h * bytes); + + if (do_fill) + { + guchar color[3] = {0,0,0}; + accumPR.x = 0; + accumPR.y = 0; + accumPR.w = w; + accumPR.h = h; + accumPR.rowstride = accumPR.bytes * w; + accumPR.data = accum_data; + color_region (&accumPR, (const guchar*)&color); + } +} + +Tool * +tools_new_smudge () +{ + Tool * tool; + PaintCore * private; + + /* The tool options */ + if (! smudge_options) + { + smudge_options = smudge_options_new (); + tools_register (SMUDGE, (ToolOptions *) smudge_options); + + /* press all default buttons */ + smudge_options_reset (); + } + + tool = paint_core_new (SMUDGE); + /*tool->modifier_key_func = smudge_modifier_key_func;*/ + + private = (PaintCore *) tool->private; + private->paint_func = smudge_paint_func; + + return tool; +} + +void +tools_free_smudge (Tool *tool) +{ + paint_core_free (tool); +} + +static void +smudge_motion (PaintCore *paint_core, + GimpDrawable *drawable) +{ + GImage *gimage; + TempBuf * area; + PixelRegion srcPR, destPR, tempPR; + gfloat pressure; + gfloat brush_opacity; + gint x,y,w,h; + + if (! (gimage = drawable_gimage (drawable))) + return; + + /* If the image type is indexed, don't smudge */ + if ((drawable_type (drawable) == INDEXED_GIMAGE) || + (drawable_type (drawable) == INDEXEDA_GIMAGE)) + return; + + smudge_nonclipped_painthit_coords (paint_core, &x, &y, &w, &h); + + /* Get the paint area */ + if (! (area = paint_core_get_paint_area (paint_core, drawable))) + return; + + /* srcPR will be the pixels under the current painthit from + the drawable*/ + + pixel_region_init (&srcPR, drawable_data (drawable), + area->x, area->y, area->width, area->height, FALSE); + + brush_opacity = PAINT_OPTIONS_GET_OPACITY (smudge_options); + pressure = (smudge_options->pressure)/100.0; + + /* The tempPR will be the built up buffer (for smudge) */ + tempPR.bytes = accumPR.bytes; + tempPR.rowstride = accumPR.rowstride; + tempPR.x = area->x - x; + tempPR.y = area->y - y; + tempPR.w = area->width; + tempPR.h = area->height; + tempPR.data = accum_data + + tempPR.rowstride * tempPR.y + + tempPR.x * tempPR.bytes; + + /* The dest will be the paint area we got above (= canvas_buf) */ + + destPR.bytes = area->bytes; + destPR.x = 0; destPR.y = 0; + destPR.w = area->width; + destPR.h = area->height; + destPR.rowstride = area->width * area->bytes; + destPR.data = temp_buf_data (area); + + /* + Smudge uses the buffer Accum. + For each successive painthit Accum is built like this + Accum = pressure*Accum + (1-pressure)*I. + where I is the pixels under the current painthit. + Then the paint area (canvas_buf) is built as + (Accum,1) (if no alpha), + */ + + blend_region (&srcPR, &tempPR, &tempPR, ROUND(pressure * 255.0)); + + /* re-init the tempPR */ + + tempPR.bytes = accumPR.bytes; + tempPR.rowstride = accumPR.rowstride; + tempPR.x = area->x - x; + tempPR.y = area->y - y; + tempPR.w = area->width; + tempPR.h = area->height; + tempPR.data = accum_data + + tempPR.rowstride * tempPR.y + + tempPR.x * tempPR.bytes; + + if (!drawable_has_alpha (drawable)) + add_alpha_region (&tempPR, &destPR); + else + copy_region(&tempPR, &destPR); + + /*Replace the newly made paint area to the gimage*/ + paint_core_replace_canvas (paint_core, drawable, ROUND(brush_opacity * 255.0), + OPAQUE_OPACITY, PRESSURE, INCREMENTAL); + +} + +static void * +smudge_non_gui_paint_func (PaintCore *paint_core, + GimpDrawable *drawable, + int state) +{ + smudge_motion (paint_core, drawable); + + return NULL; +} + +gboolean +smudge_non_gui (GimpDrawable *drawable, + double pressure, + int num_strokes, + double *stroke_array) +{ + int 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 = smudge_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) + smudge_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; +} diff --git a/app/tools/smudge.h b/app/tools/smudge.h new file mode 100644 index 0000000000..ab7fb82634 --- /dev/null +++ b/app/tools/smudge.h @@ -0,0 +1,42 @@ +/* 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. + */ +#ifndef __SMUDGE_H__ +#define __SMUDGE_H__ + +#include "paint_core.h" +#include "tools.h" + +typedef enum +{ + SMUDGE_TYPE_SMUDGE, + SMUDGE_TYPE_STREAK +} SmudgeType; + +typedef enum +{ + SMUDGE_MODE_HIGHLIGHTS, + SMUDGE_MODE_MIDTONES, + SMUDGE_MODE_SHADOWS +} SmudgeMode; + +void * smudge_paint_func (PaintCore *, GimpDrawable *, int); +gboolean smudge_non_gui (GimpDrawable *, double, int, double *); +Tool * tools_new_smudge (void); +void tools_free_smudge (Tool *); + +#endif /* __SMUDGE_H__ */ diff --git a/app/tools/tool_options.c b/app/tools/tool_options.c index 7d7746b9cd..50298fbd88 100644 --- a/app/tools/tool_options.c +++ b/app/tools/tool_options.c @@ -570,7 +570,11 @@ paint_options_init (PaintOptions *options, N_("Convolver Options") : ((tool_type == INK) ? N_("Ink Options") : - N_("ERROR: Unknown Paint Type")))))))))), + ((tool_type == DODGEBURN) ? + N_("Dodge or Burn Options") : + ((tool_type == SMUDGE) ? + N_("Smudge Options") : + N_("ERROR: Unknown Paint Type")))))))))))), reset_func); /* initialize the paint options structure */ diff --git a/app/tools/tools.c b/app/tools/tools.c index 37de4c3761..4cc92235a6 100644 --- a/app/tools/tools.c +++ b/app/tools/tools.c @@ -31,6 +31,7 @@ #include "crop.h" #include "curves.h" #include "devices.h" +#include "dodgeburn.h" #include "eraser.h" #include "gdisplay.h" #include "hue_saturation.h" @@ -49,6 +50,7 @@ #include "posterize.h" #include "rect_select.h" #include "session.h" +#include "smudge.h" #include "text_tool.h" #include "threshold.h" #include "tools.h" @@ -448,11 +450,41 @@ ToolInfo tool_info[] = NULL }, + { + NULL, + N_("Dodge or Burn"), + 22, + N_("/Tools/DodgeBurn"), + "D", + (char **) dodge_bits, + N_("Dodge or Burn"), + "ContextHelp/dodgeburn", + DODGEBURN, + tools_new_dodgeburn, + tools_free_dodgeburn, + NULL + }, + + { + NULL, + N_("Smudge"), + 23, + N_("/Tools/Smudge"), + "S", + (char **) smudge_bits, + N_("Smudge"), + "ContextHelp/smudge", + SMUDGE, + tools_new_smudge, + tools_free_smudge, + NULL + }, + /* Non-toolbox tools */ { NULL, N_("By Color Select"), - 22, + 24, N_("/Select/By Color..."), NULL, NULL, @@ -467,7 +499,7 @@ ToolInfo tool_info[] = { NULL, N_("Color Balance"), - 23, + 25, N_("/Image/Colors/Color Balance"), NULL, NULL, @@ -482,7 +514,7 @@ ToolInfo tool_info[] = { NULL, N_("Brightness-Contrast"), - 24, + 26, N_("/Image/Colors/Brightness-Contrast"), NULL, NULL, @@ -497,7 +529,7 @@ ToolInfo tool_info[] = { NULL, N_("Hue-Saturation"), - 25, + 27, N_("/Image/Colors/Hue-Saturation"), NULL, NULL, @@ -512,7 +544,7 @@ ToolInfo tool_info[] = { NULL, N_("Posterize"), - 26, + 28, N_("/Image/Colors/Posterize"), NULL, NULL, @@ -527,7 +559,7 @@ ToolInfo tool_info[] = { NULL, N_("Threshold"), - 27, + 29, N_("/Image/Colors/Threshold"), NULL, NULL, @@ -542,7 +574,7 @@ ToolInfo tool_info[] = { NULL, N_("Curves"), - 28, + 30, N_("/Image/Colors/Curves"), NULL, NULL, @@ -557,7 +589,7 @@ ToolInfo tool_info[] = { NULL, N_("Levels"), - 29, + 31, N_("/Image/Colors/Levels"), NULL, NULL, @@ -572,7 +604,7 @@ ToolInfo tool_info[] = { NULL, N_("Histogram"), - 30, + 32, N_("/Image/Histogram"), NULL, NULL, diff --git a/app/toolsF.h b/app/toolsF.h index 26cbac4c55..177a258491 100644 --- a/app/toolsF.h +++ b/app/toolsF.h @@ -62,7 +62,9 @@ typedef enum CLONE, CONVOLVE, INK, - LAST_TOOLBOX_TOOL = INK, + DODGEBURN, + SMUDGE, + LAST_TOOLBOX_TOOL = SMUDGE, /* Non-toolbox tools */ BY_COLOR_SELECT,