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