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