Commit 21b8007b authored by Michael Natterer's avatar Michael Natterer 😴

app: add GimpBrushCache which stores brush stuff based on transform parameters

such as masks and outlines. The cache is currently very stupid and
only cacheds the last transformed object. Add caches to GimpBrush for
its mask, its pixmap and its boundary, and remove the same caches and
a ton of members from GimpBrushCore. This involves adding lots of
const qualifiers because GimpBrush returns const pointers now for
trasnformed stuff.
parent 8d5ac2c7
......@@ -62,6 +62,8 @@ libappcore_a_sources = \
gimpbrush-load.h \
gimpbrush-transform.c \
gimpbrush-transform.h \
gimpbrushcache.c \
gimpbrushcache.h \
gimpbrushclipboard.c \
gimpbrushclipboard.h \
gimpbrushgenerated.c \
......
......@@ -101,6 +101,7 @@ typedef struct _GimpToolInfo GimpToolInfo;
typedef struct _GimpDataFactory GimpDataFactory;
typedef struct _GimpData GimpData;
typedef struct _GimpBrush GimpBrush;
typedef struct _GimpBrushCache GimpBrushCache;
typedef struct _GimpBrushClipboard GimpBrushClipboard;
typedef struct _GimpBrushGenerated GimpBrushGenerated;
typedef struct _GimpBrushPipe GimpBrushPipe;
......
......@@ -38,7 +38,7 @@ gimp_brush_transform_boundary_exact (GimpBrush *brush,
gdouble angle,
gdouble hardness)
{
TempBuf *mask;
const TempBuf *mask;
mask = gimp_brush_transform_mask (brush,
scale, aspect_ratio, angle, hardness);
......@@ -49,7 +49,7 @@ gimp_brush_transform_boundary_exact (GimpBrush *brush,
BoundSeg *bound_segs;
gint n_bound_segs;
pixel_region_init_temp_buf (&maskPR, mask,
pixel_region_init_temp_buf (&maskPR, (TempBuf *) mask,
0, 0, mask->width, mask->height);
bound_segs = boundary_find (&maskPR, BOUNDARY_WITHIN_BOUNDS,
......@@ -57,8 +57,6 @@ gimp_brush_transform_boundary_exact (GimpBrush *brush,
0,
&n_bound_segs);
temp_buf_free (mask);
if (bound_segs)
{
BoundSeg *stroke_segs;
......
......@@ -18,6 +18,7 @@
#include "config.h"
#include <glib-object.h>
#include <cairo.h>
#include "libgimpbase/gimpbase.h"
#include "libgimpmath/gimpmath.h"
......@@ -26,10 +27,12 @@
#include "base/temp-buf.h"
#include "gimpbezierdesc.h"
#include "gimpbrush.h"
#include "gimpbrush-boundary.h"
#include "gimpbrush-load.h"
#include "gimpbrush-transform.h"
#include "gimpbrushcache.h"
#include "gimpbrushgenerated.h"
#include "gimpmarshal.h"
#include "gimptagged.h"
......@@ -52,6 +55,7 @@ enum
static void gimp_brush_tagged_iface_init (GimpTaggedInterface *iface);
static void gimp_brush_finalize (GObject *object);
static void gimp_brush_set_property (GObject *object,
guint property_id,
const GValue *value,
......@@ -60,7 +64,6 @@ static void gimp_brush_get_property (GObject *obj
guint property_id,
GValue *value,
GParamSpec *pspec);
static void gimp_brush_finalize (GObject *object);
static gint64 gimp_brush_get_memsize (GimpObject *object,
gint64 *gui_size);
......@@ -74,6 +77,8 @@ static TempBuf * gimp_brush_get_new_preview (GimpViewable *vie
gint height);
static gchar * gimp_brush_get_description (GimpViewable *viewable,
gchar **tooltip);
static void gimp_brush_dirty (GimpData *data);
static const gchar * gimp_brush_get_extension (GimpData *data);
static GimpBrush * gimp_brush_real_select_brush (GimpBrush *brush,
......@@ -112,9 +117,9 @@ gimp_brush_class_init (GimpBrushClass *klass)
gimp_marshal_VOID__VOID,
G_TYPE_NONE, 0);
object_class->finalize = gimp_brush_finalize;
object_class->get_property = gimp_brush_get_property;
object_class->set_property = gimp_brush_set_property;
object_class->finalize = gimp_brush_finalize;
gimp_object_class->get_memsize = gimp_brush_get_memsize;
......@@ -123,6 +128,7 @@ gimp_brush_class_init (GimpBrushClass *klass)
viewable_class->get_new_preview = gimp_brush_get_new_preview;
viewable_class->get_description = gimp_brush_get_description;
data_class->dirty = gimp_brush_dirty;
data_class->get_extension = gimp_brush_get_extension;
klass->select_brush = gimp_brush_real_select_brush;
......@@ -160,6 +166,44 @@ gimp_brush_init (GimpBrush *brush)
brush->y_axis.y = 15.0;
}
static void
gimp_brush_finalize (GObject *object)
{
GimpBrush *brush = GIMP_BRUSH (object);
if (brush->mask)
{
temp_buf_free (brush->mask);
brush->mask = NULL;
}
if (brush->pixmap)
{
temp_buf_free (brush->pixmap);
brush->pixmap = NULL;
}
if (brush->mask_cache)
{
g_object_unref (brush->mask_cache);
brush->mask_cache = NULL;
}
if (brush->pixmap_cache)
{
g_object_unref (brush->pixmap_cache);
brush->pixmap_cache = NULL;
}
if (brush->boundary_cache)
{
g_object_unref (brush->boundary_cache);
brush->boundary_cache = NULL;
}
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gimp_brush_set_property (GObject *object,
guint property_id,
......@@ -200,26 +244,6 @@ gimp_brush_get_property (GObject *object,
}
}
static void
gimp_brush_finalize (GObject *object)
{
GimpBrush *brush = GIMP_BRUSH (object);
if (brush->mask)
{
temp_buf_free (brush->mask);
brush->mask = NULL;
}
if (brush->pixmap)
{
temp_buf_free (brush->pixmap);
brush->pixmap = NULL;
}
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static gint64
gimp_brush_get_memsize (GimpObject *object,
gint64 *gui_size)
......@@ -253,17 +277,18 @@ gimp_brush_get_new_preview (GimpViewable *viewable,
gint width,
gint height)
{
GimpBrush *brush = GIMP_BRUSH (viewable);
TempBuf *mask_buf = NULL;
TempBuf *pixmap_buf = NULL;
TempBuf *return_buf = NULL;
gint mask_width;
gint mask_height;
guchar transp[4] = { 0, 0, 0, 0 };
guchar *mask;
guchar *buf;
gint x, y;
gboolean scaled = FALSE;
GimpBrush *brush = GIMP_BRUSH (viewable);
const TempBuf *mask_buf = NULL;
gboolean free_mask = FALSE;
const TempBuf *pixmap_buf = NULL;
TempBuf *return_buf = NULL;
gint mask_width;
gint mask_height;
guchar transp[4] = { 0, 0, 0, 0 };
guchar *mask;
guchar *buf;
gint x, y;
gboolean scaled = FALSE;
mask_buf = brush->mask;
pixmap_buf = brush->pixmap;
......@@ -279,13 +304,20 @@ gimp_brush_get_new_preview (GimpViewable *viewable,
if (scale != 1.0)
{
mask_buf = gimp_brush_transform_mask (brush, scale, 0.0, 0.0, 1.0);
gimp_brush_start_use (brush);
mask_buf = gimp_brush_transform_mask (brush, scale,
0.0, 0.0, 1.0);
if (! mask_buf)
mask_buf = temp_buf_new (1, 1, 1, 0, 0, transp);
{
mask_buf = temp_buf_new (1, 1, 1, 0, 0, transp);
free_mask = TRUE;
}
if (pixmap_buf)
pixmap_buf = gimp_brush_transform_pixmap (brush, scale, 0.0, 0.0, 1.0);
pixmap_buf = gimp_brush_transform_pixmap (brush, scale,
0.0, 0.0, 1.0);
mask_width = mask_buf->width;
mask_height = mask_buf->height;
......@@ -330,10 +362,10 @@ gimp_brush_get_new_preview (GimpViewable *viewable,
if (scaled)
{
temp_buf_free (mask_buf);
if (free_mask)
temp_buf_free ((TempBuf *) mask_buf);
if (pixmap_buf)
temp_buf_free (pixmap_buf);
gimp_brush_end_use (brush);
}
return return_buf;
......@@ -351,6 +383,23 @@ gimp_brush_get_description (GimpViewable *viewable,
brush->mask->height);
}
static void
gimp_brush_dirty (GimpData *data)
{
GimpBrush *brush = GIMP_BRUSH (data);
if (brush->mask_cache)
gimp_brush_cache_clear (brush->mask_cache);
if (brush->pixmap_cache)
gimp_brush_cache_clear (brush->pixmap_cache);
if (brush->boundary_cache)
gimp_brush_cache_clear (brush->boundary_cache);
GIMP_DATA_CLASS (parent_class)->dirty (data);
}
static const gchar *
gimp_brush_get_extension (GimpData *data)
{
......@@ -486,54 +535,106 @@ gimp_brush_transform_size (GimpBrush *brush,
width, height);
}
TempBuf *
const TempBuf *
gimp_brush_transform_mask (GimpBrush *brush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gdouble hardness)
{
const TempBuf *mask;
gint width;
gint height;
g_return_val_if_fail (GIMP_IS_BRUSH (brush), NULL);
g_return_val_if_fail (scale > 0.0, NULL);
if (scale == 1.0 &&
aspect_ratio == 0.0 &&
angle == 0.0 &&
hardness == 1.0)
gimp_brush_transform_size (brush,
scale, aspect_ratio, angle,
&width, &height);
mask = gimp_brush_cache_get (brush->mask_cache,
width, height,
scale, aspect_ratio, angle, hardness);
if (! mask)
{
return temp_buf_copy (brush->mask, NULL);
if (scale == 1.0 &&
aspect_ratio == 0.0 &&
angle == 0.0 &&
hardness == 1.0)
{
mask = temp_buf_copy (brush->mask, NULL);
}
else
{
mask = GIMP_BRUSH_GET_CLASS (brush)->transform_mask (brush,
scale,
aspect_ratio,
angle,
hardness);
}
gimp_brush_cache_add (brush->mask_cache,
(gpointer) mask,
width, height,
scale, aspect_ratio, angle, hardness);
}
return GIMP_BRUSH_GET_CLASS (brush)->transform_mask (brush,
scale, aspect_ratio,
angle, hardness);
return mask;
}
TempBuf *
const TempBuf *
gimp_brush_transform_pixmap (GimpBrush *brush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gdouble hardness)
{
const TempBuf *pixmap;
gint width;
gint height;
g_return_val_if_fail (GIMP_IS_BRUSH (brush), NULL);
g_return_val_if_fail (brush->pixmap != NULL, NULL);
g_return_val_if_fail (scale > 0.0, NULL);
if (scale == 1.0 &&
aspect_ratio == 0.0 &&
angle == 0.0 &&
hardness == 1.0)
gimp_brush_transform_size (brush,
scale, aspect_ratio, angle,
&width, &height);
pixmap = gimp_brush_cache_get (brush->pixmap_cache,
width, height,
scale, aspect_ratio, angle, hardness);
if (! pixmap)
{
return temp_buf_copy (brush->pixmap, NULL);
if (scale == 1.0 &&
aspect_ratio == 0.0 &&
angle == 0.0 &&
hardness == 1.0)
{
pixmap = temp_buf_copy (brush->pixmap, NULL);
}
else
{
pixmap = GIMP_BRUSH_GET_CLASS (brush)->transform_pixmap (brush,
scale,
aspect_ratio,
angle,
hardness);
}
gimp_brush_cache_add (brush->pixmap_cache,
(gpointer) pixmap,
width, height,
scale, aspect_ratio, angle, hardness);
}
return GIMP_BRUSH_GET_CLASS (brush)->transform_pixmap (brush,
scale, aspect_ratio,
angle, hardness);
return pixmap;
}
GimpBezierDesc *
const GimpBezierDesc *
gimp_brush_transform_boundary (GimpBrush *brush,
gdouble scale,
gdouble aspect_ratio,
......@@ -542,15 +643,38 @@ gimp_brush_transform_boundary (GimpBrush *brush,
gint *width,
gint *height)
{
const GimpBezierDesc *boundary;
g_return_val_if_fail (GIMP_IS_BRUSH (brush), NULL);
g_return_val_if_fail (scale > 0.0, NULL);
g_return_val_if_fail (width != NULL, NULL);
g_return_val_if_fail (height != NULL, NULL);
return GIMP_BRUSH_GET_CLASS (brush)->transform_boundary (brush,
scale, aspect_ratio,
angle, hardness,
width, height);
gimp_brush_transform_size (brush,
scale, aspect_ratio, angle,
width, height);
boundary = gimp_brush_cache_get (brush->boundary_cache,
*width, *height,
scale, aspect_ratio, angle, hardness);
if (! boundary)
{
boundary = GIMP_BRUSH_GET_CLASS (brush)->transform_boundary (brush,
scale,
aspect_ratio,
angle,
hardness,
width,
height);
gimp_brush_cache_add (brush->boundary_cache,
(gpointer) boundary,
*width, *height,
scale, aspect_ratio, angle, hardness);
}
return boundary;
}
gdouble
......@@ -612,6 +736,18 @@ gimp_brush_start_use (GimpBrush *brush)
g_return_if_fail (GIMP_IS_BRUSH (brush));
brush->use_count++;
if (brush->use_count == 1)
{
brush->mask_cache =
gimp_brush_cache_new ((GDestroyNotify) temp_buf_free, 'M', 'm');
brush->pixmap_cache =
gimp_brush_cache_new ((GDestroyNotify) temp_buf_free, 'P', 'p');
brush->boundary_cache =
gimp_brush_cache_new ((GDestroyNotify) gimp_bezier_desc_free, 'B', 'b');
}
}
void
......@@ -624,6 +760,13 @@ gimp_brush_end_use (GimpBrush *brush)
if (brush->use_count == 0)
{
/* flush caches */
g_object_unref (brush->mask_cache);
brush->mask_cache = NULL;
g_object_unref (brush->pixmap_cache);
brush->pixmap_cache = NULL;
g_object_unref (brush->boundary_cache);
brush->boundary_cache = NULL;
}
}
......@@ -35,16 +35,19 @@ typedef struct _GimpBrushClass GimpBrushClass;
struct _GimpBrush
{
GimpData parent_instance;
GimpData parent_instance;
TempBuf *mask; /* the actual mask */
TempBuf *pixmap; /* optional pixmap data */
TempBuf *mask; /* the actual mask */
TempBuf *pixmap; /* optional pixmap data */
gint spacing; /* brush's spacing */
GimpVector2 x_axis; /* for calculating brush spacing */
GimpVector2 y_axis; /* for calculating brush spacing */
gint spacing; /* brush's spacing */
GimpVector2 x_axis; /* for calculating brush spacing */
GimpVector2 y_axis; /* for calculating brush spacing */
gint use_count; /* for keeping the caches alive */
gint use_count; /* for keeping the caches alive */
GimpBrushCache *mask_cache;
GimpBrushCache *pixmap_cache;
GimpBrushCache *boundary_cache;
};
struct _GimpBrushClass
......@@ -87,58 +90,58 @@ struct _GimpBrushClass
};
GType gimp_brush_get_type (void) G_GNUC_CONST;
GType gimp_brush_get_type (void) G_GNUC_CONST;
GimpData * gimp_brush_new (GimpContext *context,
const gchar *name);
GimpData * gimp_brush_get_standard (GimpContext *context);
GimpData * gimp_brush_new (GimpContext *context,
const gchar *name);
GimpData * gimp_brush_get_standard (GimpContext *context);
GimpBrush * gimp_brush_select_brush (GimpBrush *brush,
const GimpCoords *last_coords,
const GimpCoords *current_coords);
gboolean gimp_brush_want_null_motion (GimpBrush *brush,
const GimpCoords *last_coords,
const GimpCoords *current_coords);
GimpBrush * gimp_brush_select_brush (GimpBrush *brush,
const GimpCoords *last_coords,
const GimpCoords *current_coords);
gboolean gimp_brush_want_null_motion (GimpBrush *brush,
const GimpCoords *last_coords,
const GimpCoords *current_coords);
/* Gets width and height of a transformed mask of the brush, for
* provided parameters.
*/
void gimp_brush_transform_size (GimpBrush *brush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gint *width,
gint *height);
TempBuf * gimp_brush_transform_mask (GimpBrush *brush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gdouble hardness);
TempBuf * gimp_brush_transform_pixmap (GimpBrush *brush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gdouble hardness);
GimpBezierDesc * gimp_brush_transform_boundary (GimpBrush *brush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gdouble hardness,
gint *width,
gint *height);
gdouble gimp_brush_clamp_scale (GimpBrush *brush,
gdouble scale);
TempBuf * gimp_brush_get_mask (const GimpBrush *brush);
TempBuf * gimp_brush_get_pixmap (const GimpBrush *brush);
gint gimp_brush_get_spacing (const GimpBrush *brush);
void gimp_brush_set_spacing (GimpBrush *brush,
gint spacing);
void gimp_brush_start_use (GimpBrush *brush);
void gimp_brush_end_use (GimpBrush *brush);
void gimp_brush_transform_size (GimpBrush *brush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gint *width,
gint *height);
const TempBuf * gimp_brush_transform_mask (GimpBrush *brush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gdouble hardness);
const TempBuf * gimp_brush_transform_pixmap (GimpBrush *brush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gdouble hardness);
const GimpBezierDesc * gimp_brush_transform_boundary (GimpBrush *brush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gdouble hardness,
gint *width,
gint *height);
gdouble gimp_brush_clamp_scale (GimpBrush *brush,
gdouble scale);
TempBuf * gimp_brush_get_mask (const GimpBrush *brush);
TempBuf * gimp_brush_get_pixmap (const GimpBrush *brush);
gint gimp_brush_get_spacing (const GimpBrush *brush);
void gimp_brush_set_spacing (GimpBrush *brush,
gint spacing);
void gimp_brush_start_use (GimpBrush *brush);
void gimp_brush_end_use (GimpBrush *brush);
#endif /* __GIMP_BRUSH_H__ */
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* gimpbrushcache.c
* Copyright (C) 2011 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include "core-types.h"
#include "gimpbrushcache.h"
#include "gimp-intl.h"
enum
{
PROP_0,
PROP_DATA_DESTROY
};
static void gimp_brush_cache_constructed (GObject *object);
static void gimp_brush_cache_finalize (GObject *object);
static void gimp_brush_cache_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_brush_cache_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
G_DEFINE_TYPE (GimpBrushCache, gimp_brush_cache, GIMP_TYPE_OBJECT)
#define parent_class gimp_brush_cache_parent_class
static void
gimp_brush_cache_class_init (GimpBrushCacheClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = gimp_brush_cache_constructed;
object_class->finalize = gimp_brush_cache_finalize;
object_class->set_property = gimp_brush_cache_set_property;
object_class->get_property = gimp_brush_cache_get_property;
g_object_class_install_property (object_class, PROP_DATA_DESTROY,
g_param_spec_pointer ("data-destroy",
NULL, NULL,
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
}
static void
gimp_brush_cache_init (GimpBrushCache *brush)
{
}
static void
gimp_brush_cache_constructed (GObject *object)
{
GimpBrushCache *cache = GIMP_BRUSH_CACHE (object);
if (G_OBJECT_CLASS (parent_class)->constructed)
G_OBJECT_CLASS (parent_class)->constructed (object);
g_assert (cache->data_destroy != NULL);
}
static void
gimp_brush_cache_finalize (GObject *object)
{
GimpBrushCache *cache = GIMP_BRUSH_CACHE (object);
if (cache->last_data)
{
cache->data_destroy (cache->last_data);
cache->last_data = NULL;
}
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gimp_brush_cache_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpBrushCache *cache = GIMP_BRUSH_CACHE (object);
switch (property_id)
{
case PROP_DATA_DESTROY:
cache->data_destroy = g_value_get_pointer (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_brush_cache_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpBrushCache *cache = GIMP_BRUSH_CACHE (object);
switch (property_id)
{
case PROP_DATA_DESTROY:
g_value_set_pointer (value, cache->data_destroy);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}