2002-01-06 04:51:53 +00:00
|
|
|
/* Metacity Theme Rendering */
|
2001-05-31 16:18:40 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (C) 2001 Havoc Pennington
|
|
|
|
*
|
|
|
|
* 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 2 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, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
|
|
* 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "theme.h"
|
2001-06-03 21:39:57 +00:00
|
|
|
#include "util.h"
|
2002-01-06 17:52:21 +00:00
|
|
|
#include "gradient.h"
|
2002-01-06 04:51:53 +00:00
|
|
|
#include <string.h>
|
2001-05-31 16:18:40 +00:00
|
|
|
|
2002-01-06 17:52:21 +00:00
|
|
|
#if 0
|
2002-01-06 04:51:53 +00:00
|
|
|
/* fill_gradient routine from GNOME background-properties, CVS says
|
|
|
|
* Michael Fulbright checked it in, Copyright 1998 Red Hat Inc.
|
|
|
|
*/
|
2001-06-03 21:39:57 +00:00
|
|
|
static void
|
2002-01-06 04:51:53 +00:00
|
|
|
fill_gradient (GdkPixbuf *pixbuf,
|
|
|
|
const GdkColor *c1,
|
|
|
|
const GdkColor *c2,
|
|
|
|
int vertical,
|
|
|
|
int gradient_width,
|
|
|
|
int gradient_height,
|
|
|
|
int pixbuf_x,
|
|
|
|
int pixbuf_y)
|
2001-06-03 21:39:57 +00:00
|
|
|
{
|
2002-01-06 04:51:53 +00:00
|
|
|
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)
|
2001-06-03 21:39:57 +00:00
|
|
|
{
|
2002-01-06 04:51:53 +00:00
|
|
|
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;
|
|
|
|
}
|
2001-06-03 21:39:57 +00:00
|
|
|
}
|
|
|
|
|
2002-01-06 04:51:53 +00:00
|
|
|
for (i = 0; i < h; i++)
|
2001-06-03 21:39:57 +00:00
|
|
|
{
|
2002-01-06 04:51:53 +00:00
|
|
|
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;
|
2001-06-03 21:39:57 +00:00
|
|
|
}
|
2002-01-06 04:51:53 +00:00
|
|
|
|
|
|
|
#undef R1
|
|
|
|
#undef G1
|
|
|
|
#undef B1
|
|
|
|
#undef R2
|
|
|
|
#undef G2
|
|
|
|
#undef B2
|
|
|
|
|
|
|
|
g_free (row);
|
|
|
|
}
|
2002-01-06 17:52:21 +00:00
|
|
|
#endif
|
2001-06-03 21:39:57 +00:00
|
|
|
|
2002-01-06 04:51:53 +00:00
|
|
|
typedef struct _CachedGradient CachedGradient;
|
2001-06-03 21:39:57 +00:00
|
|
|
|
2002-01-06 04:51:53 +00:00
|
|
|
struct _CachedGradient
|
|
|
|
{
|
|
|
|
MetaGradientType type;
|
|
|
|
GdkColor color_one;
|
|
|
|
GdkColor color_two;
|
|
|
|
int width;
|
|
|
|
int height;
|
|
|
|
GdkPixbuf *pixbuf;
|
|
|
|
int access_serial;
|
|
|
|
};
|
2001-06-03 21:39:57 +00:00
|
|
|
|
2002-01-06 04:51:53 +00:00
|
|
|
static GHashTable *gradient_cache = NULL;
|
|
|
|
static int access_counter = 0;
|
|
|
|
static int cache_size = 0;
|
2001-06-03 21:39:57 +00:00
|
|
|
|
2002-01-06 04:51:53 +00:00
|
|
|
#define GRADIENT_SIZE(g) ((g)->width * (g)->height * 4)
|
2001-06-03 21:39:57 +00:00
|
|
|
|
2002-01-06 04:51:53 +00:00
|
|
|
#define MAX_CACHE_SIZE (1024 * 128) /* 128k */
|
2001-06-03 21:39:57 +00:00
|
|
|
|
2002-01-06 04:51:53 +00:00
|
|
|
static guint
|
|
|
|
cached_gradient_hash (gconstpointer value)
|
2001-05-31 16:18:40 +00:00
|
|
|
{
|
2002-01-06 04:51:53 +00:00
|
|
|
/* 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);
|
2001-06-03 21:39:57 +00:00
|
|
|
|
2002-01-06 04:51:53 +00:00
|
|
|
hash ^= gradient->width << 22;
|
|
|
|
hash ^= gradient->height;
|
|
|
|
hash ^= gradient->type << 15;
|
2001-06-03 21:39:57 +00:00
|
|
|
|
2002-01-06 04:51:53 +00:00
|
|
|
return hash;
|
2001-06-03 18:33:59 +00:00
|
|
|
}
|
|
|
|
|
2002-01-06 04:51:53 +00:00
|
|
|
static gboolean
|
|
|
|
cached_gradient_equal (gconstpointer value_a,
|
|
|
|
gconstpointer value_b)
|
2001-06-03 18:33:59 +00:00
|
|
|
{
|
2002-01-06 04:51:53 +00:00
|
|
|
const CachedGradient *gradient_a = value_a;
|
|
|
|
const CachedGradient *gradient_b = value_b;
|
2001-06-01 03:00:01 +00:00
|
|
|
|
2002-01-06 04:51:53 +00:00
|
|
|
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);
|
2001-05-31 16:18:40 +00:00
|
|
|
}
|
|
|
|
|
2001-06-03 21:39:57 +00:00
|
|
|
static void
|
2002-01-06 04:51:53 +00:00
|
|
|
hash_listify (gpointer key, gpointer value, gpointer data)
|
2001-06-03 21:39:57 +00:00
|
|
|
{
|
2002-01-06 04:51:53 +00:00
|
|
|
GSList **list = data;
|
2001-06-03 21:39:57 +00:00
|
|
|
|
2002-01-06 04:51:53 +00:00
|
|
|
if (key != value)
|
|
|
|
meta_bug ("Gradient cache got munged (value was overwritten)\n");
|
2001-06-03 21:39:57 +00:00
|
|
|
|
2002-01-06 04:51:53 +00:00
|
|
|
*list = g_slist_prepend (*list, value);
|
|
|
|
}
|
2001-06-03 21:39:57 +00:00
|
|
|
|
2002-01-06 04:51:53 +00:00
|
|
|
/* sort gradients so that least-recently-used are first */
|
|
|
|
static int
|
|
|
|
gradient_lru_compare (gconstpointer a,
|
|
|
|
gconstpointer b)
|
|
|
|
{
|
|
|
|
const CachedGradient *gradient_a = a;
|
|
|
|
const CachedGradient *gradient_b = b;
|
2001-06-03 21:39:57 +00:00
|
|
|
|
2002-01-06 04:51:53 +00:00
|
|
|
if (gradient_a->access_serial < gradient_b->access_serial)
|
|
|
|
return -1;
|
|
|
|
else if (gradient_a->access_serial > gradient_b->access_serial)
|
|
|
|
return 1;
|
2001-06-03 21:39:57 +00:00
|
|
|
else
|
2002-01-06 04:51:53 +00:00
|
|
|
return 0;
|
2001-06-03 21:39:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2002-01-06 04:51:53 +00:00
|
|
|
expire_some_old_gradients (void)
|
2001-06-03 21:39:57 +00:00
|
|
|
{
|
2002-01-06 04:51:53 +00:00
|
|
|
GSList *all_gradients;
|
|
|
|
GSList *tmp;
|
|
|
|
|
|
|
|
all_gradients = NULL;
|
2001-06-03 21:39:57 +00:00
|
|
|
|
2002-01-06 04:51:53 +00:00
|
|
|
g_hash_table_foreach (gradient_cache, hash_listify, &all_gradients);
|
2001-06-03 21:39:57 +00:00
|
|
|
|
2002-01-06 04:51:53 +00:00
|
|
|
all_gradients = g_slist_sort (all_gradients, gradient_lru_compare);
|
2001-06-03 21:39:57 +00:00
|
|
|
|
2002-01-06 04:51:53 +00:00
|
|
|
tmp = all_gradients;
|
|
|
|
while (tmp != NULL)
|
2001-06-06 04:47:37 +00:00
|
|
|
{
|
2002-01-06 04:51:53 +00:00
|
|
|
CachedGradient *gradient = tmp->data;
|
|
|
|
|
|
|
|
if (cache_size < MAX_CACHE_SIZE)
|
|
|
|
break;
|
2001-06-06 04:47:37 +00:00
|
|
|
|
2002-01-06 04:51:53 +00:00
|
|
|
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);
|
2001-06-06 04:47:37 +00:00
|
|
|
|
2002-01-06 04:51:53 +00:00
|
|
|
g_hash_table_remove (gradient_cache, gradient);
|
2001-06-06 04:47:37 +00:00
|
|
|
|
2002-01-06 04:51:53 +00:00
|
|
|
g_object_unref (G_OBJECT (gradient->pixbuf));
|
|
|
|
g_free (gradient);
|
|
|
|
|
|
|
|
tmp = tmp->next;
|
|
|
|
}
|
2001-06-06 04:47:37 +00:00
|
|
|
|
2002-01-06 04:51:53 +00:00
|
|
|
g_slist_free (all_gradients);
|
2001-06-06 04:47:37 +00:00
|
|
|
|
2002-01-06 04:51:53 +00:00
|
|
|
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));
|
2001-06-06 04:47:37 +00:00
|
|
|
}
|
|
|
|
|
2002-01-06 04:51:53 +00:00
|
|
|
GdkPixbuf*
|
|
|
|
meta_theme_get_gradient (MetaGradientType type,
|
|
|
|
const GdkColor *color_one,
|
|
|
|
const GdkColor *color_two,
|
|
|
|
int width,
|
|
|
|
int height)
|
2001-05-31 16:18:40 +00:00
|
|
|
{
|
2002-01-06 04:51:53 +00:00
|
|
|
CachedGradient gradient;
|
|
|
|
CachedGradient *cached;
|
|
|
|
GdkPixbuf *retval;
|
2001-06-01 03:00:01 +00:00
|
|
|
|
2002-01-06 04:51:53 +00:00
|
|
|
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",
|
2002-01-06 17:52:21 +00:00
|
|
|
color_one->red / 256, color_one->green / 256, color_one->blue / 256,
|
|
|
|
color_two->red / 256, color_two->green / 256, color_two->blue / 256,
|
2002-01-06 04:51:53 +00:00
|
|
|
width, height);
|
2001-06-01 03:00:01 +00:00
|
|
|
|
2002-01-06 04:51:53 +00:00
|
|
|
if (gradient_cache == NULL)
|
2001-06-04 04:58:22 +00:00
|
|
|
{
|
2002-01-06 04:51:53 +00:00
|
|
|
gradient_cache = g_hash_table_new (cached_gradient_hash,
|
|
|
|
cached_gradient_equal);
|
2001-06-04 04:58:22 +00:00
|
|
|
}
|
2001-06-06 04:47:37 +00:00
|
|
|
|
2002-01-06 04:51:53 +00:00
|
|
|
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;
|
2001-06-04 04:58:22 +00:00
|
|
|
|
2002-01-06 04:51:53 +00:00
|
|
|
cached = g_hash_table_lookup (gradient_cache, &gradient);
|
2001-06-03 21:39:57 +00:00
|
|
|
|
2002-01-06 04:51:53 +00:00
|
|
|
if (cached)
|
2001-06-06 04:47:37 +00:00
|
|
|
{
|
2002-01-06 04:51:53 +00:00
|
|
|
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;
|
2001-06-03 21:39:57 +00:00
|
|
|
}
|
|
|
|
|
2002-01-06 17:52:21 +00:00
|
|
|
#if 0
|
2002-01-06 04:51:53 +00:00
|
|
|
gradient.pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8,
|
|
|
|
gradient.width, gradient.height);
|
2001-06-03 21:39:57 +00:00
|
|
|
|
2002-01-06 04:51:53 +00:00
|
|
|
fill_gradient (gradient.pixbuf,
|
|
|
|
&gradient.color_one,
|
|
|
|
&gradient.color_two,
|
|
|
|
type == META_GRADIENT_VERTICAL ? TRUE : FALSE,
|
|
|
|
gradient.width,
|
|
|
|
gradient.height,
|
|
|
|
0, 0);
|
2002-01-06 17:52:21 +00:00
|
|
|
#else
|
|
|
|
gradient.pixbuf = meta_gradient_create_simple (gradient.width,
|
|
|
|
gradient.height,
|
|
|
|
&gradient.color_one,
|
|
|
|
&gradient.color_two,
|
|
|
|
type);
|
|
|
|
|
|
|
|
if (gradient.pixbuf == NULL)
|
|
|
|
{
|
|
|
|
meta_topic (META_DEBUG_GRADIENT_CACHE,
|
|
|
|
"Not enough memory to create gradient of size %d bytes\n",
|
|
|
|
GRADIENT_SIZE (&gradient));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#endif
|
2001-06-02 04:14:18 +00:00
|
|
|
|
2002-01-06 17:52:21 +00:00
|
|
|
if (GRADIENT_SIZE (&gradient) > MAX_CACHE_SIZE)
|
|
|
|
{
|
|
|
|
cached = g_new (CachedGradient, 1);
|
|
|
|
*cached = gradient;
|
|
|
|
|
|
|
|
g_hash_table_insert (gradient_cache, cached, cached);
|
2001-05-31 16:18:40 +00:00
|
|
|
|
2002-01-06 17:52:21 +00:00
|
|
|
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);
|
2001-06-03 21:39:57 +00:00
|
|
|
|
2002-01-06 17:52:21 +00:00
|
|
|
cache_size += GRADIENT_SIZE (cached);
|
|
|
|
|
|
|
|
g_object_ref (G_OBJECT (cached->pixbuf)); /* to return to caller */
|
|
|
|
retval = cached->pixbuf;
|
2001-06-06 04:47:37 +00:00
|
|
|
|
2002-01-06 17:52:21 +00:00
|
|
|
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;
|
|
|
|
}
|
2001-06-02 04:14:18 +00:00
|
|
|
|
2002-01-06 04:51:53 +00:00
|
|
|
return retval;
|
2001-05-31 16:18:40 +00:00
|
|
|
}
|
|
|
|
|