1 /* $Id: brush.c,v 1.3 2004/12/25 04:41:58 meffie Exp $
2  *
3  *
4  * GNU Paint
5  * Copyright 2000-2003, 2007  Li-Cheng (Andy) Tai
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 3
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be
13  * useful, but WITHOUT ANY WARRANTY; without even the implied
14  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #  include <config.h>
23 #endif
24 
25 #include "brush.h"
26 #include "debug.h"
27 #include <math.h>
28 
29 static const double EPSILON = 0.00001;
30 
31 typedef struct _gpaint_brush
32 {
33     gpaint_tool base;
34     void (*draw)(struct _gpaint_brush*,int,int);
35     double spacing;
36     int    size;
37     double distance;
38     gpaint_point drag;
39 } gpaint_brush;
40 
41 typedef struct _gpaint_eraser
42 {
43     gpaint_brush base;
44     GdkColor *fgcolor;  /* to restore the foreground color  */
45 } gpaint_eraser;
46 
47 #define GPAINT_BRUSH(tool) ((gpaint_brush*)(tool))
48 #define GPAINT_ERASER(tool) ((gpaint_eraser*)(tool))
49 
50 static void eraser_select(gpaint_tool *tool);
51 static void eraser_deselect(gpaint_tool *tool);
52 static void eraser_draw(gpaint_brush *brush, int x, int y);
53 static gboolean eraser_attribute(gpaint_tool *tool, gpaint_attribute, gpointer);
54 
55 static void eraser_set_foreground_color(gpaint_eraser *eraser, GdkColor* color);
56 static void eraser_set_background_color(gpaint_eraser *eraser, GdkColor* color);
57 
58 static void paint_brush_draw(gpaint_brush *brush, int x, int y);
59 
60 static void brush_destroy(gpaint_tool *tool);
61 static void brush_select(gpaint_tool *tool);
62 static void brush_button_press(gpaint_tool *tool, int x, int y);
63 static void brush_button_release(gpaint_tool *tool, int x, int y);
64 static void brush_motion(gpaint_tool *tool, int x, int y);
65 static void brush_interpolate(gpaint_brush *brush, int x, int y);
66 
67 
68 /*
69  * Create the erase tool object.
70  */
71 gpaint_tool *
eraser_create(const char * name)72 eraser_create(const char *name)
73 {
74     gpaint_eraser *eraser = g_new0(gpaint_eraser,1);
75 
76     GPAINT_TOOL(eraser)->name   = name;
77     GPAINT_TOOL(eraser)->cursor = gdk_cursor_new(GDK_X_CURSOR);
78     GPAINT_TOOL(eraser)->destroy        = brush_destroy;
79     GPAINT_TOOL(eraser)->select         = brush_select;
80     GPAINT_TOOL(eraser)->button_press   = brush_button_press;
81     GPAINT_TOOL(eraser)->button_release = brush_button_release;
82     GPAINT_TOOL(eraser)->motion         = brush_motion;
83     GPAINT_TOOL(eraser)->select         = eraser_select;
84     GPAINT_TOOL(eraser)->deselect       = eraser_deselect;
85     GPAINT_TOOL(eraser)->attribute      = eraser_attribute;
86 
87     GPAINT_BRUSH(eraser)->draw    = eraser_draw;
88     GPAINT_BRUSH(eraser)->spacing = 3.0;
89     GPAINT_BRUSH(eraser)->size = 15;
90     GPAINT_BRUSH(eraser)->distance = 0;
91     return GPAINT_TOOL(eraser);
92 }
93 
94 /*
95  * Set up the eraser tool when selected. Use the current
96  * background color as the foreground to simulate erasing.
97  */
98 static void
eraser_select(gpaint_tool * tool)99 eraser_select(gpaint_tool *tool)
100 {
101     GdkGCValues gcvalues;
102     GPAINT_BRUSH(tool)->drag.defined = FALSE;
103     gdk_gc_get_values(tool->drawing->gc, &gcvalues);
104     g_assert(!GPAINT_ERASER(tool)->fgcolor);
105     GPAINT_ERASER(tool)->fgcolor = gdk_color_copy(&(gcvalues.foreground));
106     gdk_gc_set_foreground(tool->drawing->gc, &(gcvalues.background));
107 }
108 
109 /*
110  * Restore the foreground color when the eraser is deselect.
111  */
112 static void
eraser_deselect(gpaint_tool * tool)113 eraser_deselect(gpaint_tool *tool)
114 {
115     gdk_gc_set_foreground(tool->drawing->gc, GPAINT_ERASER(tool)->fgcolor);
116     gdk_color_free(GPAINT_ERASER(tool)->fgcolor);
117     GPAINT_ERASER(tool)->fgcolor = NULL;
118 }
119 
120 /*
121  * Erase
122  */
123 static void
eraser_draw(gpaint_brush * brush,int x,int y)124 eraser_draw(gpaint_brush *brush, int x, int y)
125 {
126     int radius = brush->size / 2;
127     x -= radius;
128     y -= radius;
129     gdk_draw_rectangle(
130             GPAINT_TOOL(brush)->drawing->window,
131             GPAINT_TOOL(brush)->drawing->gc,
132             TRUE, x, y, brush->size, brush->size);
133     gdk_draw_rectangle(
134             GPAINT_TOOL(brush)->drawing->backing_pixmap,
135             GPAINT_TOOL(brush)->drawing->gc,
136             TRUE, x, y, brush->size, brush->size);
137     drawing_modified(GPAINT_TOOL(brush)->drawing);
138 }
139 
140 /*
141  * Save the new foreground/backgound color.
142  */
143 static gboolean
eraser_attribute(gpaint_tool * tool,gpaint_attribute attrib,gpointer data)144 eraser_attribute(gpaint_tool* tool, gpaint_attribute attrib, gpointer data)
145 {
146     gboolean handled = FALSE;
147     gpaint_eraser *eraser = GPAINT_ERASER(tool);
148     debug_fn();
149     if (attrib == GpaintForegroundColor)
150     {
151         eraser_set_foreground_color(eraser, (GdkColor*)data);
152         handled = TRUE;
153     }
154     else if (attrib == GpaintBackgroundColor)
155     {
156         eraser_set_background_color(eraser, (GdkColor*)data);
157         handled = TRUE;
158     }
159     return handled;
160 }
161 
162 /*
163  * Save the new foreground color.
164  */
165 static void
eraser_set_foreground_color(gpaint_eraser * eraser,GdkColor * color)166 eraser_set_foreground_color(gpaint_eraser *eraser, GdkColor* color)
167 {
168     debug_fn();
169     g_assert(eraser->fgcolor);
170     gdk_color_free(eraser->fgcolor);
171     eraser->fgcolor = gdk_color_copy(color);
172 }
173 
174 /*
175  * Set the new background color.
176  */
177 static void
eraser_set_background_color(gpaint_eraser * eraser,GdkColor * color)178 eraser_set_background_color(gpaint_eraser *eraser, GdkColor* color)
179 {
180     gpaint_tool *tool = GPAINT_TOOL(eraser);
181     debug_fn();
182     gdk_gc_set_background(tool->drawing->gc, color);
183     gdk_gc_set_foreground(tool->drawing->gc, color);
184 }
185 
186 /*
187  * Create the paint brush tool object.
188  */
189 gpaint_tool *
paint_brush_create(const char * name)190 paint_brush_create(const char *name)
191 {
192     gpaint_brush *brush = g_new0(gpaint_brush, 1);
193     g_assert(brush);
194     GPAINT_TOOL(brush)->name = name;
195     GPAINT_TOOL(brush)->cursor = gdk_cursor_new(GDK_CROSSHAIR);
196     GPAINT_TOOL(brush)->destroy        = brush_destroy;
197     GPAINT_TOOL(brush)->select         = brush_select;
198     GPAINT_TOOL(brush)->button_press   = brush_button_press;
199     GPAINT_TOOL(brush)->button_release = brush_button_release;
200     GPAINT_TOOL(brush)->motion         = brush_motion;
201     brush->draw = paint_brush_draw;
202     brush->spacing = 3.0;
203     brush->size = 15;
204     brush->distance = 0;
205     return GPAINT_TOOL(brush);
206 }
207 
208 /*
209  * Paint
210  *
211  * TODO: brush styles.
212  */
213 static void
paint_brush_draw(gpaint_brush * brush,int x,int y)214 paint_brush_draw(gpaint_brush *brush, int x, int y)
215 {
216     int radius = brush->size / 2;
217     x -= radius;
218     y -= radius;
219     gdk_draw_arc( GPAINT_TOOL(brush)->drawing->window,
220                   GPAINT_TOOL(brush)->drawing->gc,
221                   TRUE, x, y, brush->size, brush->size, 0, 360 * 64);
222     gdk_draw_arc( GPAINT_TOOL(brush)->drawing->backing_pixmap,
223                   GPAINT_TOOL(brush)->drawing->gc,
224                   TRUE, x, y, brush->size, brush->size, 0, 360 * 64);
225     drawing_modified(GPAINT_TOOL(brush)->drawing);
226 }
227 
228 /*
229  * Free the brush tool object.
230  */
231 static void
brush_destroy(gpaint_tool * tool)232 brush_destroy(gpaint_tool *tool)
233 {
234     debug_fn();
235     gdk_cursor_destroy(tool->cursor);
236     g_free(tool);
237 }
238 
239 /*
240  * Set up the brush tool when selected.
241  */
242 static void
brush_select(gpaint_tool * tool)243 brush_select(gpaint_tool *tool)
244 {
245     GPAINT_BRUSH(tool)->drag.defined = FALSE;
246 }
247 
248 /*
249  * Draw a point where the brush was pressed.
250  */
251 static void
brush_button_press(gpaint_tool * tool,int x,int y)252 brush_button_press(gpaint_tool *tool, int x, int y)
253 {
254     gpaint_brush *brush = GPAINT_BRUSH(tool);
255     debug_fn();
256     set_point(&brush->drag, x, y);
257     (*(brush->draw))(brush, x, y);
258 }
259 
260 /*
261  * Handle paint brush motion.
262  */
263 static void
brush_motion(gpaint_tool * tool,int x,int y)264 brush_motion(gpaint_tool *tool, int x, int y)
265 {
266     gpaint_brush* brush = GPAINT_BRUSH(tool);
267     if (!is_point_defined(&brush->drag))
268     {
269         set_point(&brush->drag, x, y);
270     }
271     brush_interpolate(brush, x, y);
272     set_point(&brush->drag, x, y);
273 }
274 
275 /*
276  * Handle paint brush release.
277  */
278 static void
brush_button_release(gpaint_tool * tool,int x,int y)279 brush_button_release(gpaint_tool *tool, int x, int y)
280 {
281     debug_fn();
282     clear_point(&(GPAINT_BRUSH(tool)->drag));
283 }
284 
285 
286 /*
287  * Paint along the mouse movement.
288  * adapted from paint_core.c in the GIMP.
289  */
290 static void
brush_interpolate(gpaint_brush * brush,int x,int y)291 brush_interpolate(gpaint_brush *brush, int x, int y)
292 {
293     double dx;         /* delta x */
294     double dy;         /* delta y */
295     double moved;      /* mouse movement */
296     double initial;    /* initial paint distance */
297     double final;      /* final paint distance   */
298 
299     double points;     /* number of paint points */
300     double next;       /* distance to the next point */
301     double percent;    /* linear interplotation, 0 to 1.0 */
302 
303     /* calculate mouse move distance */
304     dx = (double)(x - brush->drag.x);
305     dy = (double)(y - brush->drag.y);
306     moved = sqrt(dx*dx + dy*dy);
307 
308     initial = brush->distance;
309     final = initial + moved;
310 
311     /* paint along the movement */
312     while (brush->distance < final)
313     {
314         /* calculate distance to the next point */
315         points = (int) (brush->distance / brush->spacing + 1.0 + EPSILON);
316         next = points * brush->spacing - brush->distance;
317         brush->distance += next;
318         if (brush->distance <= (final + EPSILON))
319         {
320            /* calculate interpolation */
321            percent = (brush->distance - initial) / moved;
322            x = (int)(brush->drag.x + percent * dx);
323            y = (int)(brush->drag.y + percent * dy);
324 
325            (*brush->draw)(brush, x, y);
326         }
327      }
328      brush->distance = final;
329 }
330 
331