1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18 #include "config.h"
19
20 #include <string.h>
21
22 #include <gegl.h>
23 #include <gtk/gtk.h>
24
25 #include "libgimpmath/gimpmath.h"
26
27 #include "tools-types.h"
28
29 #include "config/gimpdisplayconfig.h"
30
31 #include "core/gimp.h"
32 #include "core/gimpbezierdesc.h"
33 #include "core/gimpbrush.h"
34 #include "core/gimpimage.h"
35 #include "core/gimptoolinfo.h"
36
37 #include "paint/gimpbrushcore.h"
38 #include "paint/gimppaintoptions.h"
39
40 #include "display/gimpcanvashandle.h"
41 #include "display/gimpcanvaspath.h"
42 #include "display/gimpdisplay.h"
43 #include "display/gimpdisplayshell.h"
44
45 #include "gimpbrushtool.h"
46 #include "gimppainttool-paint.h"
47 #include "gimptoolcontrol.h"
48
49
50 static void gimp_brush_tool_constructed (GObject *object);
51
52 static void gimp_brush_tool_oper_update (GimpTool *tool,
53 const GimpCoords *coords,
54 GdkModifierType state,
55 gboolean proximity,
56 GimpDisplay *display);
57 static void gimp_brush_tool_cursor_update (GimpTool *tool,
58 const GimpCoords *coords,
59 GdkModifierType state,
60 GimpDisplay *display);
61 static void gimp_brush_tool_options_notify (GimpTool *tool,
62 GimpToolOptions *options,
63 const GParamSpec *pspec);
64
65 static void gimp_brush_tool_paint_start (GimpPaintTool *paint_tool);
66 static void gimp_brush_tool_paint_end (GimpPaintTool *paint_tool);
67 static void gimp_brush_tool_paint_flush (GimpPaintTool *paint_tool);
68 static GimpCanvasItem *
69 gimp_brush_tool_get_outline (GimpPaintTool *paint_tool,
70 GimpDisplay *display,
71 gdouble x,
72 gdouble y);
73
74 static void gimp_brush_tool_brush_changed (GimpContext *context,
75 GimpBrush *brush,
76 GimpBrushTool *brush_tool);
77 static void gimp_brush_tool_set_brush (GimpBrushCore *brush_core,
78 GimpBrush *brush,
79 GimpBrushTool *brush_tool);
80
81 static const GimpBezierDesc *
82 gimp_brush_tool_get_boundary (GimpBrushTool *brush_tool,
83 gint *width,
84 gint *height);
85
86
G_DEFINE_TYPE(GimpBrushTool,gimp_brush_tool,GIMP_TYPE_PAINT_TOOL)87 G_DEFINE_TYPE (GimpBrushTool, gimp_brush_tool, GIMP_TYPE_PAINT_TOOL)
88
89 #define parent_class gimp_brush_tool_parent_class
90
91
92 static void
93 gimp_brush_tool_class_init (GimpBrushToolClass *klass)
94 {
95 GObjectClass *object_class = G_OBJECT_CLASS (klass);
96 GimpToolClass *tool_class = GIMP_TOOL_CLASS (klass);
97 GimpPaintToolClass *paint_tool_class = GIMP_PAINT_TOOL_CLASS (klass);
98
99 object_class->constructed = gimp_brush_tool_constructed;
100
101 tool_class->oper_update = gimp_brush_tool_oper_update;
102 tool_class->cursor_update = gimp_brush_tool_cursor_update;
103 tool_class->options_notify = gimp_brush_tool_options_notify;
104
105 paint_tool_class->paint_start = gimp_brush_tool_paint_start;
106 paint_tool_class->paint_end = gimp_brush_tool_paint_end;
107 paint_tool_class->paint_flush = gimp_brush_tool_paint_flush;
108 paint_tool_class->get_outline = gimp_brush_tool_get_outline;
109 }
110
111 static void
gimp_brush_tool_init(GimpBrushTool * brush_tool)112 gimp_brush_tool_init (GimpBrushTool *brush_tool)
113 {
114 GimpTool *tool = GIMP_TOOL (brush_tool);
115
116 gimp_tool_control_set_action_size (tool->control,
117 "tools/tools-paintbrush-size-set");
118 gimp_tool_control_set_action_aspect (tool->control,
119 "tools/tools-paintbrush-aspect-ratio-set");
120 gimp_tool_control_set_action_angle (tool->control,
121 "tools/tools-paintbrush-angle-set");
122 gimp_tool_control_set_action_spacing (tool->control,
123 "tools/tools-paintbrush-spacing-set");
124 gimp_tool_control_set_action_hardness (tool->control,
125 "tools/tools-paintbrush-hardness-set");
126 gimp_tool_control_set_action_force (tool->control,
127 "tools/tools-paintbrush-force-set");
128 gimp_tool_control_set_action_object_1 (tool->control,
129 "context/context-brush-select-set");
130 }
131
132 static void
gimp_brush_tool_constructed(GObject * object)133 gimp_brush_tool_constructed (GObject *object)
134 {
135 GimpTool *tool = GIMP_TOOL (object);
136 GimpPaintTool *paint_tool = GIMP_PAINT_TOOL (object);
137
138 G_OBJECT_CLASS (parent_class)->constructed (object);
139
140 gimp_assert (GIMP_IS_BRUSH_CORE (paint_tool->core));
141
142 g_signal_connect_object (gimp_tool_get_options (tool), "brush-changed",
143 G_CALLBACK (gimp_brush_tool_brush_changed),
144 paint_tool, 0);
145
146 g_signal_connect_object (paint_tool->core, "set-brush",
147 G_CALLBACK (gimp_brush_tool_set_brush),
148 paint_tool, 0);
149 }
150
151 static void
gimp_brush_tool_oper_update(GimpTool * tool,const GimpCoords * coords,GdkModifierType state,gboolean proximity,GimpDisplay * display)152 gimp_brush_tool_oper_update (GimpTool *tool,
153 const GimpCoords *coords,
154 GdkModifierType state,
155 gboolean proximity,
156 GimpDisplay *display)
157 {
158 GimpPaintOptions *paint_options = GIMP_PAINT_TOOL_GET_OPTIONS (tool);
159 GimpDrawable *drawable;
160
161 gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
162
163 GIMP_TOOL_CLASS (parent_class)->oper_update (tool, coords, state,
164 proximity, display);
165
166 drawable = gimp_image_get_active_drawable (gimp_display_get_image (display));
167
168 if (! gimp_color_tool_is_enabled (GIMP_COLOR_TOOL (tool)) &&
169 drawable && proximity)
170 {
171 GimpContext *context = GIMP_CONTEXT (paint_options);
172 GimpPaintTool *paint_tool = GIMP_PAINT_TOOL (tool);
173 GimpBrushCore *brush_core = GIMP_BRUSH_CORE (paint_tool->core);
174
175 gimp_brush_core_set_brush (brush_core,
176 gimp_context_get_brush (context));
177
178 gimp_brush_core_set_dynamics (brush_core,
179 gimp_context_get_dynamics (context));
180
181 if (GIMP_BRUSH_CORE_GET_CLASS (brush_core)->handles_transforming_brush)
182 {
183 gimp_brush_core_eval_transform_dynamics (brush_core,
184 drawable,
185 paint_options,
186 coords);
187 }
188 }
189
190 gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
191 }
192
193 static void
gimp_brush_tool_cursor_update(GimpTool * tool,const GimpCoords * coords,GdkModifierType state,GimpDisplay * display)194 gimp_brush_tool_cursor_update (GimpTool *tool,
195 const GimpCoords *coords,
196 GdkModifierType state,
197 GimpDisplay *display)
198 {
199 GimpBrushTool *brush_tool = GIMP_BRUSH_TOOL (tool);
200 GimpBrushCore *brush_core = GIMP_BRUSH_CORE (GIMP_PAINT_TOOL (brush_tool)->core);
201
202 if (! gimp_color_tool_is_enabled (GIMP_COLOR_TOOL (tool)))
203 {
204 if (! brush_core->main_brush || ! brush_core->dynamics)
205 {
206 gimp_tool_set_cursor (tool, display,
207 gimp_tool_control_get_cursor (tool->control),
208 gimp_tool_control_get_tool_cursor (tool->control),
209 GIMP_CURSOR_MODIFIER_BAD);
210 return;
211 }
212 }
213
214 GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display);
215 }
216
217 static void
gimp_brush_tool_options_notify(GimpTool * tool,GimpToolOptions * options,const GParamSpec * pspec)218 gimp_brush_tool_options_notify (GimpTool *tool,
219 GimpToolOptions *options,
220 const GParamSpec *pspec)
221 {
222 GIMP_TOOL_CLASS (parent_class)->options_notify (tool, options, pspec);
223
224 if (! strcmp (pspec->name, "brush-size") ||
225 ! strcmp (pspec->name, "brush-angle") ||
226 ! strcmp (pspec->name, "brush-aspect-ratio"))
227 {
228 GimpPaintTool *paint_tool = GIMP_PAINT_TOOL (tool);
229 GimpBrushCore *brush_core = GIMP_BRUSH_CORE (paint_tool->core);
230
231 g_signal_emit_by_name (brush_core, "set-brush",
232 brush_core->main_brush);
233 }
234 }
235
236 static void
gimp_brush_tool_paint_start(GimpPaintTool * paint_tool)237 gimp_brush_tool_paint_start (GimpPaintTool *paint_tool)
238 {
239 GimpBrushTool *brush_tool = GIMP_BRUSH_TOOL (paint_tool);
240 GimpBrushCore *brush_core = GIMP_BRUSH_CORE (paint_tool->core);
241 const GimpBezierDesc *boundary;
242
243 if (GIMP_PAINT_TOOL_CLASS (parent_class)->paint_start)
244 GIMP_PAINT_TOOL_CLASS (parent_class)->paint_start (paint_tool);
245
246 boundary = gimp_brush_tool_get_boundary (brush_tool,
247 &brush_tool->boundary_width,
248 &brush_tool->boundary_height);
249
250 if (boundary)
251 brush_tool->boundary = gimp_bezier_desc_copy (boundary);
252
253 brush_tool->boundary_scale = brush_core->scale;
254 brush_tool->boundary_aspect_ratio = brush_core->aspect_ratio;
255 brush_tool->boundary_angle = brush_core->angle;
256 brush_tool->boundary_reflect = brush_core->reflect;
257 brush_tool->boundary_hardness = brush_core->hardness;
258 }
259
260 static void
gimp_brush_tool_paint_end(GimpPaintTool * paint_tool)261 gimp_brush_tool_paint_end (GimpPaintTool *paint_tool)
262 {
263 GimpBrushTool *brush_tool = GIMP_BRUSH_TOOL (paint_tool);
264
265 g_clear_pointer (&brush_tool->boundary, gimp_bezier_desc_free);
266
267 if (GIMP_PAINT_TOOL_CLASS (parent_class)->paint_end)
268 GIMP_PAINT_TOOL_CLASS (parent_class)->paint_end (paint_tool);
269 }
270
271 static void
gimp_brush_tool_paint_flush(GimpPaintTool * paint_tool)272 gimp_brush_tool_paint_flush (GimpPaintTool *paint_tool)
273 {
274 GimpBrushTool *brush_tool = GIMP_BRUSH_TOOL (paint_tool);
275 GimpBrushCore *brush_core = GIMP_BRUSH_CORE (paint_tool->core);
276 const GimpBezierDesc *boundary;
277
278 if (GIMP_PAINT_TOOL_CLASS (parent_class)->paint_flush)
279 GIMP_PAINT_TOOL_CLASS (parent_class)->paint_flush (paint_tool);
280
281 if (brush_tool->boundary_scale != brush_core->scale ||
282 brush_tool->boundary_aspect_ratio != brush_core->aspect_ratio ||
283 brush_tool->boundary_angle != brush_core->angle ||
284 brush_tool->boundary_reflect != brush_core->reflect ||
285 brush_tool->boundary_hardness != brush_core->hardness)
286 {
287 g_clear_pointer (&brush_tool->boundary, gimp_bezier_desc_free);
288
289 boundary = gimp_brush_tool_get_boundary (brush_tool,
290 &brush_tool->boundary_width,
291 &brush_tool->boundary_height);
292
293 if (boundary)
294 brush_tool->boundary = gimp_bezier_desc_copy (boundary);
295
296 brush_tool->boundary_scale = brush_core->scale;
297 brush_tool->boundary_aspect_ratio = brush_core->aspect_ratio;
298 brush_tool->boundary_angle = brush_core->angle;
299 brush_tool->boundary_reflect = brush_core->reflect;
300 brush_tool->boundary_hardness = brush_core->hardness;
301 }
302 }
303
304 static GimpCanvasItem *
gimp_brush_tool_get_outline(GimpPaintTool * paint_tool,GimpDisplay * display,gdouble x,gdouble y)305 gimp_brush_tool_get_outline (GimpPaintTool *paint_tool,
306 GimpDisplay *display,
307 gdouble x,
308 gdouble y)
309 {
310 GimpBrushTool *brush_tool = GIMP_BRUSH_TOOL (paint_tool);
311 GimpCanvasItem *item;
312
313 item = gimp_brush_tool_create_outline (brush_tool, display, x, y);
314
315 if (! item)
316 {
317 GimpBrushCore *brush_core = GIMP_BRUSH_CORE (paint_tool->core);
318
319 if (brush_core->main_brush && brush_core->dynamics)
320 {
321 /* if an outline was expected, but got scaled away by
322 * transform/dynamics, draw a circle in the "normal" size.
323 */
324 GimpPaintOptions *options;
325
326 options = GIMP_PAINT_TOOL_GET_OPTIONS (brush_tool);
327
328 gimp_paint_tool_set_draw_fallback (paint_tool,
329 TRUE, options->brush_size);
330 }
331 }
332
333 return item;
334 }
335
336 GimpCanvasItem *
gimp_brush_tool_create_outline(GimpBrushTool * brush_tool,GimpDisplay * display,gdouble x,gdouble y)337 gimp_brush_tool_create_outline (GimpBrushTool *brush_tool,
338 GimpDisplay *display,
339 gdouble x,
340 gdouble y)
341 {
342 GimpTool *tool;
343 GimpDisplayShell *shell;
344 const GimpBezierDesc *boundary = NULL;
345 gint width = 0;
346 gint height = 0;
347
348 g_return_val_if_fail (GIMP_IS_BRUSH_TOOL (brush_tool), NULL);
349 g_return_val_if_fail (GIMP_IS_DISPLAY (display), NULL);
350
351 if (gimp_paint_tool_paint_is_active (GIMP_PAINT_TOOL (brush_tool)))
352 {
353 boundary = brush_tool->boundary;
354 width = brush_tool->boundary_width;
355 height = brush_tool->boundary_height;
356 }
357 else
358 {
359 boundary = gimp_brush_tool_get_boundary (brush_tool, &width, &height);
360 }
361
362 if (! boundary)
363 return NULL;
364
365 tool = GIMP_TOOL (brush_tool);
366 shell = gimp_display_get_shell (display);
367
368 /* don't draw the boundary if it becomes too small */
369 if (SCALEX (shell, width) > 4 &&
370 SCALEY (shell, height) > 4)
371 {
372 x -= width / 2.0;
373 y -= height / 2.0;
374
375 if (gimp_tool_control_get_precision (tool->control) ==
376 GIMP_CURSOR_PRECISION_PIXEL_CENTER)
377 {
378 #define EPSILON 0.000001
379 /* Add EPSILON before rounding since e.g.
380 * (5.0 - 0.5) may end up at (4.499999999....)
381 * due to floating point fnords
382 */
383 x = RINT (x + EPSILON);
384 y = RINT (y + EPSILON);
385 #undef EPSILON
386 }
387
388 return gimp_canvas_path_new (shell, boundary, x, y, FALSE,
389 GIMP_PATH_STYLE_OUTLINE);
390 }
391
392 return NULL;
393 }
394
395 static void
gimp_brush_tool_brush_changed(GimpContext * context,GimpBrush * brush,GimpBrushTool * brush_tool)396 gimp_brush_tool_brush_changed (GimpContext *context,
397 GimpBrush *brush,
398 GimpBrushTool *brush_tool)
399 {
400 GimpPaintTool *paint_tool = GIMP_PAINT_TOOL (brush_tool);
401 GimpBrushCore *brush_core = GIMP_BRUSH_CORE (paint_tool->core);
402
403 gimp_brush_core_set_brush (brush_core, brush);
404
405 }
406
407 static void
gimp_brush_tool_set_brush(GimpBrushCore * brush_core,GimpBrush * brush,GimpBrushTool * brush_tool)408 gimp_brush_tool_set_brush (GimpBrushCore *brush_core,
409 GimpBrush *brush,
410 GimpBrushTool *brush_tool)
411 {
412 gimp_draw_tool_pause (GIMP_DRAW_TOOL (brush_tool));
413
414 if (GIMP_BRUSH_CORE_GET_CLASS (brush_core)->handles_transforming_brush)
415 {
416 GimpPaintCore *paint_core = GIMP_PAINT_CORE (brush_core);
417
418 gimp_brush_core_eval_transform_dynamics (brush_core,
419 NULL,
420 GIMP_PAINT_TOOL_GET_OPTIONS (brush_tool),
421 &paint_core->cur_coords);
422 }
423
424 gimp_draw_tool_resume (GIMP_DRAW_TOOL (brush_tool));
425 }
426
427 static const GimpBezierDesc *
gimp_brush_tool_get_boundary(GimpBrushTool * brush_tool,gint * width,gint * height)428 gimp_brush_tool_get_boundary (GimpBrushTool *brush_tool,
429 gint *width,
430 gint *height)
431 {
432 GimpPaintTool *paint_tool = GIMP_PAINT_TOOL (brush_tool);
433 GimpBrushCore *brush_core = GIMP_BRUSH_CORE (paint_tool->core);
434
435 if (paint_tool->draw_brush &&
436 brush_core->main_brush &&
437 brush_core->dynamics &&
438 brush_core->scale > 0.0)
439 {
440 return gimp_brush_transform_boundary (brush_core->main_brush,
441 brush_core->scale,
442 brush_core->aspect_ratio,
443 brush_core->angle,
444 brush_core->reflect,
445 brush_core->hardness,
446 width,
447 height);
448 }
449
450 return NULL;
451 }
452