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