diff --git a/cogl/Makefile.am b/cogl/Makefile.am
index 4a8f92fa6..f6a8caa22 100644
--- a/cogl/Makefile.am
+++ b/cogl/Makefile.am
@@ -137,6 +137,8 @@ cogl_sources_c = \
$(srcdir)/cogl-matrix-mesa.c \
$(srcdir)/cogl-profile.h \
$(srcdir)/cogl-profile.c \
+ $(srcdir)/cogl-bitmask.h \
+ $(srcdir)/cogl-bitmask.c \
$(NULL)
# glib-mkenums rules
diff --git a/cogl/cogl-bitmask.c b/cogl/cogl-bitmask.c
new file mode 100644
index 000000000..f2ec22c51
--- /dev/null
+++ b/cogl/cogl-bitmask.c
@@ -0,0 +1,257 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2010 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see .
+ *
+ *
+ *
+ * Authors:
+ * Neil Roberts
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include
+#include
+
+#include "cogl-bitmask.h"
+
+gboolean
+_cogl_bitmask_get_from_array (const CoglBitmask *bitmask,
+ unsigned int bit_num)
+{
+ GArray *array = (GArray *) *bitmask;
+
+ /* If the index is off the end of the array then assume the bit is
+ not set */
+ if (bit_num >= sizeof (unsigned int) * 8 * array->len)
+ return FALSE;
+ else
+ return !!(g_array_index (array, unsigned int,
+ bit_num / (sizeof (unsigned int) * 8)) &
+ (1 << (bit_num % (sizeof (unsigned int) * 8))));
+}
+
+static void
+_cogl_bitmask_convert_to_array (CoglBitmask *bitmask)
+{
+ GArray *array;
+ /* Fetch the old values, ignoring the least significant bit */
+ unsigned int old_values = GPOINTER_TO_UINT (*bitmask) >> 1;
+
+ array = g_array_new (FALSE, TRUE, sizeof (unsigned int));
+ /* Copy the old values back in */
+ g_array_append_val (array, old_values);
+
+ *bitmask = (struct _CoglBitmaskImaginaryType *) array;
+}
+
+void
+_cogl_bitmask_set_in_array (CoglBitmask *bitmask,
+ unsigned int bit_num,
+ gboolean value)
+{
+ GArray *array;
+ unsigned int array_index, new_value_mask;
+
+ /* If the bitmask is not already an array then we need to allocate one */
+ if (!_cogl_bitmask_has_array (bitmask))
+ _cogl_bitmask_convert_to_array (bitmask);
+
+ array = (GArray *) *bitmask;
+
+ array_index = bit_num / (sizeof (unsigned int) * 8);
+ /* Grow the array if necessary. This will clear the new data */
+ if (array_index >= array->len)
+ g_array_set_size (array, array_index + 1);
+
+ new_value_mask = 1 << (bit_num % (sizeof (unsigned int) * 8));
+
+ if (value)
+ g_array_index (array, unsigned int, array_index) |= new_value_mask;
+ else
+ g_array_index (array, unsigned int, array_index) &= ~new_value_mask;
+}
+
+void
+_cogl_bitmask_set_bits (CoglBitmask *dst,
+ const CoglBitmask *src)
+{
+ if (_cogl_bitmask_has_array (src))
+ {
+ GArray *src_array, *dst_array;
+ int i;
+
+ if (!_cogl_bitmask_has_array (dst))
+ _cogl_bitmask_convert_to_array (dst);
+
+ dst_array = (GArray *) *dst;
+ src_array = (GArray *) *src;
+
+ if (dst_array->len < src_array->len)
+ g_array_set_size (dst_array, src_array->len);
+
+ for (i = 0; i < src_array->len; i++)
+ g_array_index (dst_array, unsigned int, i) |=
+ g_array_index (src_array, unsigned int, i);
+ }
+ else if (_cogl_bitmask_has_array (dst))
+ {
+ GArray *dst_array;
+
+ dst_array = (GArray *) *dst;
+
+ g_array_index (dst_array, unsigned int, 0) |=
+ (GPOINTER_TO_UINT (*src) >> 1);
+ }
+ else
+ *dst = GUINT_TO_POINTER (GPOINTER_TO_UINT (*dst) |
+ GPOINTER_TO_UINT (*src));
+}
+
+void
+_cogl_bitmask_set_range_in_array (CoglBitmask *bitmask,
+ unsigned int n_bits,
+ gboolean value)
+{
+ GArray *array;
+ unsigned int array_index, bit_index;
+
+ if (n_bits == 0)
+ return;
+
+ /* If the bitmask is not already an array then we need to allocate one */
+ if (!_cogl_bitmask_has_array (bitmask))
+ _cogl_bitmask_convert_to_array (bitmask);
+
+ array = (GArray *) *bitmask;
+
+ /* Get the array index of the top most value that will be touched */
+ array_index = (n_bits - 1) / (sizeof (unsigned int) * 8);
+ /* Get the bit index of the top most value */
+ bit_index = (n_bits - 1) % (sizeof (unsigned int) * 8);
+ /* Grow the array if necessary. This will clear the new data */
+ if (array_index >= array->len)
+ g_array_set_size (array, array_index + 1);
+
+ if (value)
+ {
+ /* Set the bits that are touching this index */
+ g_array_index (array, unsigned int, array_index) |=
+ ~(unsigned int) 0 >> (sizeof (unsigned int) * 8 - 1 - bit_index);
+
+ /* Set all of the bits in any lesser indices */
+ memset (array->data, 0xff, sizeof (unsigned int) * array_index);
+ }
+ else
+ {
+ /* Clear the bits that are touching this index */
+ g_array_index (array, unsigned int, array_index) &=
+ ~(unsigned int) 1 << bit_index;
+
+ /* Clear all of the bits in any lesser indices */
+ memset (array->data, 0x00, sizeof (unsigned int) * array_index);
+ }
+}
+
+void
+_cogl_bitmask_clear_bits (CoglBitmask *dst,
+ const CoglBitmask *src)
+{
+ if (_cogl_bitmask_has_array (src))
+ {
+ GArray *src_array, *dst_array;
+ int i;
+
+ if (!_cogl_bitmask_has_array (dst))
+ _cogl_bitmask_convert_to_array (dst);
+
+ dst_array = (GArray *) *dst;
+ src_array = (GArray *) *src;
+
+ if (dst_array->len < src_array->len)
+ g_array_set_size (dst_array, src_array->len);
+
+ for (i = 0; i < src_array->len; i++)
+ g_array_index (dst_array, unsigned int, i) &=
+ ~g_array_index (src_array, unsigned int, i);
+ }
+ else if (_cogl_bitmask_has_array (dst))
+ {
+ GArray *dst_array;
+
+ dst_array = (GArray *) *dst;
+
+ g_array_index (dst_array, unsigned int, 0) &=
+ ~(GPOINTER_TO_UINT (*src) >> 1);
+ }
+ else
+ *dst = GUINT_TO_POINTER ((GPOINTER_TO_UINT (*dst) &
+ ~GPOINTER_TO_UINT (*src)) | 1);
+}
+
+void
+_cogl_bitmask_clear_all_in_array (CoglBitmask *bitmask)
+{
+ GArray *array = (GArray *) *bitmask;
+
+ memset (array->data, 0, sizeof (unsigned int) * array->len);
+}
+
+void
+_cogl_bitmask_foreach (const CoglBitmask *bitmask,
+ CoglBitmaskForeachFunc func,
+ gpointer user_data)
+{
+ if (_cogl_bitmask_has_array (bitmask))
+ {
+ GArray *array = (GArray *) *bitmask;
+ int array_index;
+
+ for (array_index = 0; array_index < array->len; array_index++)
+ {
+ unsigned int mask = g_array_index (array, unsigned int, array_index);
+ int bit = 0;
+
+ while (mask)
+ {
+ if (mask & 1)
+ func (array_index * sizeof (unsigned int) * 8 + bit, user_data);
+
+ bit++;
+ mask >>= 1;
+ }
+ }
+ }
+ else
+ {
+ unsigned int mask = GPOINTER_TO_UINT (*bitmask) >> 1;
+ int bit = 0;
+
+ while (mask)
+ {
+ if (mask & 1)
+ func (bit, user_data);
+
+ bit++;
+ mask >>= 1;
+ }
+ }
+}
diff --git a/cogl/cogl-bitmask.h b/cogl/cogl-bitmask.h
new file mode 100644
index 000000000..00e387261
--- /dev/null
+++ b/cogl/cogl-bitmask.h
@@ -0,0 +1,223 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2010 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ * Neil Roberts
+ */
+
+#ifndef __COGL_BITMASK_H
+#define __COGL_BITMASK_H
+
+#include
+
+G_BEGIN_DECLS
+
+/*
+ * CoglBitmask implements a growable array of bits. A CoglBitmask can
+ * be allocated on the stack but it must be initialised with
+ * _cogl_bitmask_init() before use and then destroyed with
+ * _cogl_bitmask_destroy(). A CoglBitmask will try to avoid allocating
+ * any memory unless more than 31 bits are needed.
+ *
+ * Internally a CoglBitmask is a pointer. If the least significant bit
+ * of the pointer is 1 then the rest of the bits are directly used as
+ * part of the bitmask, otherwise it is a pointer to a GArray of
+ * unsigned ints. This relies on the fact the g_malloc will return a
+ * pointer aligned to at least two bytes (so that the least
+ * significant bit of the address is always 0)
+ */
+
+typedef struct _CoglBitmaskImaginaryType *CoglBitmask;
+
+/* Internal helper macro to determine whether this bitmask has a
+ GArray allocated or whether the pointer is just used directly */
+#define _cogl_bitmask_has_array(bitmask) \
+ (!(GPOINTER_TO_UINT (*bitmask) & 1))
+
+/* Number of bits we can use before needing to allocate an array */
+#define COGL_BITMASK_MAX_DIRECT_BITS (sizeof (unsigned int) * 8 - 1)
+
+/*
+ * _cogl_bitmask_init:
+ * @bitmask: A pointer to a bitmask
+ *
+ * Initialises the cogl bitmask. This must be called before any other
+ * bitmask functions are called. Initially all of the values are
+ * zero
+ */
+/* Set the last significant bit to mark that no array has been
+ allocated yet */
+#define _cogl_bitmask_init(bitmask) \
+ G_STMT_START { *(bitmask) = GUINT_TO_POINTER (1); } G_STMT_END
+
+gboolean
+_cogl_bitmask_get_from_array (const CoglBitmask *bitmask,
+ unsigned int bit_num);
+
+void
+_cogl_bitmask_set_in_array (CoglBitmask *bitmask,
+ unsigned int bit_num,
+ gboolean value);
+
+void
+_cogl_bitmask_set_range_in_array (CoglBitmask *bitmask,
+ unsigned int n_bits,
+ gboolean value);
+
+void
+_cogl_bitmask_clear_all_in_array (CoglBitmask *bitmask);
+
+/*
+ * cogl_bitmask_set_bits:
+ * @dst: The bitmask to modify
+ * @src: The bitmask to copy bits from
+ *
+ * This makes sure that all of the bits that are set in @src are also
+ * set in @dst. Any unset bits in @src are left alone in @dst.
+ */
+void
+_cogl_bitmask_set_bits (CoglBitmask *dst,
+ const CoglBitmask *src);
+
+/*
+ * cogl_bitmask_clear_bits:
+ * @dst: The bitmask to modify
+ * @src: The bitmask to copy bits from
+ *
+ * This makes sure that all of the bits that are set in @src are
+ * cleared in @dst. Any unset bits in @src are left alone in @dst.
+ */
+void
+_cogl_bitmask_clear_bits (CoglBitmask *dst,
+ const CoglBitmask *src);
+
+typedef void (* CoglBitmaskForeachFunc) (int bit_num, gpointer user_data);
+
+/*
+ * cogl_bitmask_foreach:
+ * @bitmask: A pointer to a bitmask
+ * @func: A callback function
+ * @user_data: A pointer to pass to the callback
+ *
+ * This calls @func for each bit that is set in @bitmask.
+ */
+void
+_cogl_bitmask_foreach (const CoglBitmask *bitmask,
+ CoglBitmaskForeachFunc func,
+ gpointer user_data);
+
+/*
+ * _cogl_bitmask_get:
+ * @bitmask: A pointer to a bitmask
+ * @bit_num: A bit number
+ *
+ * Return value: whether bit number @bit_num is set in @bitmask
+ */
+static inline gboolean
+_cogl_bitmask_get (const CoglBitmask *bitmask, unsigned int bit_num)
+{
+ if (_cogl_bitmask_has_array (bitmask))
+ return _cogl_bitmask_get_from_array (bitmask, bit_num);
+ else if (bit_num >= COGL_BITMASK_MAX_DIRECT_BITS)
+ return FALSE;
+ else
+ return !!(GPOINTER_TO_UINT (*bitmask) & (1 << (bit_num + 1)));
+}
+
+/*
+ * _cogl_bitmask_set:
+ * @bitmask: A pointer to a bitmask
+ * @bit_num: A bit number
+ * @value: The new value
+ *
+ * Sets or resets a bit number @bit_num in @bitmask according to @value.
+ */
+static inline void
+_cogl_bitmask_set (CoglBitmask *bitmask, unsigned int bit_num, gboolean value)
+{
+ if (_cogl_bitmask_has_array (bitmask) ||
+ bit_num >= COGL_BITMASK_MAX_DIRECT_BITS)
+ _cogl_bitmask_set_in_array (bitmask, bit_num, value);
+ else if (value)
+ *bitmask = GUINT_TO_POINTER (GPOINTER_TO_UINT (*bitmask) |
+ (1 << (bit_num + 1)));
+ else
+ *bitmask = GUINT_TO_POINTER (GPOINTER_TO_UINT (*bitmask) &
+ ~(1 << (bit_num + 1)));
+}
+
+/*
+ * _cogl_bitmask_set_range:
+ * @bitmask: A pointer to a bitmask
+ * @n_bits: The number of bits to set
+ * @value: The value to set
+ *
+ * Sets the first @n_bits in @bitmask to @value.
+ */
+static inline void
+_cogl_bitmask_set_range (CoglBitmask *bitmask,
+ unsigned int n_bits,
+ gboolean value)
+{
+ if (_cogl_bitmask_has_array (bitmask) ||
+ n_bits > COGL_BITMASK_MAX_DIRECT_BITS)
+ _cogl_bitmask_set_range_in_array (bitmask, n_bits, value);
+ else if (value)
+ *bitmask = GUINT_TO_POINTER (GPOINTER_TO_UINT (*bitmask) |
+ ~(~(unsigned int) 1 << n_bits));
+ else
+ *bitmask = GUINT_TO_POINTER (GPOINTER_TO_UINT (*bitmask) &
+ ((~(unsigned int) 1 << n_bits) | 1));
+}
+
+/*
+ * _cogl_bitmask_destroy:
+ * @bitmask: A pointer to a bitmask
+ *
+ * Destroys any resources allocated by the bitmask
+ */
+static inline void
+_cogl_bitmask_destroy (CoglBitmask *bitmask)
+{
+ if (_cogl_bitmask_has_array (bitmask))
+ g_array_free ((GArray *) *bitmask, TRUE);
+}
+
+/*
+ * _cogl_bitmask_clear_all:
+ * @bitmask: A pointer to a bitmask
+ *
+ * Clears all the bits in a bitmask without destroying any resources.
+ */
+static inline void
+_cogl_bitmask_clear_all (CoglBitmask *bitmask)
+{
+ if (_cogl_bitmask_has_array (bitmask))
+ _cogl_bitmask_clear_all_in_array (bitmask);
+ else
+ *bitmask = GUINT_TO_POINTER (1);
+}
+
+G_END_DECLS
+
+#endif /* __COGL_BITMASK_H */
+