summaryrefslogtreecommitdiff
path: root/src/theme.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/theme.c')
-rw-r--r--src/theme.c604
1 files changed, 350 insertions, 254 deletions
diff --git a/src/theme.c b/src/theme.c
index c42a336..1629c5c 100644
--- a/src/theme.c
+++ b/src/theme.c
@@ -24,307 +24,403 @@
#include "gradient.h"
#include <string.h>
-#if 0
-/* fill_gradient routine from GNOME background-properties, CVS says
- * Michael Fulbright checked it in, Copyright 1998 Red Hat Inc.
- */
-static void
-fill_gradient (GdkPixbuf *pixbuf,
- const GdkColor *c1,
- const GdkColor *c2,
- int vertical,
- int gradient_width,
- int gradient_height,
- int pixbuf_x,
- int pixbuf_y)
+
+MetaGradientSpec*
+meta_gradient_spec_new (MetaGradientType type)
{
- int i, j;
- int dr, dg, db;
- int gs1;
- int vc = (!vertical || (c1 == c2));
- int w = gdk_pixbuf_get_width (pixbuf);
- int h = gdk_pixbuf_get_height (pixbuf);
- guchar *b, *row;
- guchar *d = gdk_pixbuf_get_pixels (pixbuf);
- int rowstride = gdk_pixbuf_get_rowstride (pixbuf);
-
-#define R1 c1->red
-#define G1 c1->green
-#define B1 c1->blue
-#define R2 c2->red
-#define G2 c2->green
-#define B2 c2->blue
-
- dr = R2 - R1;
- dg = G2 - G1;
- db = B2 - B1;
-
- gs1 = (vertical) ? gradient_height - 1 : gradient_width - 1;
-
- row = g_new (unsigned char, rowstride);
-
- if (vc)
- {
- b = row;
- for (j = 0; j < w; j++)
- {
- *b++ = (R1 + ((j + pixbuf_x) * dr) / gs1) >> 8;
- *b++ = (G1 + ((j + pixbuf_x) * dg) / gs1) >> 8;
- *b++ = (B1 + ((j + pixbuf_x) * db) / gs1) >> 8;
- }
- }
-
- for (i = 0; i < h; i++)
- {
- if (!vc)
- {
- unsigned char cr, cg, cb;
- cr = (R1 + ((i + pixbuf_y) * dr) / gs1) >> 8;
- cg = (G1 + ((i + pixbuf_y) * dg) / gs1) >> 8;
- cb = (B1 + ((i + pixbuf_y) * db) / gs1) >> 8;
- b = row;
- for (j = 0; j < w; j++)
- {
- *b++ = cr;
- *b++ = cg;
- *b++ = cb;
- }
- }
- memcpy (d, row, w * 3);
- d += rowstride;
- }
+ MetaGradientSpec *spec;
+
+ spec = g_new (MetaGradientSpec, 1);
+
+ spec->type = type;
+ spec->color_specs = NULL;
-#undef R1
-#undef G1
-#undef B1
-#undef R2
-#undef G2
-#undef B2
-
- g_free (row);
+ return spec;
}
-#endif
-typedef struct _CachedGradient CachedGradient;
-
-struct _CachedGradient
+void
+meta_gradient_spec_free (MetaGradientSpec *spec)
{
- MetaGradientType type;
- GdkColor color_one;
- GdkColor color_two;
- int width;
- int height;
- GdkPixbuf *pixbuf;
- int access_serial;
-};
-
-static GHashTable *gradient_cache = NULL;
-static int access_counter = 0;
-static int cache_size = 0;
+ g_return_if_fail (spec != NULL);
+
+ g_slist_foreach (spec->color_specs, (GFunc) meta_color_spec_free, NULL);
+ g_slist_free (spec->color_specs);
+ g_free (spec);
+}
-#define GRADIENT_SIZE(g) ((g)->width * (g)->height * 4)
+GdkPixbuf*
+meta_gradient_spec_render (const MetaGradientSpec *spec,
+ GtkWidget *widget,
+ int width,
+ int height)
+{
+ int n_colors;
+ GdkColor *colors;
+ GSList *tmp;
+ int i;
+ GdkPixbuf *pixbuf;
+
+ n_colors = g_slist_length (spec->color_specs);
-#define MAX_CACHE_SIZE (1024 * 128) /* 128k */
+ if (n_colors == 0)
+ return NULL;
+
+ colors = g_new (GdkColor, n_colors);
-static guint
-cached_gradient_hash (gconstpointer value)
-{
- /* I have no idea how to write a hash function. */
- const CachedGradient *gradient = value;
- guint colorone_hash = gdk_color_hash (&gradient->color_one);
- guint colortwo_hash = gdk_color_hash (&gradient->color_two);
- guint hash = (colorone_hash >> 16) | (colortwo_hash << 16);
+ i = 0;
+ tmp = spec->color_specs;
+ while (tmp != NULL)
+ {
+ meta_color_spec_render (tmp->data, widget, &colors[i]);
+
+ tmp = tmp->next;
+ ++i;
+ }
+
+ pixbuf = meta_gradient_create_multi (width, height,
+ colors, n_colors,
+ spec->type);
- hash ^= gradient->width << 22;
- hash ^= gradient->height;
- hash ^= gradient->type << 15;
+ g_free (colors);
- return hash;
+ return pixbuf;
}
-static gboolean
-cached_gradient_equal (gconstpointer value_a,
- gconstpointer value_b)
+MetaColorSpec*
+meta_color_spec_new (MetaColorSpecType type)
{
- const CachedGradient *gradient_a = value_a;
- const CachedGradient *gradient_b = value_b;
+ MetaColorSpec *spec;
+ MetaColorSpec dummy;
+ int size;
+
+ size = G_STRUCT_OFFSET (MetaColorSpec, data);
- return gradient_a->type == gradient_b->type &&
- gradient_a->width == gradient_b->width &&
- gradient_a->height == gradient_b->height &&
- gdk_color_equal (&gradient_a->color_one, &gradient_b->color_one) &&
- gdk_color_equal (&gradient_a->color_two, &gradient_b->color_two);
-}
+ switch (type)
+ {
+ case META_COLOR_SPEC_BASIC:
+ size += sizeof (dummy.data.basic);
+ break;
-static void
-hash_listify (gpointer key, gpointer value, gpointer data)
-{
- GSList **list = data;
+ case META_COLOR_SPEC_GTK:
+ size += sizeof (dummy.data.gtk);
+ break;
+
+ case META_COLOR_SPEC_BLEND:
+ size += sizeof (dummy.data.blend);
+ break;
+ }
+
+ spec = g_malloc0 (size);
- if (key != value)
- meta_bug ("Gradient cache got munged (value was overwritten)\n");
+ spec->type = type;
- *list = g_slist_prepend (*list, value);
+ return spec;
}
-/* sort gradients so that least-recently-used are first */
-static int
-gradient_lru_compare (gconstpointer a,
- gconstpointer b)
+void
+meta_color_spec_free (MetaColorSpec *spec)
{
- const CachedGradient *gradient_a = a;
- const CachedGradient *gradient_b = b;
-
- if (gradient_a->access_serial < gradient_b->access_serial)
- return -1;
- else if (gradient_a->access_serial > gradient_b->access_serial)
- return 1;
- else
- return 0;
+ g_return_if_fail (spec != NULL);
+
+ switch (spec->type)
+ {
+ case META_COLOR_SPEC_BASIC:
+ break;
+
+ case META_COLOR_SPEC_GTK:
+ break;
+
+ case META_COLOR_SPEC_BLEND:
+ if (spec->data.blend.foreground)
+ meta_color_spec_free (spec->data.blend.foreground);
+ if (spec->data.blend.background)
+ meta_color_spec_free (spec->data.blend.background);
+ break;
+ }
+
+ g_free (spec);
}
-static void
-expire_some_old_gradients (void)
+void
+meta_color_spec_render (MetaColorSpec *spec,
+ GtkWidget *widget,
+ GdkColor *color)
{
- GSList *all_gradients;
- GSList *tmp;
+ g_return_if_fail (spec != NULL);
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_return_if_fail (widget->style != NULL);
- all_gradients = NULL;
+ switch (spec->type)
+ {
+ case META_COLOR_SPEC_BASIC:
+ *color = spec->data.basic.color;
+ break;
- g_hash_table_foreach (gradient_cache, hash_listify, &all_gradients);
+ case META_COLOR_SPEC_GTK:
+ switch (spec->data.gtk.component)
+ {
+ case GTK_RC_BG:
+ *color = widget->style->bg[spec->data.gtk.state];
+ break;
+ case GTK_RC_FG:
+ *color = widget->style->fg[spec->data.gtk.state];
+ break;
+ case GTK_RC_BASE:
+ *color = widget->style->base[spec->data.gtk.state];
+ break;
+ case GTK_RC_TEXT:
+ *color = widget->style->text[spec->data.gtk.state];
+ break;
+ }
+ break;
+
+ case META_COLOR_SPEC_BLEND:
+ {
+ GdkColor fg, bg;
+ int alpha;
+
+ meta_color_spec_render (spec->data.blend.foreground, widget, &fg);
+ meta_color_spec_render (spec->data.blend.background, widget, &bg);
+
+ *color = fg;
+ alpha = spec->data.blend.alpha * 0xffff;
+ color->red = color->red + (((bg.red - color->red) * alpha + 0x8000) >> 16);
+ color->green = color->green + (((bg.green - color->green) * alpha + 0x8000) >> 16);
+ color->blue = color->blue + (((bg.blue - color->blue) * alpha + 0x8000) >> 16);
+ }
+ break;
+ }
+}
- all_gradients = g_slist_sort (all_gradients, gradient_lru_compare);
- tmp = all_gradients;
- while (tmp != NULL)
- {
- CachedGradient *gradient = tmp->data;
-
- if (cache_size < MAX_CACHE_SIZE)
- break;
+MetaTextureSpec*
+meta_texture_spec_new (MetaTextureType type)
+{
+ MetaTextureSpec *spec;
+ MetaTextureSpec dummy;
+ int size;
- meta_topic (META_DEBUG_GRADIENT_CACHE,
- " Removing gradient of size %d from cache of size %d\n",
- GRADIENT_SIZE (gradient), cache_size);
-
- cache_size -= GRADIENT_SIZE (gradient);
+ size = G_STRUCT_OFFSET (MetaTextureSpec, data);
+
+ switch (type)
+ {
+ case META_TEXTURE_SOLID:
+ size += sizeof (dummy.data.solid);
+ break;
- g_hash_table_remove (gradient_cache, gradient);
+ case META_TEXTURE_GRADIENT:
+ size += sizeof (dummy.data.gradient);
+ break;
- g_object_unref (G_OBJECT (gradient->pixbuf));
- g_free (gradient);
-
- tmp = tmp->next;
+ case META_TEXTURE_IMAGE:
+ size += sizeof (dummy.data.image);
+ break;
}
+
+ spec = g_malloc0 (size);
- g_slist_free (all_gradients);
-
- meta_topic (META_DEBUG_GRADIENT_CACHE,
- "Cache reduced to size %d bytes %d gradients after expiring old gradients\n",
- cache_size, g_hash_table_size (gradient_cache));
+ spec->type = type;
+
+ return spec;
}
-GdkPixbuf*
-meta_theme_get_gradient (MetaGradientType type,
- const GdkColor *color_one,
- const GdkColor *color_two,
- int width,
- int height)
+void
+meta_texture_spec_free (MetaTextureSpec *spec)
{
- CachedGradient gradient;
- CachedGradient *cached;
- GdkPixbuf *retval;
+ g_return_if_fail (spec != NULL);
- meta_topic (META_DEBUG_GRADIENT_CACHE,
- "Requesting %s gradient one %d/%d/%d two %d/%d/%d "
- "%d x %d\n",
- type == META_GRADIENT_VERTICAL ? "vertical" : "horizontal",
- color_one->red / 256, color_one->green / 256, color_one->blue / 256,
- color_two->red / 256, color_two->green / 256, color_two->blue / 256,
- width, height);
-
- if (gradient_cache == NULL)
+ switch (spec->type)
{
- gradient_cache = g_hash_table_new (cached_gradient_hash,
- cached_gradient_equal);
+ case META_TEXTURE_SOLID:
+ if (spec->data.solid.color_spec)
+ meta_color_spec_free (spec->data.solid.color_spec);
+ break;
+
+ case META_TEXTURE_GRADIENT:
+ if (spec->data.gradient.gradient_spec)
+ meta_gradient_spec_free (spec->data.gradient.gradient_spec);
+ break;
+
+ case META_TEXTURE_IMAGE:
+ if (spec->data.image.pixbuf)
+ g_object_unref (G_OBJECT (spec->data.image.pixbuf));
+ break;
}
- gradient.type = type;
- gradient.color_one = *color_one;
- gradient.color_two = *color_two;
- gradient.width = width;
- gradient.height = height;
- gradient.pixbuf = NULL;
- gradient.access_serial = access_counter;
+ g_free (spec);
+}
+
+static void
+render_pixbuf (GdkDrawable *drawable,
+ const GdkRectangle *clip,
+ GdkPixbuf *pixbuf,
+ int x,
+ int y)
+{
+ /* grumble, render_to_drawable_alpha does not accept a clip
+ * mask, so we have to go through some BS
+ */
+ GdkRectangle pixbuf_rect;
+ GdkRectangle draw_rect;
- cached = g_hash_table_lookup (gradient_cache, &gradient);
+ pixbuf_rect.x = x;
+ pixbuf_rect.y = y;
+ pixbuf_rect.width = gdk_pixbuf_get_width (pixbuf);
+ pixbuf_rect.height = gdk_pixbuf_get_height (pixbuf);
- if (cached)
+ if (clip)
{
- meta_topic (META_DEBUG_GRADIENT_CACHE,
- "Found gradient in cache\n");
- ++access_counter;
- cached->access_serial = access_counter;
- g_object_ref (G_OBJECT (cached->pixbuf));
- return cached->pixbuf;
+ if (!gdk_rectangle_intersect ((GdkRectangle*)clip,
+ &pixbuf_rect, &draw_rect))
+ return;
}
-
-#if 0
- gradient.pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8,
- gradient.width, gradient.height);
-
- fill_gradient (gradient.pixbuf,
- &gradient.color_one,
- &gradient.color_two,
- type == META_GRADIENT_VERTICAL ? TRUE : FALSE,
- gradient.width,
- gradient.height,
- 0, 0);
-#else
- gradient.pixbuf = meta_gradient_create_simple (gradient.width,
- gradient.height,
- &gradient.color_one,
- &gradient.color_two,
- type);
-
- if (gradient.pixbuf == NULL)
+ else
{
- meta_topic (META_DEBUG_GRADIENT_CACHE,
- "Not enough memory to create gradient of size %d bytes\n",
- GRADIENT_SIZE (&gradient));
- return NULL;
+ draw_rect = pixbuf_rect;
}
-#endif
-
- if (GRADIENT_SIZE (&gradient) > MAX_CACHE_SIZE)
- {
- cached = g_new (CachedGradient, 1);
- *cached = gradient;
-
- g_hash_table_insert (gradient_cache, cached, cached);
-
- meta_topic (META_DEBUG_GRADIENT_CACHE,
- "Caching newly-created gradient, size is %d bytes, total cache size %d bytes %d gradients, maximum %d bytes\n",
- GRADIENT_SIZE (cached),
- cache_size, g_hash_table_size (gradient_cache), MAX_CACHE_SIZE);
- cache_size += GRADIENT_SIZE (cached);
-
- g_object_ref (G_OBJECT (cached->pixbuf)); /* to return to caller */
- retval = cached->pixbuf;
+ gdk_pixbuf_render_to_drawable_alpha (pixbuf,
+ drawable,
+ draw_rect.x - pixbuf_rect.x,
+ draw_rect.y - pixbuf_rect.y,
+ draw_rect.x, draw_rect.y,
+ draw_rect.width,
+ draw_rect.height,
+ GDK_PIXBUF_ALPHA_FULL, /* ignored */
+ 128, /* ignored */
+ GDK_RGB_DITHER_NORMAL,
+ draw_rect.x - pixbuf_rect.x,
+ draw_rect.y - pixbuf_rect.y);
+}
- if (cache_size > MAX_CACHE_SIZE)
- expire_some_old_gradients (); /* may unref "cached->pixbuf" and free "cached" */
- }
- else
- {
- meta_topic (META_DEBUG_GRADIENT_CACHE,
- "Gradient of size %d bytes is too large to cache\n",
- GRADIENT_SIZE (&gradient));
- retval = gradient.pixbuf;
- }
+void
+meta_texture_spec_draw (const MetaTextureSpec *spec,
+ GtkWidget *widget,
+ GdkDrawable *drawable,
+ const GdkRectangle *clip,
+ MetaTextureDrawMode mode,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ g_return_if_fail (spec != NULL);
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_return_if_fail (GDK_IS_DRAWABLE (drawable));
+ g_return_if_fail (widget->style != NULL);
- return retval;
+ switch (spec->type)
+ {
+ case META_TEXTURE_SOLID:
+ {
+ GdkGC *gc;
+ GdkGCValues values;
+
+ g_return_if_fail (spec->data.solid.color_spec != NULL);
+
+ meta_color_spec_render (spec->data.solid.color_spec,
+ widget,
+ &values.foreground);
+
+ gdk_rgb_find_color (widget->style->colormap, &values.foreground);
+ gc = gdk_gc_new_with_values (drawable, &values, GDK_GC_FOREGROUND);
+
+ gdk_draw_rectangle (drawable,
+ gc, TRUE, x, y, width, height);
+
+ g_object_unref (G_OBJECT (gc));
+ }
+ break;
+
+ case META_TEXTURE_GRADIENT:
+ {
+ GdkPixbuf *pixbuf;
+
+ g_return_if_fail (spec->data.gradient.gradient_spec != NULL);
+
+ pixbuf = meta_gradient_spec_render (spec->data.gradient.gradient_spec,
+ widget, width, height);
+
+ if (pixbuf == NULL)
+ return;
+
+ render_pixbuf (drawable, clip, pixbuf, x, y);
+
+ g_object_unref (G_OBJECT (pixbuf));
+ }
+ break;
+
+ case META_TEXTURE_IMAGE:
+ {
+ GdkPixbuf *pixbuf;
+
+ g_return_if_fail (spec->data.image.pixbuf != NULL);
+
+ pixbuf = NULL;
+
+ switch (mode)
+ {
+ case META_TEXTURE_DRAW_UNSCALED:
+ pixbuf = spec->data.image.pixbuf;
+ g_object_ref (G_OBJECT (pixbuf));
+ break;
+ case META_TEXTURE_DRAW_SCALED_VERTICALLY:
+ pixbuf = spec->data.image.pixbuf;
+ if (gdk_pixbuf_get_height (pixbuf) == height)
+ {
+ g_object_ref (G_OBJECT (pixbuf));
+ }
+ else
+ {
+ pixbuf = gdk_pixbuf_scale_simple (pixbuf,
+ gdk_pixbuf_get_width (pixbuf),
+ height,
+ GDK_INTERP_BILINEAR);
+ if (pixbuf == NULL)
+ return;
+ }
+ break;
+ case META_TEXTURE_DRAW_SCALED_HORIZONTALLY:
+ pixbuf = spec->data.image.pixbuf;
+ if (gdk_pixbuf_get_width (pixbuf) == width)
+ {
+ g_object_ref (G_OBJECT (pixbuf));
+ }
+ else
+ {
+ pixbuf = gdk_pixbuf_scale_simple (pixbuf,
+ width,
+ gdk_pixbuf_get_height (pixbuf),
+ GDK_INTERP_BILINEAR);
+ if (pixbuf == NULL)
+ return;
+ }
+ break;
+ case META_TEXTURE_DRAW_SCALED_BOTH:
+ pixbuf = spec->data.image.pixbuf;
+ if (gdk_pixbuf_get_width (pixbuf) == width &&
+ gdk_pixbuf_get_height (pixbuf) == height)
+ {
+ g_object_ref (G_OBJECT (pixbuf));
+ }
+ else
+ {
+ pixbuf = gdk_pixbuf_scale_simple (pixbuf,
+ width, height,
+ GDK_INTERP_BILINEAR);
+ if (pixbuf == NULL)
+ return;
+ }
+ break;
+ }
+
+ g_return_if_fail (pixbuf != NULL);
+
+ render_pixbuf (drawable, clip, pixbuf, x, y);
+
+ g_object_unref (G_OBJECT (pixbuf));
+ }
+ break;
+ }
}
-