Internalize computed css values to save memory

This commit is contained in:
Alexander Larsson
2012-02-15 13:36:15 +01:00
parent cd189e515e
commit b2e36bb85b
2 changed files with 360 additions and 30 deletions

View File

@@ -20,14 +20,88 @@
#include "config.h"
#include <string.h>
#include <cairo-gobject.h>
#include "gtkcsscomputedvaluesprivate.h"
#include "gtkcssstylepropertyprivate.h"
#include "gtkcsstypesprivate.h"
#include "gtkprivatetypebuiltins.h"
#include "gtkshadowprivate.h"
#include "gtkanimationdescription.h"
#include "gtkcssimageprivate.h"
G_DEFINE_TYPE (GtkCssComputedValues, _gtk_css_computed_values, G_TYPE_OBJECT)
typedef GValue GtkCssValue;
static GHashTable *gtk_css_values;
static guint gtk_css_value_hash (GtkCssValue *css_value);
static gboolean gtk_css_value_equal (GtkCssValue *css_value_a, GtkCssValue *css_value_b);
static GtkCssValue *
gtk_css_value_ref (GtkCssValue *v)
{
v->data[1].v_int++;
return v;
}
static void
gtk_css_value_unref (GtkCssValue *v)
{
if (--v->data[1].v_int == 0)
{
g_hash_table_remove (gtk_css_values, v);
g_value_unset ((GValue *)v);
}
}
static GtkCssValue *
gtk_css_value_dup_value (const GValue *v)
{
GtkCssValue *new;
if (v == NULL || !G_IS_VALUE (v))
return NULL;
new = g_hash_table_lookup (gtk_css_values, v);
if (new)
return gtk_css_value_ref (new);
new = g_new0 (GtkCssValue, 1);
g_value_init ((GValue *)new, G_VALUE_TYPE (v));
g_value_copy (v, (GValue *)new);
new->data[1].v_int = 1;
g_hash_table_insert (gtk_css_values, new, new);
return new;
}
static GtkCssValue *
gtk_css_value_ref_value (GValue *v)
{
if (v == NULL || !G_IS_VALUE (v))
return NULL;
/* Some magic to detect if the GValue is already a GtkCssValue so we can just ref it */
if (v->data[1].v_int > 0)
return gtk_css_value_ref ((GtkCssValue *)v);
return gtk_css_value_dup_value (v);
}
const GValue *
gtk_css_value_peek (GtkCssValue *v)
{
return (const GValue *)v;
}
static void
gtk_css_computed_values_dispose (GObject *object)
{
@@ -35,7 +109,7 @@ gtk_css_computed_values_dispose (GObject *object)
if (values->values)
{
g_array_free (values->values, TRUE);
g_ptr_array_unref (values->values);
values->values = NULL;
}
if (values->sections)
@@ -47,18 +121,282 @@ gtk_css_computed_values_dispose (GObject *object)
G_OBJECT_CLASS (_gtk_css_computed_values_parent_class)->dispose (object);
}
static gboolean
strv_equal (char **a, char **b)
{
int i;
if (a == b)
return TRUE;
if (a == NULL || b == NULL)
return FALSE;
for (i = 0; a[i] != NULL && b[i] != NULL; i++)
{
if (strcmp (a[i], b[i]) != 0)
return FALSE;
}
return a[i] == NULL && b[i] == NULL;
}
static guint
strv_hash (char **v)
{
int i;
guint hash;
if (v == NULL)
return 0;
hash = 0;
for (i = 0; v[i] != NULL; i++)
hash ^= g_str_hash (v[i]);
return hash;
}
static gboolean
gtk_css_value_equal (GtkCssValue *css_value_a, GtkCssValue *css_value_b)
{
GType type;
const GValue *a = gtk_css_value_peek (css_value_a);
const GValue *b = gtk_css_value_peek (css_value_b);
if (a == b)
return TRUE;
if (a == NULL || b == NULL)
return FALSE;
if (a->g_type != b->g_type)
return FALSE;
type = a->g_type;
if (type == G_TYPE_INT || type == G_TYPE_BOOLEAN)
{
return a->data[0].v_int == b->data[0].v_int;
}
else if (type == G_TYPE_DOUBLE)
{
return a->data[0].v_double == b->data[0].v_double;
}
else if (type == G_TYPE_LONG ||
G_TYPE_IS_ENUM (type) ||
G_TYPE_IS_FLAGS (type))
{
return a->data[0].v_long == b->data[0].v_long;
}
else if (type == GDK_TYPE_RGBA)
{
return gdk_rgba_equal (a->data[0].v_pointer,
b->data[0].v_pointer);
}
else if (type == G_TYPE_STRV)
{
return strv_equal (a->data[0].v_pointer,
b->data[0].v_pointer);
}
else if (type == GTK_TYPE_CSS_NUMBER)
{
return _gtk_css_number_equal (a->data[0].v_pointer,
b->data[0].v_pointer);
}
else if (type == GTK_TYPE_CSS_BORDER_IMAGE_REPEAT)
{
GtkCssBorderImageRepeat *aa = a->data[0].v_pointer;
GtkCssBorderImageRepeat *bb = b->data[0].v_pointer;
if (aa == bb)
return TRUE;
if (aa == NULL || bb == NULL)
return FALSE;
return
aa->vrepeat == bb->vrepeat &&
aa->hrepeat == bb->hrepeat;
}
else if (type == GTK_TYPE_CSS_BORDER_CORNER_RADIUS)
{
GtkCssBorderCornerRadius *aa = a->data[0].v_pointer;
GtkCssBorderCornerRadius *bb = b->data[0].v_pointer;
if (aa == bb)
return TRUE;
if (aa == NULL || bb == NULL)
return FALSE;
return
_gtk_css_number_equal (&aa->horizontal, &bb->horizontal) &&
_gtk_css_number_equal (&aa->vertical, &bb->vertical);
}
else if (type == GTK_TYPE_BORDER)
{
GtkBorder *aa = a->data[0].v_pointer;
GtkBorder *bb = b->data[0].v_pointer;
if (aa == bb)
return TRUE;
if (aa == NULL || bb == NULL)
return FALSE;
return
aa->left == bb->left &&
aa->right == bb->right &&
aa->top == bb->top &&
aa->bottom == bb->bottom;
}
else if (type == GTK_TYPE_CSS_BACKGROUND_SIZE)
{
GtkCssBackgroundSize *aa = a->data[0].v_pointer;
GtkCssBackgroundSize *bb = b->data[0].v_pointer;
if (aa == bb)
return TRUE;
if (aa == NULL || bb == NULL)
return FALSE;
return
_gtk_css_number_equal (&aa->width, &bb->width) &&
_gtk_css_number_equal (&aa->height, &bb->height) &&
aa->cover == bb->cover &&
aa->contain == bb->contain;
}
else if (type == GTK_TYPE_SHADOW ||
type == G_TYPE_PTR_ARRAY ||
type == CAIRO_GOBJECT_TYPE_PATTERN ||
type == GTK_TYPE_THEMING_ENGINE ||
type == GTK_TYPE_ANIMATION_DESCRIPTION||
type == GTK_TYPE_CSS_IMAGE)
{
/* These are refcounted, compare by pointer */
return a->data[0].v_pointer == b->data[0].v_pointer;
}
else
{
g_error ("Can't handle CSS type %s\n", g_type_name (type));
}
return FALSE;
}
static guint
gtk_css_value_hash (GtkCssValue *css_value)
{
GType type;
const GValue *v = gtk_css_value_peek (css_value);
if (v == NULL)
return 0;
if (v->g_type == 0)
return 0;
type = v->g_type;
if (type == G_TYPE_INT || type == G_TYPE_BOOLEAN)
{
return (guint)v->data[0].v_int;
}
else if (type == G_TYPE_DOUBLE)
{
return (guint)v->data[0].v_double;
}
else if (type == G_TYPE_LONG ||
G_TYPE_IS_ENUM (type) ||
G_TYPE_IS_FLAGS (type))
{
return (guint)v->data[0].v_long;
}
else if (type == GDK_TYPE_RGBA)
{
return gdk_rgba_hash (v->data[0].v_pointer);
}
else if (type == G_TYPE_STRV)
{
return strv_hash (v->data[0].v_pointer);
}
else if (type == GTK_TYPE_CSS_NUMBER)
{
return _gtk_css_number_hash (v->data[0].v_pointer);
}
else if (type == GTK_TYPE_CSS_BORDER_IMAGE_REPEAT)
{
GtkCssBorderImageRepeat *vv = v->data[0].v_pointer;
if (vv == NULL)
return 0;
return ((guint)vv->vrepeat) ^ ((guint)vv->hrepeat);
}
else if (type == GTK_TYPE_CSS_BORDER_CORNER_RADIUS)
{
GtkCssBorderCornerRadius *vv = v->data[0].v_pointer;
if (vv == NULL)
return 0;
return
_gtk_css_number_hash (&vv->horizontal) ^
_gtk_css_number_hash (&vv->vertical);
}
else if (type == GTK_TYPE_BORDER)
{
GtkBorder *vv = v->data[0].v_pointer;
if (vv == NULL)
return 0;
return
((guint)vv->left) ^
(((guint)vv->right) << 16) ^
((guint)vv->top) ^
(((guint)vv->bottom) << 16);
}
else if (type == GTK_TYPE_CSS_BACKGROUND_SIZE)
{
GtkCssBackgroundSize *vv = v->data[0].v_pointer;
if (vv == NULL)
return 0;
return
_gtk_css_number_hash (&vv->width) ^
_gtk_css_number_hash (&vv->height) ^
(((guint)vv->cover) << 9) ^
(((guint)vv->contain) << 9);
}
else if (type == GTK_TYPE_SHADOW ||
type == G_TYPE_PTR_ARRAY ||
type == CAIRO_GOBJECT_TYPE_PATTERN ||
type == GTK_TYPE_THEMING_ENGINE ||
type == GTK_TYPE_ANIMATION_DESCRIPTION||
type == GTK_TYPE_CSS_IMAGE)
{
/* These are refcounted, compare by pointer */
return GPOINTER_TO_INT (v->data[0].v_pointer);
}
else
{
g_error ("Can't handle CSS type %s\n", g_type_name (type));
}
return FALSE;
}
static void
_gtk_css_computed_values_class_init (GtkCssComputedValuesClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = gtk_css_computed_values_dispose;
gtk_css_values = g_hash_table_new ((GHashFunc)gtk_css_value_hash, (GEqualFunc)gtk_css_value_equal);
}
static void
_gtk_css_computed_values_init (GtkCssComputedValues *computed_values)
{
}
GtkCssComputedValues *
@@ -92,12 +430,9 @@ _gtk_css_computed_values_compute_value (GtkCssComputedValues *values,
parent = gtk_style_context_get_parent (context);
if (values->values == NULL)
{
values->values = g_array_new (FALSE, TRUE, sizeof (GValue));
g_array_set_clear_func (values->values, (GDestroyNotify) g_value_unset);
}
values->values = g_ptr_array_new_with_free_func ((GDestroyNotify)gtk_css_value_unref);
if (id <= values->values->len)
g_array_set_size (values->values, id + 1);
g_ptr_array_set_size (values->values, id + 1);
/* http://www.w3.org/TR/css3-cascade/#cascade
* Then, for every element, the value for each property can be found
@@ -158,22 +493,27 @@ _gtk_css_computed_values_compute_value (GtkCssComputedValues *values,
specified = _gtk_css_style_property_get_initial_value (prop);
}
/* Clear existing value, can't reuse as it may be shared */
if (g_ptr_array_index (values->values, id) != NULL)
gtk_css_value_unref (g_ptr_array_index (values->values, id));
if (specified)
{
GValue value = G_VALUE_INIT;
_gtk_css_style_property_compute_value (prop,
&g_array_index (values->values, GValue, id),
&value,
context,
specified);
g_ptr_array_index (values->values, id) = gtk_css_value_dup_value (&value);
g_value_unset (&value);
}
else
{
const GValue *parent_value;
GValue *value = &g_array_index (values->values, GValue, id);
/* Set NULL here and do the inheritance upon lookup? */
parent_value = _gtk_style_context_peek_property (parent,
_gtk_style_property_get_name (GTK_STYLE_PROPERTY (prop)));
g_value_init (value, G_VALUE_TYPE (parent_value));
g_value_copy (parent_value, value);
g_ptr_array_index (values->values, id) = gtk_css_value_ref_value ((GValue *)parent_value);
}
if (section)
@@ -193,23 +533,19 @@ _gtk_css_computed_values_set_value (GtkCssComputedValues *values,
const GValue *value,
GtkCssSection *section)
{
GValue *set;
g_return_if_fail (GTK_IS_CSS_COMPUTED_VALUES (values));
g_return_if_fail (value == NULL || G_IS_VALUE (value));
if (values->values == NULL)
{
values->values = g_array_new (FALSE, TRUE, sizeof (GValue));
g_array_set_clear_func (values->values, (GDestroyNotify) g_value_unset);
}
values->values = g_ptr_array_new_with_free_func ((GDestroyNotify)gtk_css_value_unref);
if (id <= values->values->len)
g_array_set_size (values->values, id + 1);
g_ptr_array_set_size (values->values, id + 1);
/* Clear existing value, can't reuse as it may be shared */
if (g_ptr_array_index (values->values, id) != NULL)
gtk_css_value_unref (g_ptr_array_index (values->values, id));
set = &g_array_index (values->values, GValue, id);
g_value_init (set, G_VALUE_TYPE (value));
g_value_copy (value, set);
g_ptr_array_index (values->values, id) = gtk_css_value_ref_value ((GValue *)value);
if (section)
{
@@ -226,19 +562,13 @@ const GValue *
_gtk_css_computed_values_get_value (GtkCssComputedValues *values,
guint id)
{
const GValue *v;
g_return_val_if_fail (GTK_IS_CSS_COMPUTED_VALUES (values), NULL);
if (values->values == NULL ||
id >= values->values->len)
return NULL;
v = &g_array_index (values->values, GValue, id);
if (!G_IS_VALUE (v))
return NULL;
return v;
return gtk_css_value_peek (g_ptr_array_index (values->values, id));
}
const GValue *

View File

@@ -42,7 +42,7 @@ struct _GtkCssComputedValues
{
GObject parent;
GArray *values;
GPtrArray *values;
GPtrArray *sections;
};