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 <cairo.h>
21 #include <gdk-pixbuf/gdk-pixbuf.h>
22 #include <gegl.h>
23 
24 #include "libgimpbase/gimpbase.h"
25 #include "libgimpmath/gimpmath.h"
26 
27 #include "core-types.h"
28 
29 #include "gimpbezierdesc.h"
30 #include "gimpbrush.h"
31 #include "gimpbrush-boundary.h"
32 #include "gimpbrush-load.h"
33 #include "gimpbrush-mipmap.h"
34 #include "gimpbrush-private.h"
35 #include "gimpbrush-save.h"
36 #include "gimpbrush-transform.h"
37 #include "gimpbrushcache.h"
38 #include "gimpbrushgenerated.h"
39 #include "gimpbrushpipe.h"
40 #include "gimpmarshal.h"
41 #include "gimptagged.h"
42 #include "gimptempbuf.h"
43 
44 #include "gimp-intl.h"
45 
46 
47 enum
48 {
49   SPACING_CHANGED,
50   LAST_SIGNAL
51 };
52 
53 enum
54 {
55   PROP_0,
56   PROP_SPACING
57 };
58 
59 
60 static void          gimp_brush_tagged_iface_init     (GimpTaggedInterface  *iface);
61 
62 static void          gimp_brush_finalize              (GObject              *object);
63 static void          gimp_brush_set_property          (GObject              *object,
64                                                        guint                 property_id,
65                                                        const GValue         *value,
66                                                        GParamSpec           *pspec);
67 static void          gimp_brush_get_property          (GObject              *object,
68                                                        guint                 property_id,
69                                                        GValue               *value,
70                                                        GParamSpec           *pspec);
71 
72 static gint64        gimp_brush_get_memsize           (GimpObject           *object,
73                                                        gint64               *gui_size);
74 
75 static gboolean      gimp_brush_get_size              (GimpViewable         *viewable,
76                                                        gint                 *width,
77                                                        gint                 *height);
78 static GimpTempBuf * gimp_brush_get_new_preview       (GimpViewable         *viewable,
79                                                        GimpContext          *context,
80                                                        gint                  width,
81                                                        gint                  height);
82 static gchar       * gimp_brush_get_description       (GimpViewable         *viewable,
83                                                        gchar               **tooltip);
84 
85 static void          gimp_brush_dirty                 (GimpData             *data);
86 static const gchar * gimp_brush_get_extension         (GimpData             *data);
87 static void          gimp_brush_copy                  (GimpData             *data,
88                                                        GimpData             *src_data);
89 
90 static void          gimp_brush_real_begin_use        (GimpBrush            *brush);
91 static void          gimp_brush_real_end_use          (GimpBrush            *brush);
92 static GimpBrush   * gimp_brush_real_select_brush     (GimpBrush            *brush,
93                                                        const GimpCoords     *last_coords,
94                                                        const GimpCoords     *current_coords);
95 static gboolean      gimp_brush_real_want_null_motion (GimpBrush            *brush,
96                                                        const GimpCoords     *last_coords,
97                                                        const GimpCoords     *current_coords);
98 
99 static gchar       * gimp_brush_get_checksum          (GimpTagged           *tagged);
100 
101 
102 G_DEFINE_TYPE_WITH_CODE (GimpBrush, gimp_brush, GIMP_TYPE_DATA,
103                          G_ADD_PRIVATE (GimpBrush)
104                          G_IMPLEMENT_INTERFACE (GIMP_TYPE_TAGGED,
105                                                 gimp_brush_tagged_iface_init))
106 
107 #define parent_class gimp_brush_parent_class
108 
109 static guint brush_signals[LAST_SIGNAL] = { 0 };
110 
111 
112 static void
gimp_brush_class_init(GimpBrushClass * klass)113 gimp_brush_class_init (GimpBrushClass *klass)
114 {
115   GObjectClass      *object_class      = G_OBJECT_CLASS (klass);
116   GimpObjectClass   *gimp_object_class = GIMP_OBJECT_CLASS (klass);
117   GimpViewableClass *viewable_class    = GIMP_VIEWABLE_CLASS (klass);
118   GimpDataClass     *data_class        = GIMP_DATA_CLASS (klass);
119 
120   brush_signals[SPACING_CHANGED] =
121     g_signal_new ("spacing-changed",
122                   G_TYPE_FROM_CLASS (klass),
123                   G_SIGNAL_RUN_FIRST,
124                   G_STRUCT_OFFSET (GimpBrushClass, spacing_changed),
125                   NULL, NULL,
126                   gimp_marshal_VOID__VOID,
127                   G_TYPE_NONE, 0);
128 
129   object_class->finalize            = gimp_brush_finalize;
130   object_class->get_property        = gimp_brush_get_property;
131   object_class->set_property        = gimp_brush_set_property;
132 
133   gimp_object_class->get_memsize    = gimp_brush_get_memsize;
134 
135   viewable_class->default_icon_name = "gimp-tool-paintbrush";
136   viewable_class->get_size          = gimp_brush_get_size;
137   viewable_class->get_new_preview   = gimp_brush_get_new_preview;
138   viewable_class->get_description   = gimp_brush_get_description;
139 
140   data_class->dirty                 = gimp_brush_dirty;
141   data_class->save                  = gimp_brush_save;
142   data_class->get_extension         = gimp_brush_get_extension;
143   data_class->copy                  = gimp_brush_copy;
144 
145   klass->begin_use                  = gimp_brush_real_begin_use;
146   klass->end_use                    = gimp_brush_real_end_use;
147   klass->select_brush               = gimp_brush_real_select_brush;
148   klass->want_null_motion           = gimp_brush_real_want_null_motion;
149   klass->transform_size             = gimp_brush_real_transform_size;
150   klass->transform_mask             = gimp_brush_real_transform_mask;
151   klass->transform_pixmap           = gimp_brush_real_transform_pixmap;
152   klass->transform_boundary         = gimp_brush_real_transform_boundary;
153   klass->spacing_changed            = NULL;
154 
155   g_object_class_install_property (object_class, PROP_SPACING,
156                                    g_param_spec_double ("spacing", NULL,
157                                                         _("Brush Spacing"),
158                                                         1.0, 5000.0, 20.0,
159                                                         GIMP_PARAM_READWRITE |
160                                                         G_PARAM_CONSTRUCT));
161 }
162 
163 static void
gimp_brush_tagged_iface_init(GimpTaggedInterface * iface)164 gimp_brush_tagged_iface_init (GimpTaggedInterface *iface)
165 {
166   iface->get_checksum = gimp_brush_get_checksum;
167 }
168 
169 static void
gimp_brush_init(GimpBrush * brush)170 gimp_brush_init (GimpBrush *brush)
171 {
172   brush->priv = gimp_brush_get_instance_private (brush);
173 
174   brush->priv->spacing  = 20;
175   brush->priv->x_axis.x = 15.0;
176   brush->priv->x_axis.y =  0.0;
177   brush->priv->y_axis.x =  0.0;
178   brush->priv->y_axis.y = 15.0;
179 
180   brush->priv->blur_hardness = 1.0;
181 }
182 
183 static void
gimp_brush_finalize(GObject * object)184 gimp_brush_finalize (GObject *object)
185 {
186   GimpBrush *brush = GIMP_BRUSH (object);
187 
188   g_clear_pointer (&brush->priv->mask,          gimp_temp_buf_unref);
189   g_clear_pointer (&brush->priv->pixmap,        gimp_temp_buf_unref);
190   g_clear_pointer (&brush->priv->blurred_mask,   gimp_temp_buf_unref);
191   g_clear_pointer (&brush->priv->blurred_pixmap, gimp_temp_buf_unref);
192 
193   gimp_brush_mipmap_clear (brush);
194 
195   g_clear_object (&brush->priv->mask_cache);
196   g_clear_object (&brush->priv->pixmap_cache);
197   g_clear_object (&brush->priv->boundary_cache);
198 
199   G_OBJECT_CLASS (parent_class)->finalize (object);
200 }
201 
202 static void
gimp_brush_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)203 gimp_brush_set_property (GObject      *object,
204                          guint         property_id,
205                          const GValue *value,
206                          GParamSpec   *pspec)
207 {
208   GimpBrush *brush = GIMP_BRUSH (object);
209 
210   switch (property_id)
211     {
212     case PROP_SPACING:
213       gimp_brush_set_spacing (brush, g_value_get_double (value));
214       break;
215 
216     default:
217       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
218       break;
219     }
220 }
221 
222 static void
gimp_brush_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)223 gimp_brush_get_property (GObject    *object,
224                          guint       property_id,
225                          GValue     *value,
226                          GParamSpec *pspec)
227 {
228   GimpBrush *brush = GIMP_BRUSH (object);
229 
230   switch (property_id)
231     {
232     case PROP_SPACING:
233       g_value_set_double (value, gimp_brush_get_spacing (brush));
234       break;
235 
236     default:
237       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
238       break;
239     }
240 }
241 
242 static gint64
gimp_brush_get_memsize(GimpObject * object,gint64 * gui_size)243 gimp_brush_get_memsize (GimpObject *object,
244                         gint64     *gui_size)
245 {
246   GimpBrush *brush   = GIMP_BRUSH (object);
247   gint64     memsize = 0;
248 
249   memsize += gimp_temp_buf_get_memsize (brush->priv->mask);
250   memsize += gimp_temp_buf_get_memsize (brush->priv->pixmap);
251 
252   memsize += gimp_brush_mipmap_get_memsize (brush);
253 
254   return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
255                                                                   gui_size);
256 }
257 
258 static gboolean
gimp_brush_get_size(GimpViewable * viewable,gint * width,gint * height)259 gimp_brush_get_size (GimpViewable *viewable,
260                      gint         *width,
261                      gint         *height)
262 {
263   GimpBrush *brush = GIMP_BRUSH (viewable);
264 
265   *width  = gimp_temp_buf_get_width  (brush->priv->mask);
266   *height = gimp_temp_buf_get_height (brush->priv->mask);
267 
268   return TRUE;
269 }
270 
271 static GimpTempBuf *
gimp_brush_get_new_preview(GimpViewable * viewable,GimpContext * context,gint width,gint height)272 gimp_brush_get_new_preview (GimpViewable *viewable,
273                             GimpContext  *context,
274                             gint          width,
275                             gint          height)
276 {
277   GimpBrush         *brush       = GIMP_BRUSH (viewable);
278   const GimpTempBuf *mask_buf    = brush->priv->mask;
279   const GimpTempBuf *pixmap_buf  = brush->priv->pixmap;
280   GimpTempBuf       *return_buf  = NULL;
281   gint               mask_width;
282   gint               mask_height;
283   guchar            *mask_data;
284   guchar            *mask;
285   guchar            *buf;
286   gint               x, y;
287   gboolean           scaled = FALSE;
288 
289   mask_width  = gimp_temp_buf_get_width  (mask_buf);
290   mask_height = gimp_temp_buf_get_height (mask_buf);
291 
292   if (mask_width > width || mask_height > height)
293     {
294       gdouble ratio_x = (gdouble) width  / (gdouble) mask_width;
295       gdouble ratio_y = (gdouble) height / (gdouble) mask_height;
296       gdouble scale   = MIN (ratio_x, ratio_y);
297 
298       if (scale != 1.0)
299         {
300           gimp_brush_begin_use (brush);
301 
302           if (GIMP_IS_BRUSH_GENERATED (brush))
303             {
304                GimpBrushGenerated *gen_brush = GIMP_BRUSH_GENERATED (brush);
305 
306                mask_buf = gimp_brush_transform_mask (brush, scale,
307                                                      (gimp_brush_generated_get_aspect_ratio (gen_brush) - 1.0) * 20.0 / 19.0,
308                                                      gimp_brush_generated_get_angle (gen_brush) / 360.0,
309                                                      FALSE,
310                                                      gimp_brush_generated_get_hardness (gen_brush));
311             }
312           else
313             mask_buf = gimp_brush_transform_mask (brush, scale,
314                                                   0.0, 0.0, FALSE, 1.0);
315 
316           if (! mask_buf)
317             {
318               mask_buf = gimp_temp_buf_new (1, 1, babl_format ("Y u8"));
319               gimp_temp_buf_data_clear ((GimpTempBuf *) mask_buf);
320             }
321           else
322             {
323               gimp_temp_buf_ref ((GimpTempBuf *) mask_buf);
324             }
325 
326           if (pixmap_buf)
327             pixmap_buf = gimp_brush_transform_pixmap (brush, scale,
328                                                       0.0, 0.0, FALSE, 1.0);
329 
330           mask_width  = gimp_temp_buf_get_width  (mask_buf);
331           mask_height = gimp_temp_buf_get_height (mask_buf);
332 
333           scaled = TRUE;
334         }
335     }
336 
337   return_buf = gimp_temp_buf_new (mask_width, mask_height,
338                                   babl_format ("R'G'B'A u8"));
339 
340   mask = mask_data = gimp_temp_buf_lock (mask_buf, babl_format ("Y u8"),
341                                          GEGL_ACCESS_READ);
342   buf  = gimp_temp_buf_get_data (return_buf);
343 
344   if (pixmap_buf)
345     {
346       guchar *pixmap_data;
347       guchar *pixmap;
348 
349       pixmap = pixmap_data = gimp_temp_buf_lock (pixmap_buf,
350                                                  babl_format ("R'G'B' u8"),
351                                                  GEGL_ACCESS_READ);
352 
353       for (y = 0; y < mask_height; y++)
354         {
355           for (x = 0; x < mask_width ; x++)
356             {
357               *buf++ = *pixmap++;
358               *buf++ = *pixmap++;
359               *buf++ = *pixmap++;
360               *buf++ = *mask++;
361             }
362         }
363 
364       gimp_temp_buf_unlock (pixmap_buf, pixmap_data);
365     }
366   else
367     {
368       for (y = 0; y < mask_height; y++)
369         {
370           for (x = 0; x < mask_width ; x++)
371             {
372               *buf++ = 0;
373               *buf++ = 0;
374               *buf++ = 0;
375               *buf++ = *mask++;
376             }
377         }
378     }
379 
380   gimp_temp_buf_unlock (mask_buf, mask_data);
381 
382   if (scaled)
383     {
384       gimp_temp_buf_unref ((GimpTempBuf *) mask_buf);
385 
386       gimp_brush_end_use (brush);
387     }
388 
389   return return_buf;
390 }
391 
392 static gchar *
gimp_brush_get_description(GimpViewable * viewable,gchar ** tooltip)393 gimp_brush_get_description (GimpViewable  *viewable,
394                             gchar        **tooltip)
395 {
396   GimpBrush *brush = GIMP_BRUSH (viewable);
397 
398   return g_strdup_printf ("%s (%d × %d)",
399                           gimp_object_get_name (brush),
400                           gimp_temp_buf_get_width  (brush->priv->mask),
401                           gimp_temp_buf_get_height (brush->priv->mask));
402 }
403 
404 static void
gimp_brush_dirty(GimpData * data)405 gimp_brush_dirty (GimpData *data)
406 {
407   GimpBrush *brush = GIMP_BRUSH (data);
408 
409   if (brush->priv->mask_cache)
410     gimp_brush_cache_clear (brush->priv->mask_cache);
411 
412   if (brush->priv->pixmap_cache)
413     gimp_brush_cache_clear (brush->priv->pixmap_cache);
414 
415   if (brush->priv->boundary_cache)
416     gimp_brush_cache_clear (brush->priv->boundary_cache);
417 
418   gimp_brush_mipmap_clear (brush);
419 
420   g_clear_pointer (&brush->priv->blurred_mask,   gimp_temp_buf_unref);
421   g_clear_pointer (&brush->priv->blurred_pixmap, gimp_temp_buf_unref);
422 
423   GIMP_DATA_CLASS (parent_class)->dirty (data);
424 }
425 
426 static const gchar *
gimp_brush_get_extension(GimpData * data)427 gimp_brush_get_extension (GimpData *data)
428 {
429   return GIMP_BRUSH_FILE_EXTENSION;
430 }
431 
432 static void
gimp_brush_copy(GimpData * data,GimpData * src_data)433 gimp_brush_copy (GimpData *data,
434                  GimpData *src_data)
435 {
436   GimpBrush *brush     = GIMP_BRUSH (data);
437   GimpBrush *src_brush = GIMP_BRUSH (src_data);
438 
439   g_clear_pointer (&brush->priv->mask, gimp_temp_buf_unref);
440   if (src_brush->priv->mask)
441     brush->priv->mask = gimp_temp_buf_copy (src_brush->priv->mask);
442 
443   g_clear_pointer (&brush->priv->pixmap, gimp_temp_buf_unref);
444   if (src_brush->priv->pixmap)
445     brush->priv->pixmap = gimp_temp_buf_copy (src_brush->priv->pixmap);
446 
447   brush->priv->spacing = src_brush->priv->spacing;
448   brush->priv->x_axis  = src_brush->priv->x_axis;
449   brush->priv->y_axis  = src_brush->priv->y_axis;
450 
451   gimp_data_dirty (data);
452 }
453 
454 static void
gimp_brush_real_begin_use(GimpBrush * brush)455 gimp_brush_real_begin_use (GimpBrush *brush)
456 {
457   brush->priv->mask_cache =
458     gimp_brush_cache_new ((GDestroyNotify) gimp_temp_buf_unref, 'M', 'm');
459 
460   brush->priv->pixmap_cache =
461     gimp_brush_cache_new ((GDestroyNotify) gimp_temp_buf_unref, 'P', 'p');
462 
463   brush->priv->boundary_cache =
464     gimp_brush_cache_new ((GDestroyNotify) gimp_bezier_desc_free, 'B', 'b');
465 }
466 
467 static void
gimp_brush_real_end_use(GimpBrush * brush)468 gimp_brush_real_end_use (GimpBrush *brush)
469 {
470   g_clear_object (&brush->priv->mask_cache);
471   g_clear_object (&brush->priv->pixmap_cache);
472   g_clear_object (&brush->priv->boundary_cache);
473 
474   g_clear_pointer (&brush->priv->blurred_mask,   gimp_temp_buf_unref);
475   g_clear_pointer (&brush->priv->blurred_pixmap, gimp_temp_buf_unref);
476 }
477 
478 static GimpBrush *
gimp_brush_real_select_brush(GimpBrush * brush,const GimpCoords * last_coords,const GimpCoords * current_coords)479 gimp_brush_real_select_brush (GimpBrush        *brush,
480                               const GimpCoords *last_coords,
481                               const GimpCoords *current_coords)
482 {
483   return brush;
484 }
485 
486 static gboolean
gimp_brush_real_want_null_motion(GimpBrush * brush,const GimpCoords * last_coords,const GimpCoords * current_coords)487 gimp_brush_real_want_null_motion (GimpBrush        *brush,
488                                   const GimpCoords *last_coords,
489                                   const GimpCoords *current_coords)
490 {
491   return TRUE;
492 }
493 
494 static gchar *
gimp_brush_get_checksum(GimpTagged * tagged)495 gimp_brush_get_checksum (GimpTagged *tagged)
496 {
497   GimpBrush *brush           = GIMP_BRUSH (tagged);
498   gchar     *checksum_string = NULL;
499 
500   if (brush->priv->mask)
501     {
502       GChecksum *checksum = g_checksum_new (G_CHECKSUM_MD5);
503 
504       g_checksum_update (checksum,
505                          gimp_temp_buf_get_data (brush->priv->mask),
506                          gimp_temp_buf_get_data_size (brush->priv->mask));
507       if (brush->priv->pixmap)
508         g_checksum_update (checksum,
509                            gimp_temp_buf_get_data (brush->priv->pixmap),
510                            gimp_temp_buf_get_data_size (brush->priv->pixmap));
511       g_checksum_update (checksum,
512                          (const guchar *) &brush->priv->spacing,
513                          sizeof (brush->priv->spacing));
514       g_checksum_update (checksum,
515                          (const guchar *) &brush->priv->x_axis,
516                          sizeof (brush->priv->x_axis));
517       g_checksum_update (checksum,
518                          (const guchar *) &brush->priv->y_axis,
519                          sizeof (brush->priv->y_axis));
520 
521       checksum_string = g_strdup (g_checksum_get_string (checksum));
522 
523       g_checksum_free (checksum);
524     }
525 
526   return checksum_string;
527 }
528 
529 /*  public functions  */
530 
531 GimpData *
gimp_brush_new(GimpContext * context,const gchar * name)532 gimp_brush_new (GimpContext *context,
533                 const gchar *name)
534 {
535   g_return_val_if_fail (name != NULL, NULL);
536 
537   return gimp_brush_generated_new (name,
538                                    GIMP_BRUSH_GENERATED_CIRCLE,
539                                    5.0, 2, 0.5, 1.0, 0.0);
540 }
541 
542 GimpData *
gimp_brush_get_standard(GimpContext * context)543 gimp_brush_get_standard (GimpContext *context)
544 {
545   static GimpData *standard_brush = NULL;
546 
547   if (! standard_brush)
548     {
549       standard_brush = gimp_brush_new (context, "Standard");
550 
551       gimp_data_clean (standard_brush);
552       gimp_data_make_internal (standard_brush, "gimp-brush-standard");
553 
554       g_object_add_weak_pointer (G_OBJECT (standard_brush),
555                                  (gpointer *) &standard_brush);
556     }
557 
558   return standard_brush;
559 }
560 
561 void
gimp_brush_begin_use(GimpBrush * brush)562 gimp_brush_begin_use (GimpBrush *brush)
563 {
564   g_return_if_fail (GIMP_IS_BRUSH (brush));
565 
566   brush->priv->use_count++;
567 
568   if (brush->priv->use_count == 1)
569     GIMP_BRUSH_GET_CLASS (brush)->begin_use (brush);
570 }
571 
572 void
gimp_brush_end_use(GimpBrush * brush)573 gimp_brush_end_use (GimpBrush *brush)
574 {
575   g_return_if_fail (GIMP_IS_BRUSH (brush));
576   g_return_if_fail (brush->priv->use_count > 0);
577 
578   brush->priv->use_count--;
579 
580   if (brush->priv->use_count == 0)
581     GIMP_BRUSH_GET_CLASS (brush)->end_use (brush);
582 }
583 
584 GimpBrush *
gimp_brush_select_brush(GimpBrush * brush,const GimpCoords * last_coords,const GimpCoords * current_coords)585 gimp_brush_select_brush (GimpBrush        *brush,
586                          const GimpCoords *last_coords,
587                          const GimpCoords *current_coords)
588 {
589   g_return_val_if_fail (GIMP_IS_BRUSH (brush), NULL);
590   g_return_val_if_fail (last_coords != NULL, NULL);
591   g_return_val_if_fail (current_coords != NULL, NULL);
592 
593   return GIMP_BRUSH_GET_CLASS (brush)->select_brush (brush,
594                                                      last_coords,
595                                                      current_coords);
596 }
597 
598 gboolean
gimp_brush_want_null_motion(GimpBrush * brush,const GimpCoords * last_coords,const GimpCoords * current_coords)599 gimp_brush_want_null_motion (GimpBrush        *brush,
600                              const GimpCoords *last_coords,
601                              const GimpCoords *current_coords)
602 {
603   g_return_val_if_fail (GIMP_IS_BRUSH (brush), FALSE);
604   g_return_val_if_fail (last_coords != NULL, FALSE);
605   g_return_val_if_fail (current_coords != NULL, FALSE);
606 
607   return GIMP_BRUSH_GET_CLASS (brush)->want_null_motion (brush,
608                                                          last_coords,
609                                                          current_coords);
610 }
611 
612 void
gimp_brush_transform_size(GimpBrush * brush,gdouble scale,gdouble aspect_ratio,gdouble angle,gboolean reflect,gint * width,gint * height)613 gimp_brush_transform_size (GimpBrush     *brush,
614                            gdouble        scale,
615                            gdouble        aspect_ratio,
616                            gdouble        angle,
617                            gboolean       reflect,
618                            gint          *width,
619                            gint          *height)
620 {
621   g_return_if_fail (GIMP_IS_BRUSH (brush));
622   g_return_if_fail (scale > 0.0);
623   g_return_if_fail (width != NULL);
624   g_return_if_fail (height != NULL);
625 
626   if (scale             == 1.0 &&
627       aspect_ratio      == 0.0 &&
628       fmod (angle, 0.5) == 0.0)
629     {
630       *width  = gimp_temp_buf_get_width  (brush->priv->mask);
631       *height = gimp_temp_buf_get_height (brush->priv->mask);
632 
633       return;
634     }
635 
636   GIMP_BRUSH_GET_CLASS (brush)->transform_size (brush,
637                                                 scale, aspect_ratio, angle, reflect,
638                                                 width, height);
639 }
640 
641 const GimpTempBuf *
gimp_brush_transform_mask(GimpBrush * brush,gdouble scale,gdouble aspect_ratio,gdouble angle,gboolean reflect,gdouble hardness)642 gimp_brush_transform_mask (GimpBrush *brush,
643                            gdouble    scale,
644                            gdouble    aspect_ratio,
645                            gdouble    angle,
646                            gboolean   reflect,
647                            gdouble    hardness)
648 {
649   const GimpTempBuf *mask;
650   gint               width;
651   gint               height;
652   gdouble            effective_hardness = hardness;
653 
654   g_return_val_if_fail (GIMP_IS_BRUSH (brush), NULL);
655   g_return_val_if_fail (scale > 0.0, NULL);
656 
657   gimp_brush_transform_size (brush,
658                              scale, aspect_ratio, angle, reflect,
659                              &width, &height);
660 
661   mask = gimp_brush_cache_get (brush->priv->mask_cache,
662                                width, height,
663                                scale, aspect_ratio, angle, reflect, hardness);
664 
665   if (! mask)
666     {
667 #if 0
668       /* This code makes sure that brushes using blur for hardness
669        * (all of them but generated) are blurred once and no more.
670        * It also makes hardnes dynamics not work for these brushes.
671        * This is intentional. Confoliving for each stamp is too expensive.*/
672       if (! brush->priv->blurred_mask &&
673           ! GIMP_IS_BRUSH_GENERATED(brush) &&
674           ! GIMP_IS_BRUSH_PIPE(brush) && /*Can't cache pipes. Sanely anyway*/
675           hardness < 1.0)
676         {
677            brush->priv->blurred_mask = GIMP_BRUSH_GET_CLASS (brush)->transform_mask (brush,
678                                                              1.0,
679                                                              0.0,
680                                                              0.0,
681                                                              FALSE,
682                                                              hardness);
683            brush->priv->blur_hardness = hardness;
684         }
685 
686       if (brush->priv->blurred_mask)
687         {
688           effective_hardness = 1.0; /*Hardness has already been applied*/
689         }
690 #endif
691 
692       mask = GIMP_BRUSH_GET_CLASS (brush)->transform_mask (brush,
693                                                            scale,
694                                                            aspect_ratio,
695                                                            angle,
696                                                            reflect,
697                                                            effective_hardness);
698 
699       gimp_brush_cache_add (brush->priv->mask_cache,
700                             (gpointer) mask,
701                             width, height,
702                             scale, aspect_ratio, angle, reflect, effective_hardness);
703     }
704 
705   return mask;
706 }
707 
708 const GimpTempBuf *
gimp_brush_transform_pixmap(GimpBrush * brush,gdouble scale,gdouble aspect_ratio,gdouble angle,gboolean reflect,gdouble hardness)709 gimp_brush_transform_pixmap (GimpBrush *brush,
710                              gdouble    scale,
711                              gdouble    aspect_ratio,
712                              gdouble    angle,
713                              gboolean   reflect,
714                              gdouble    hardness)
715 {
716   const GimpTempBuf *pixmap;
717   gint               width;
718   gint               height;
719   gdouble            effective_hardness = hardness;
720 
721   g_return_val_if_fail (GIMP_IS_BRUSH (brush), NULL);
722   g_return_val_if_fail (brush->priv->pixmap != NULL, NULL);
723   g_return_val_if_fail (scale > 0.0, NULL);
724 
725   gimp_brush_transform_size (brush,
726                              scale, aspect_ratio, angle, reflect,
727                              &width, &height);
728 
729   pixmap = gimp_brush_cache_get (brush->priv->pixmap_cache,
730                                  width, height,
731                                  scale, aspect_ratio, angle, reflect, hardness);
732 
733   if (! pixmap)
734     {
735 #if 0
736      if (! brush->priv->blurred_pixmap &&
737          ! GIMP_IS_BRUSH_GENERATED(brush) &&
738          ! GIMP_IS_BRUSH_PIPE(brush) /*Can't cache pipes. Sanely anyway*/
739          && hardness < 1.0)
740       {
741          brush->priv->blurred_pixmap = GIMP_BRUSH_GET_CLASS (brush)->transform_pixmap (brush,
742                                                                   1.0,
743                                                                   0.0,
744                                                                   0.0,
745                                                                   FALSE,
746                                                                   hardness);
747          brush->priv->blur_hardness = hardness;
748        }
749 
750       if (brush->priv->blurred_pixmap) {
751         effective_hardness = 1.0; /*Hardness has already been applied*/
752       }
753 #endif
754 
755       pixmap = GIMP_BRUSH_GET_CLASS (brush)->transform_pixmap (brush,
756                                                                scale,
757                                                                aspect_ratio,
758                                                                angle,
759                                                                reflect,
760                                                                effective_hardness);
761 
762       gimp_brush_cache_add (brush->priv->pixmap_cache,
763                             (gpointer) pixmap,
764                             width, height,
765                             scale, aspect_ratio, angle, reflect, effective_hardness);
766     }
767 
768   return pixmap;
769 }
770 
771 const GimpBezierDesc *
gimp_brush_transform_boundary(GimpBrush * brush,gdouble scale,gdouble aspect_ratio,gdouble angle,gboolean reflect,gdouble hardness,gint * width,gint * height)772 gimp_brush_transform_boundary (GimpBrush *brush,
773                                gdouble    scale,
774                                gdouble    aspect_ratio,
775                                gdouble    angle,
776                                gboolean   reflect,
777                                gdouble    hardness,
778                                gint      *width,
779                                gint      *height)
780 {
781   const GimpBezierDesc *boundary;
782 
783   g_return_val_if_fail (GIMP_IS_BRUSH (brush), NULL);
784   g_return_val_if_fail (scale > 0.0, NULL);
785   g_return_val_if_fail (width != NULL, NULL);
786   g_return_val_if_fail (height != NULL, NULL);
787 
788   gimp_brush_transform_size (brush,
789                              scale, aspect_ratio, angle, reflect,
790                              width, height);
791 
792   boundary = gimp_brush_cache_get (brush->priv->boundary_cache,
793                                    *width, *height,
794                                    scale, aspect_ratio, angle, reflect, hardness);
795 
796   if (! boundary)
797     {
798       boundary = GIMP_BRUSH_GET_CLASS (brush)->transform_boundary (brush,
799                                                                    scale,
800                                                                    aspect_ratio,
801                                                                    angle,
802                                                                    reflect,
803                                                                    hardness,
804                                                                    width,
805                                                                    height);
806 
807       /*  while the brush mask is always at least 1x1 pixels, its
808        *  outline can correctly be NULL
809        *
810        *  FIXME: make the cache handle NULL things when it is
811        *         properly implemented
812        */
813       if (boundary)
814         gimp_brush_cache_add (brush->priv->boundary_cache,
815                               (gpointer) boundary,
816                               *width, *height,
817                               scale, aspect_ratio, angle, reflect, hardness);
818     }
819 
820   return boundary;
821 }
822 
823 GimpTempBuf *
gimp_brush_get_mask(GimpBrush * brush)824 gimp_brush_get_mask (GimpBrush *brush)
825 {
826   g_return_val_if_fail (brush != NULL, NULL);
827   g_return_val_if_fail (GIMP_IS_BRUSH (brush), NULL);
828 
829   if (brush->priv->blurred_mask)
830     {
831       return brush->priv->blurred_mask;
832     }
833   return brush->priv->mask;
834 }
835 
836 GimpTempBuf *
gimp_brush_get_pixmap(GimpBrush * brush)837 gimp_brush_get_pixmap (GimpBrush *brush)
838 {
839   g_return_val_if_fail (brush != NULL, NULL);
840   g_return_val_if_fail (GIMP_IS_BRUSH (brush), NULL);
841 
842   if(brush->priv->blurred_pixmap)
843     {
844       return brush->priv->blurred_pixmap;
845     }
846   return brush->priv->pixmap;
847 }
848 
849 void
gimp_brush_flush_blur_caches(GimpBrush * brush)850 gimp_brush_flush_blur_caches (GimpBrush *brush)
851 {
852 #if 0
853   g_clear_pointer (&brush->priv->blurred_mask,   gimp_temp_buf_unref);
854   g_clear_pointer (&brush->priv->blurred_pixmap, gimp_temp_buf_unref);
855 
856   if (brush->priv->mask_cache)
857     gimp_brush_cache_clear (brush->priv->mask_cache);
858 
859   if (brush->priv->pixmap_cache)
860     gimp_brush_cache_clear (brush->priv->pixmap_cache);
861 
862   if (brush->priv->boundary_cache)
863     gimp_brush_cache_clear (brush->priv->boundary_cache);
864 #endif
865 }
866 
867 gdouble
gimp_brush_get_blur_hardness(GimpBrush * brush)868 gimp_brush_get_blur_hardness (GimpBrush *brush)
869 {
870   g_return_val_if_fail (GIMP_IS_BRUSH (brush), 0);
871 
872   return brush->priv->blur_hardness;
873 }
874 
875 gint
gimp_brush_get_width(GimpBrush * brush)876 gimp_brush_get_width (GimpBrush *brush)
877 {
878   g_return_val_if_fail (GIMP_IS_BRUSH (brush), 0);
879 
880   if (brush->priv->blurred_mask)
881     return gimp_temp_buf_get_width (brush->priv->blurred_mask);
882 
883   if (brush->priv->blurred_pixmap)
884     return gimp_temp_buf_get_width (brush->priv->blurred_pixmap);
885 
886   return gimp_temp_buf_get_width (brush->priv->mask);
887 }
888 
889 gint
gimp_brush_get_height(GimpBrush * brush)890 gimp_brush_get_height (GimpBrush *brush)
891 {
892   g_return_val_if_fail (GIMP_IS_BRUSH (brush), 0);
893 
894   if (brush->priv->blurred_mask)
895     return gimp_temp_buf_get_height (brush->priv->blurred_mask);
896 
897   if (brush->priv->blurred_pixmap)
898     return gimp_temp_buf_get_height (brush->priv->blurred_pixmap);
899 
900   return gimp_temp_buf_get_height (brush->priv->mask);
901 }
902 
903 gint
gimp_brush_get_spacing(GimpBrush * brush)904 gimp_brush_get_spacing (GimpBrush *brush)
905 {
906   g_return_val_if_fail (GIMP_IS_BRUSH (brush), 0);
907 
908   return brush->priv->spacing;
909 }
910 
911 void
gimp_brush_set_spacing(GimpBrush * brush,gint spacing)912 gimp_brush_set_spacing (GimpBrush *brush,
913                         gint       spacing)
914 {
915   g_return_if_fail (GIMP_IS_BRUSH (brush));
916 
917   if (brush->priv->spacing != spacing)
918     {
919       brush->priv->spacing = spacing;
920 
921       g_signal_emit (brush, brush_signals[SPACING_CHANGED], 0);
922       g_object_notify (G_OBJECT (brush), "spacing");
923     }
924 }
925 
926 static const GimpVector2 fail = { 0.0, 0.0 };
927 
928 GimpVector2
gimp_brush_get_x_axis(GimpBrush * brush)929 gimp_brush_get_x_axis (GimpBrush *brush)
930 {
931   g_return_val_if_fail (GIMP_IS_BRUSH (brush), fail);
932 
933   return brush->priv->x_axis;
934 }
935 
936 GimpVector2
gimp_brush_get_y_axis(GimpBrush * brush)937 gimp_brush_get_y_axis (GimpBrush *brush)
938 {
939   g_return_val_if_fail (GIMP_IS_BRUSH (brush), fail);
940 
941   return brush->priv->y_axis;
942 }
943