summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am2
-rw-r--r--openbox/client.c266
-rw-r--r--openbox/client.h20
-rw-r--r--openbox/client_list_combined_menu.c10
-rw-r--r--openbox/client_list_menu.c9
-rw-r--r--openbox/focus_cycle_popup.c29
-rw-r--r--openbox/framerender.c20
-rw-r--r--openbox/menu.c1
-rw-r--r--openbox/menu.h8
-rw-r--r--openbox/menuframe.c22
-rw-r--r--openbox/openbox.c23
-rw-r--r--openbox/openbox.h1
-rw-r--r--openbox/popup.c15
-rw-r--r--openbox/popup.h2
-rw-r--r--render/image.c317
-rw-r--r--render/image.h9
-rw-r--r--render/imagecache.c149
-rw-r--r--render/imagecache.h37
-rw-r--r--render/render.c49
-rw-r--r--render/render.h64
-rw-r--r--render/theme.c3
-rw-r--r--render/theme.h4
-rw-r--r--tests/Makefile2
-rw-r--r--tests/icons.c65
24 files changed, 834 insertions, 293 deletions
diff --git a/Makefile.am b/Makefile.am
index d8dba2c7..dd5bb6c5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -86,6 +86,8 @@ render_libobrender_la_SOURCES = \
render/icon.h \
render/image.h \
render/image.c \
+ render/imagecache.h \
+ render/imagecache.c \
render/instance.h \
render/instance.c \
render/mask.h \
diff --git a/openbox/client.c b/openbox/client.c
index e65ed07c..c7a82d16 100644
--- a/openbox/client.c
+++ b/openbox/client.c
@@ -67,9 +67,10 @@ typedef struct
gpointer data;
} ClientCallback;
-GList *client_list = NULL;
+GList *client_list = NULL;
-static GSList *client_destroy_notifies = NULL;
+static GSList *client_destroy_notifies = NULL;
+static RrImage *client_default_icon = NULL;
static void client_get_all(ObClient *self, gboolean real);
static void client_get_startup_id(ObClient *self);
@@ -109,6 +110,19 @@ static void client_prompt_kill(ObClient *self);
void client_startup(gboolean reconfig)
{
+ if ((client_default_icon = RrImageCacheFind(ob_rr_icons,
+ ob_rr_theme->def_win_icon,
+ ob_rr_theme->def_win_icon_w,
+ ob_rr_theme->def_win_icon_h)))
+ RrImageRef(client_default_icon);
+ else {
+ client_default_icon = RrImageNew(ob_rr_icons);
+ RrImageAddPicture(client_default_icon,
+ ob_rr_theme->def_win_icon,
+ ob_rr_theme->def_win_icon_w,
+ ob_rr_theme->def_win_icon_h);
+ }
+
if (reconfig) return;
client_set_list();
@@ -116,6 +130,9 @@ void client_startup(gboolean reconfig)
void client_shutdown(gboolean reconfig)
{
+ RrImageUnref(client_default_icon);
+ client_default_icon = NULL;
+
if (reconfig) return;
}
@@ -587,7 +604,6 @@ void client_unmanage_all(void)
void client_unmanage(ObClient *self)
{
- guint j;
GSList *it;
gulong ignore_start;
@@ -719,11 +735,8 @@ void client_unmanage(ObClient *self)
ob_debug("Unmanaged window 0x%lx", self->window);
/* free all data allocated in the client struct */
+ RrImageUnref(self->icon_set);
g_slist_free(self->transients);
- for (j = 0; j < self->nicons; ++j)
- g_free(self->icons[j].data);
- if (self->nicons > 0)
- g_free(self->icons);
g_free(self->startup_id);
g_free(self->wm_command);
g_free(self->title);
@@ -2009,143 +2022,134 @@ void client_update_strut(ObClient *self)
}
}
-/* Avoid storing icons above this size if possible */
-#define AVOID_ABOVE 64
-
void client_update_icons(ObClient *self)
{
guint num;
guint32 *data;
guint w, h, i, j;
guint num_seen; /* number of icons present */
- guint num_small_seen; /* number of icons small enough present */
- guint smallest, smallest_area;
+ RrImage *img;
+
+ img = NULL;
- for (i = 0; i < self->nicons; ++i)
- g_free(self->icons[i].data);
- if (self->nicons > 0)
- g_free(self->icons);
- self->nicons = 0;
+ /* grab the server, because we might be setting the window's icon and
+ we don't want them to set it in between and we overwrite their own
+ icon */
+ grab_server(TRUE);
if (OBT_PROP_GETA32(self->window, NET_WM_ICON, CARDINAL, &data, &num)) {
/* figure out how many valid icons are in here */
i = 0;
- num_seen = num_small_seen = 0;
- smallest = smallest_area = 0;
- if (num > 2)
- while (i < num) {
+ num_seen = 0;
+ while (i + 2 < num) { /* +2 is to make sure there is a w and h */
+ w = data[i++];
+ h = data[i++];
+ /* watch for the data being too small for the specified size,
+ or for zero sized icons. */
+ if (i + w*h > num || w == 0 || h == 0) break;
+
+ /* convert it to the right bit order for ObRender */
+ for (j = 0; j < w*h; ++j)
+ data[i+j] =
+ (((data[i+j] >> 24) & 0xff) << RrDefaultAlphaOffset) +
+ (((data[i+j] >> 16) & 0xff) << RrDefaultRedOffset) +
+ (((data[i+j] >> 8) & 0xff) << RrDefaultGreenOffset) +
+ (((data[i+j] >> 0) & 0xff) << RrDefaultBlueOffset);
+
+ /* is it in the cache? */
+ img = RrImageCacheFind(ob_rr_icons, &data[i], w, h);
+ if (img) RrImageRef(img); /* own it */
+
+ i += w*h;
+ ++num_seen;
+
+ /* don't bother looping anymore if we already found it in the cache
+ since we'll just use that! */
+ if (img) break;
+ }
+
+ /* if it's not in the cache yet, then add it to the cache now.
+ we have already converted it to the correct bit order above */
+ if (!img && num_seen > 0) {
+ img = RrImageNew(ob_rr_icons);
+ i = 0;
+ for (j = 0; j < num_seen; ++j) {
w = data[i++];
h = data[i++];
- i += w * h;
- /* watch for it being too small for the specified size, or for
- zero sized icons. */
- if (i > num || w == 0 || h == 0) break;
-
- if (!smallest_area || w*h < smallest_area) {
- smallest = num_seen;
- smallest_area = w*h;
- }
- ++num_seen;
- if (w <= AVOID_ABOVE && h <= AVOID_ABOVE)
- ++num_small_seen;
- }
- if (num_small_seen > 0)
- self->nicons = num_small_seen;
- else if (num_seen)
- self->nicons = 1;
-
- self->icons = g_new(ObClientIcon, self->nicons);
-
- /* store the icons */
- i = 0;
- for (j = 0; j < self->nicons;) {
- guint x, y, t;
-
- w = self->icons[j].width = data[i++];
- h = self->icons[j].height = data[i++];
-
- /* if there are some icons smaller than the threshold, we're
- skipping all the ones above */
- if (num_small_seen > 0) {
- if (w > AVOID_ABOVE || h > AVOID_ABOVE) {
- i += w*h;
- continue;
- }
- }
- /* if there were no icons smaller than the threshold, then we are
- only taking the smallest available one we saw */
- else if (j != smallest) {
+ RrImageAddPicture(img, &data[i], w, h);
i += w*h;
- continue;
}
-
- self->icons[j].data = g_new(RrPixel32, w * h);
- for (x = 0, y = 0, t = 0; t < w * h; ++t, ++x, ++i) {
- if (x >= w) {
- x = 0;
- ++y;
- }
- self->icons[j].data[t] =
- (((data[i] >> 24) & 0xff) << RrDefaultAlphaOffset) +
- (((data[i] >> 16) & 0xff) << RrDefaultRedOffset) +
- (((data[i] >> 8) & 0xff) << RrDefaultGreenOffset) +
- (((data[i] >> 0) & 0xff) << RrDefaultBlueOffset);
- }
- g_assert(i <= num);
-
- ++j;
}
g_free(data);
- } else {
+ }
+
+ /* if we didn't find an image from the NET_WM_ICON stuff, then try the
+ legacy X hints */
+ if (!img) {
XWMHints *hints;
if ((hints = XGetWMHints(obt_display, self->window))) {
if (hints->flags & IconPixmapHint) {
- self->nicons = 1;
- self->icons = g_new(ObClientIcon, self->nicons);
+ gboolean xicon;
obt_display_ignore_errors(TRUE);
- if (!RrPixmapToRGBA(ob_rr_inst,
- hints->icon_pixmap,
- (hints->flags & IconMaskHint ?
- hints->icon_mask : None),
- &self->icons[0].width,
- &self->icons[0].height,
- &self->icons[0].data))
- {
- g_free(self->icons);
- self->nicons = 0;
- }
+ xicon = RrPixmapToRGBA(ob_rr_inst,
+ hints->icon_pixmap,
+ (hints->flags & IconMaskHint ?
+ hints->icon_mask : None),
+ (gint*)&w, (gint*)&h, &data);
obt_display_ignore_errors(FALSE);
+
+
+ if (xicon) {
+ if (w > 0 && h > 0) {
+ /* is this icon in the cache yet? */
+ img = RrImageCacheFind(ob_rr_icons, data, w, h);
+ if (img) RrImageRef(img); /* own it */
+
+ /* if not, then add it */
+ if (!img) {
+ img = RrImageNew(ob_rr_icons);
+ RrImageAddPicture(img, data, w, h);
+ }
+ }
+
+ g_free(data);
+ }
}
XFree(hints);
}
}
- /* set the default icon onto the window
- in theory, this could be a race, but if a window doesn't set an icon
- or removes it entirely, it's not very likely it is going to set one
- right away afterwards
+ /* set the client's icons to be whatever we found */
+ RrImageUnref(self->icon_set);
+ self->icon_set = img;
- if it has parents, then one of them will have an icon already
+ /* if the client has no icon at all, then we set a default icon onto it.
+ but, if it has parents, then one of them will have an icon already
*/
- if (self->nicons == 0 && !self->parents) {
+ if (!self->icon_set && !self->parents) {
RrPixel32 *icon = ob_rr_theme->def_win_icon;
- gulong *data;
-
- data = g_new(gulong, 48*48+2);
- data[0] = data[1] = 48;
- for (i = 0; i < 48*48; ++i)
- data[i+2] = (((icon[i] >> RrDefaultAlphaOffset) & 0xff) << 24) +
+ gulong *ldata; /* use a long here to satisfy OBT_PROP_SETA32 */
+
+ w = ob_rr_theme->def_win_icon_w;
+ h = ob_rr_theme->def_win_icon_h;
+ ldata = g_new(gulong, w*h+2);
+ ldata[0] = w;
+ ldata[1] = h;
+ for (i = 0; i < w*h; ++i)
+ ldata[i+2] = (((icon[i] >> RrDefaultAlphaOffset) & 0xff) << 24) +
(((icon[i] >> RrDefaultRedOffset) & 0xff) << 16) +
(((icon[i] >> RrDefaultGreenOffset) & 0xff) << 8) +
(((icon[i] >> RrDefaultBlueOffset) & 0xff) << 0);
- OBT_PROP_SETA32(self->window, NET_WM_ICON, CARDINAL, data, 48*48+2);
- g_free(data);
+ OBT_PROP_SETA32(self->window, NET_WM_ICON, CARDINAL, ldata, w*h+2);
+ g_free(ldata);
} else if (self->frame)
/* don't draw the icon empty if we're just setting one now anyways,
we'll get the property change any second */
frame_adjust_icon(self->frame);
+
+ grab_server(FALSE);
}
void client_update_icon_geometry(ObClient *self)
@@ -3840,53 +3844,21 @@ gboolean client_focused(ObClient *self)
return self == focus_client;
}
-static ObClientIcon* client_icon_recursive(ObClient *self, gint w, gint h)
-{
- guint i;
- gulong min_diff, min_i;
- if (!self->nicons) {
- ObClientIcon *parent = NULL;
- GSList *it;
- for (it = self->parents; it; it = g_slist_next(it)) {
- ObClient *c = it->data;
- if ((parent = client_icon_recursive(c, w, h)))
- break;
- }
-
- return parent;
- }
-
- /* some kind of crappy approximation to find the icon closest in size to
- what we requested, but icons are generally all the same ratio as
- eachother so it's good enough. */
-
- min_diff = ABS(self->icons[0].width - w) + ABS(self->icons[0].height - h);
- min_i = 0;
-
- for (i = 1; i < self->nicons; ++i) {
- gulong diff;
-
- diff = ABS(self->icons[i].width - w) + ABS(self->icons[i].height - h);
- if (diff < min_diff) {
- min_diff = diff;
- min_i = i;
- }
- }
- return &self->icons[min_i];
-}
-
-const ObClientIcon* client_icon(ObClient *self, gint w, gint h)
+RrImage* client_icon(ObClient *self)
{
- ObClientIcon *ret;
- static ObClientIcon deficon;
+ RrImage *ret = NULL;
- if (!(ret = client_icon_recursive(self, w, h))) {
- deficon.width = deficon.height = 48;
- deficon.data = ob_rr_theme->def_win_icon;
- ret = &deficon;
+ if (self->icon_set)
+ ret = self->icon_set;
+ else if (self->parents) {
+ GSList *it;
+ for (it = self->parents; it && !ret; it = g_slist_next(it))
+ ret = client_icon(it->data);
}
+ if (!ret)
+ ret = client_default_icon;
return ret;
}
diff --git a/openbox/client.h b/openbox/client.h
index bb63137f..c24fcb20 100644
--- a/openbox/client.h
+++ b/openbox/client.h
@@ -40,15 +40,6 @@ struct _ObSessionState;
struct _ObPrompt;
typedef struct _ObClient ObClient;
-typedef struct _ObClientIcon ObClientIcon;
-
-/*! Holds an icon in ARGB format */
-struct _ObClientIcon
-{
- gint width;
- gint height;
- RrPixel32 *data;
-};
/*! Possible window types */
typedef enum
@@ -307,10 +298,8 @@ struct _ObClient
*/
guint functions;
- /*! Icons for the client as specified on the client window */
- ObClientIcon *icons;
- /*! The number of icons in icons */
- guint nicons;
+ /* The window's icon, in a variety of shapes and sizes */
+ RrImage *icon_set;
/*! Where the window should iconify to/from */
Rect icon_geometry;
@@ -642,7 +631,10 @@ void client_setup_decor_and_functions(ObClient *self, gboolean reconfig);
/*! Sets the window's type and transient flag */
void client_get_type_and_transientness(ObClient *self);
-const ObClientIcon *client_icon(ObClient *self, gint w, gint h);
+/*! Returns a client's icon set, or its parents (recursively) if it doesn't
+ have one
+*/
+RrImage* client_icon(ObClient *self);
/*! Return TRUE if the client is transient for some other window. Return
FALSE if it's not transient or there is no window for it to be
diff --git a/openbox/client_list_combined_menu.c b/openbox/client_list_combined_menu.c
index 76a819fc..a04d07d6 100644
--- a/openbox/client_list_combined_menu.c
+++ b/openbox/client_list_combined_menu.c
@@ -56,8 +56,6 @@ static gboolean self_update(ObMenuFrame *frame, gpointer data)
if (client_normal(c) && (!c->skip_taskbar || c->iconic) &&
(c->desktop == desktop || c->desktop == DESKTOP_ALL))
{
- const ObClientIcon *icon;
-
empty = FALSE;
if (c->iconic) {
@@ -69,11 +67,9 @@ static gboolean self_update(ObMenuFrame *frame, gpointer data)
e = menu_add_normal(menu, desktop, c->title, NULL, FALSE);
}
- if (config_menu_client_list_icons
- && (icon = client_icon(c, 32, 32))) {
- e->data.normal.icon_width = icon->width;
- e->data.normal.icon_height = icon->height;
- e->data.normal.icon_data = icon->data;
+ if (config_menu_client_list_icons) {
+ e->data.normal.icon = client_icon(c);
+ RrImageRef(e->data.normal.icon);
e->data.normal.icon_alpha =
c->iconic ? OB_ICONIC_ALPHA : 0xff;
}
diff --git a/openbox/client_list_menu.c b/openbox/client_list_menu.c
index e6521a0a..4ec6e785 100644
--- a/openbox/client_list_menu.c
+++ b/openbox/client_list_menu.c
@@ -58,7 +58,6 @@ static gboolean desk_menu_update(ObMenuFrame *frame, gpointer data)
(c->desktop == d->desktop || c->desktop == DESKTOP_ALL))
{
ObMenuEntry *e;
- const ObClientIcon *icon;
empty = FALSE;
@@ -71,11 +70,9 @@ static gboolean desk_menu_update(ObMenuFrame *frame, gpointer data)
e = menu_add_normal(menu, d->desktop, c->title, NULL, FALSE);
}
- if (config_menu_client_list_icons
- && (icon = client_icon(c, 32, 32))) {
- e->data.normal.icon_width = icon->width;
- e->data.normal.icon_height = icon->height;
- e->data.normal.icon_data = icon->data;
+ if (config_menu_client_list_icons) {
+ e->data.normal.icon = client_icon(c);
+ RrImageRef(e->data.normal.icon);
e->data.normal.icon_alpha = c->iconic ? OB_ICONIC_ALPHA : 0xff;
}
diff --git a/openbox/focus_cycle_popup.c b/openbox/focus_cycle_popup.c
index 4d1dfaa3..3feac6ab 100644
--- a/openbox/focus_cycle_popup.c
+++ b/openbox/focus_cycle_popup.c
@@ -54,6 +54,7 @@ typedef struct _ObFocusCyclePopupTarget ObFocusCyclePopupTarget;
struct _ObFocusCyclePopupTarget
{
ObClient *client;
+ RrImage *icon;
gchar *text;
Window iconwin;
/* This is used when the popup is in list mode */
@@ -139,8 +140,10 @@ void focus_cycle_popup_startup(gboolean reconfig)
may or may not be used */
RrAppearanceAddTextures(popup.a_icon, 2);
- popup.a_icon->texture[0].type = RR_TEXTURE_RGBA;
+ RrAppearanceClearTextures(popup.a_icon);
+ popup.a_icon->texture[0].type = RR_TEXTURE_IMAGE;
+ RrAppearanceClearTextures(popup.a_arrow);
popup.a_arrow->texture[0].type = RR_TEXTURE_MASK;
popup.a_arrow->texture[0].data.mask.color =
ob_rr_theme->osd_color;
@@ -212,9 +215,11 @@ void focus_cycle_popup_shutdown(gboolean reconfig)
while(popup.targets) {
ObFocusCyclePopupTarget *t = popup.targets->data;
+ RrImageUnref(t->icon);
g_free(t->text);
XDestroyWindow(obt_display, t->iconwin);
XDestroyWindow(obt_display, t->textwin);
+ g_free(t);
popup.targets = g_list_delete_link(popup.targets, popup.targets);
}
@@ -272,6 +277,8 @@ static void popup_setup(ObFocusCyclePopup *p, gboolean create_targets,
t->client = ft;
t->text = text;
+ t->icon = client_icon(t->client);
+ RrImageRef(t->icon); /* own the icon so it won't go away */
t->iconwin = create_window(p->bg, 0, 0, NULL);
t->textwin = create_window(p->bg, 0, 0, NULL);
@@ -547,7 +554,6 @@ static void popup_render(ObFocusCyclePopup *p, const ObClient *c)
/* row and column start from 0 */
const gint row = i / icons_per_row - p->scroll;
const gint col = i % icons_per_row;
- const ObClientIcon *icon;
gint iconx, icony;
gint list_mode_textx, list_mode_texty;
RrAppearance *text;
@@ -590,16 +596,13 @@ static void popup_render(ObFocusCyclePopup *p, const ObClient *c)
}
/* get the icon from the client */
- icon = client_icon(target->client, ICON_SIZE, ICON_SIZE);
- p->a_icon->texture[0].data.rgba.width = icon->width;
- p->a_icon->texture[0].data.rgba.height = icon->height;
- p->a_icon->texture[0].data.rgba.twidth = ICON_SIZE;
- p->a_icon->texture[0].data.rgba.theight = ICON_SIZE;
- p->a_icon->texture[0].data.rgba.tx = HILITE_OFFSET;
- p->a_icon->texture[0].data.rgba.ty = HILITE_OFFSET;
- p->a_icon->texture[0].data.rgba.alpha =
+ p->a_icon->texture[0].data.image.twidth = ICON_SIZE;
+ p->a_icon->texture[0].data.image.theight = ICON_SIZE;
+ p->a_icon->texture[0].data.image.tx = HILITE_OFFSET;
+ p->a_icon->texture[0].data.image.ty = HILITE_OFFSET;
+ p->a_icon->texture[0].data.image.alpha =
target->client->iconic ? OB_ICONIC_ALPHA : 0xff;
- p->a_icon->texture[0].data.rgba.data = icon->data;
+ p->a_icon->texture[0].data.image.image = target->icon;
/* Draw the hilite? */
p->a_icon->texture[1].type = (target == newtarget) ?
@@ -685,6 +688,7 @@ void focus_cycle_popup_hide(void)
while(popup.targets) {
ObFocusCyclePopupTarget *t = popup.targets->data;
+ RrImageUnref(t->icon);
g_free(t->text);
XDestroyWindow(obt_display, t->iconwin);
XDestroyWindow(obt_display, t->textwin);
@@ -726,8 +730,7 @@ void focus_cycle_popup_single_show(struct _ObClient *c,
}
text = popup_get_name(c);
- icon_popup_show(single_popup, text, client_icon(c, HILITE_SIZE,
- HILITE_SIZE));
+ icon_popup_show(single_popup, text, client_icon(c));
g_free(text);
screen_hide_desktop_popup();
}
diff --git a/openbox/framerender.c b/openbox/framerender.c
index 87706b2b..bf71d2c3 100644
--- a/openbox/framerender.c
+++ b/openbox/framerender.c
@@ -354,21 +354,21 @@ static void framerender_label(ObFrame *self, RrAppearance *a)
static void framerender_icon(ObFrame *self, RrAppearance *a)
{
- const ObClientIcon *icon;
+ RrImage *icon;
if (!self->icon_on) return;
- icon = client_icon(self->client,
- ob_rr_theme->button_size + 2,
- ob_rr_theme->button_size + 2);
+ icon = client_icon(self->client);
+
if (icon) {
- a->texture[0].type = RR_TEXTURE_RGBA;
- a->texture[0].data.rgba.width = icon->width;
- a->texture[0].data.rgba.height = icon->height;
- a->texture[0].data.rgba.alpha = 0xff;
- a->texture[0].data.rgba.data = icon->data;
- } else
+ RrAppearanceClearTextures(a);
+ a->texture[0].type = RR_TEXTURE_IMAGE;
+ a->texture[0].data.image.alpha = 0xff;
+ a->texture[0].data.image.image = icon;
+ } else {
+ RrAppearanceClearTextures(a);
a->texture[0].type = RR_TEXTURE_NONE;
+ }
RrPaint(a, self->icon,
ob_rr_theme->button_size + 2, ob_rr_theme->button_size + 2);
diff --git a/openbox/menu.c b/openbox/menu.c
index 768630bd..63b74b95 100644
--- a/openbox/menu.c
+++ b/openbox/menu.c
@@ -500,6 +500,7 @@ void menu_entry_unref(ObMenuEntry *self)
if (self && --self->ref == 0) {
switch (self->type) {
case OB_MENU_ENTRY_TYPE_NORMAL:
+ RrImageUnref(self->data.normal.icon);
g_free(self->data.normal.label);
while (self->data.normal.actions) {
actions_act_unref(self->data.normal.actions->data);
diff --git a/openbox/menu.h b/openbox/menu.h
index 752a6771..c488f67d 100644
--- a/openbox/menu.h
+++ b/openbox/menu.h
@@ -115,11 +115,9 @@ struct _ObNormalMenuEntry {
/* List of ObActions */
GSList *actions;
- /* Icon shit */
- gint icon_width;
- gint icon_height;
- gint icon_alpha;
- RrPixel32 *icon_data;
+ /* Icon stuff. If you set this, make sure you RrImageRef() it too. */
+ RrImage *icon;
+ gint icon_alpha;
/* Mask icon */
RrPixmapMask *mask;
diff --git a/openbox/menuframe.c b/openbox/menuframe.c
index 598bc005..c25e485f 100644
--- a/openbox/menuframe.c
+++ b/openbox/menuframe.c
@@ -338,6 +338,7 @@ static void menu_entry_frame_render(ObMenuEntryFrame *self)
default:
g_assert_not_reached();
}
+
RECT_SET_SIZE(self->area, self->frame->inner_w, th);
XResizeWindow(obt_display, self->window,
self->area.width, self->area.height);
@@ -442,6 +443,7 @@ static void menu_entry_frame_render(ObMenuEntryFrame *self)
self->area.width - 2*PADDING, SEPARATOR_HEIGHT);
clear = ob_rr_theme->a_clear_tex;
+ RrAppearanceClearTextures(clear);
clear->texture[0].type = RR_TEXTURE_LINE_ART;
clear->surface.parent = item_a;
clear->surface.parentx = PADDING;
@@ -459,7 +461,7 @@ static void menu_entry_frame_render(ObMenuEntryFrame *self)
}
if (self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
- self->entry->data.normal.icon_data)
+ self->entry->data.normal.icon)
{
RrAppearance *clear;
@@ -471,15 +473,12 @@ static void menu_entry_frame_render(ObMenuEntryFrame *self)
- frame->item_margin.bottom);
clear = ob_rr_theme->a_clear_tex;
- clear->texture[0].type = RR_TEXTURE_RGBA;
- clear->texture[0].data.rgba.width =
- self->entry->data.normal.icon_width;
- clear->texture[0].data.rgba.height =
- self->entry->data.normal.icon_height;
- clear->texture[0].data.rgba.alpha =
+ RrAppearanceClearTextures(clear);
+ clear->texture[0].type = RR_TEXTURE_IMAGE;
+ clear->texture[0].data.image.image =
+ self->entry->data.normal.icon;
+ clear->texture[0].data.image.alpha =
self->entry->data.normal.icon_alpha;
- clear->texture[0].data.rgba.data =
- self->entry->data.normal.icon_data;
clear->surface.parent = item_a;
clear->surface.parentx = PADDING;
clear->surface.parenty = frame->item_margin.top;
@@ -503,6 +502,7 @@ static void menu_entry_frame_render(ObMenuEntryFrame *self)
- frame->item_margin.bottom);
clear = ob_rr_theme->a_clear_tex;
+ RrAppearanceClearTextures(clear);
clear->texture[0].type = RR_TEXTURE_MASK;
clear->texture[0].data.mask.mask =
self->entry->data.normal.mask;
@@ -694,7 +694,7 @@ void menu_frame_render(ObMenuFrame *self)
tw = MIN(tw, MAX_MENU_WIDTH);
th = ob_rr_theme->menu_font_height;
- if (e->entry->data.normal.icon_data ||
+ if (e->entry->data.normal.icon ||
e->entry->data.normal.mask)
has_icon = TRUE;
break;
@@ -705,7 +705,7 @@ void menu_frame_render(ObMenuFrame *self)
tw = MIN(tw, MAX_MENU_WIDTH);
th = ob_rr_theme->menu_font_height;
- if (e->entry->data.normal.icon_data ||
+ if (e->entry->data.normal.icon ||
e->entry->data.normal.mask)
has_icon = TRUE;
diff --git a/openbox/openbox.c b/openbox/openbox.c
index a6a81cef..ec6cc44c 100644
--- a/openbox/openbox.c
+++ b/openbox/openbox.c
@@ -83,16 +83,17 @@
#include <X11/Xlib.h>
#include <X11/keysym.h>
-RrInstance *ob_rr_inst;
-RrTheme *ob_rr_theme;
-ObtMainLoop *ob_main_loop;
-gint ob_screen;
-gboolean ob_replace_wm = FALSE;
-gboolean ob_sm_use = TRUE;
-gchar *ob_sm_id = NULL;
-gchar *ob_sm_save_file = NULL;
-gboolean ob_sm_restore = TRUE;
-gboolean ob_debug_xinerama = FALSE;
+RrInstance *ob_rr_inst;
+RrImageCache *ob_rr_icons;
+RrTheme *ob_rr_theme;
+ObtMainLoop *ob_main_loop;
+gint ob_screen;
+gboolean ob_replace_wm = FALSE;
+gboolean ob_sm_use = TRUE;
+gchar *ob_sm_id = NULL;
+gchar *ob_sm_save_file = NULL;
+gboolean ob_sm_restore = TRUE;
+gboolean ob_debug_xinerama = FALSE;
static ObState state;
static gboolean xsync = FALSE;
@@ -167,6 +168,7 @@ gint main(gint argc, gchar **argv)
ob_rr_inst = RrInstanceNew(obt_display, ob_screen);
if (ob_rr_inst == NULL)
ob_exit_with_error(_("Failed to initialize the obrender library."));
+ ob_rr_icons = RrImageCacheNew();
XSynchronize(obt_display, xsync);
@@ -373,6 +375,7 @@ gint main(gint argc, gchar **argv)
XSync(obt_display, FALSE);
RrThemeFree(ob_rr_theme);
+ RrImageCacheUnref(ob_rr_icons);
RrInstanceFree(ob_rr_inst);
session_shutdown(being_replaced);
diff --git a/openbox/openbox.h b/openbox/openbox.h
index 47d92042..471aa779 100644
--- a/openbox/openbox.h
+++ b/openbox/openbox.h
@@ -30,6 +30,7 @@
#include <X11/Xlib.h>
extern RrInstance *ob_rr_inst;
+extern RrImageCache *ob_rr_icons;
extern RrTheme *ob_rr_theme;
extern ObtMainLoop *ob_main_loop;
diff --git a/openbox/popup.c b/openbox/popup.c
index 02c87848..fd31846e 100644
--- a/openbox/popup.c
+++ b/openbox/popup.c
@@ -366,16 +366,17 @@ void icon_popup_free(ObIconPopup *self)
}
void icon_popup_delay_show(ObIconPopup *self, gulong usec,
- gchar *text, const ObClientIcon *icon)
+ gchar *text, RrImage *icon)
{
if (icon) {
- self->a_icon->texture[0].type = RR_TEXTURE_RGBA;
- self->a_icon->texture[0].data.rgba.width = icon->width;
- self->a_icon->texture[0].data.rgba.height = icon->height;
- self->a_icon->texture[0].data.rgba.alpha = 0xff;
- self->a_icon->texture[0].data.rgba.data = icon->data;
- } else
+ RrAppearanceClearTextures(self->a_icon);
+ self->a_icon->texture[0].type = RR_TEXTURE_IMAGE;
+ self->a_icon->texture[0].data.image.alpha = 0xff;
+ self->a_icon->texture[0].data.image.image = icon;
+ } else {
+ RrAppearanceClearTextures(self->a_icon);
self->a_icon->texture[0].type = RR_TEXTURE_NONE;
+ }
popup_delay_show(self->popup, usec, text);
}
diff --git a/openbox/popup.h b/openbox/popup.h
index 0072f53e..2b01ce24 100644
--- a/openbox/popup.h
+++ b/openbox/popup.h
@@ -110,7 +110,7 @@ void icon_popup_free(ObIconPopup *self);
#define icon_popup_show(s, t, i) icon_popup_delay_show((s),0,(t),(i))
void icon_popup_delay_show(ObIconPopup *self, gulong usec,
- gchar *text, const struct _ObClientIcon *icon);
+ gchar *text, RrImage *icon);
#define icon_popup_hide(p) popup_hide((p)->popup)
#define icon_popup_position(p, g, x, y) popup_position((p)->popup,(g),(x),(y))
#define icon_popup_text_width(p, w) popup_text_width((p)->popup,(w))
diff --git a/render/image.c b/render/image.c
index 2eb043a2..d22ef9e3 100644
--- a/render/image.c
+++ b/render/image.c
@@ -20,6 +20,7 @@
#include "geom.h"
#include "image.h"
#include "color.h"
+#include "imagecache.h"
#include <glib.h>
@@ -27,13 +28,91 @@
#define FLOOR(i) ((i) & (~0UL << FRACTION))
#define AVERAGE(a, b) (((((a) ^ (b)) & 0xfefefefeL) >> 1) + ((a) & (b)))
-static void ImageCopyResampled(RrPixel32 *dst, RrPixel32 *src,
- gulong dstW, gulong dstH,
- gulong srcW, gulong srcH)
+static void AddPicture(RrImage *self, RrImagePic ***list, gint *len,
+ RrImagePic *pic)
{
+ gint i;
+
+ g_assert(pic->width > 0 && pic->height > 0);
+
+ g_assert(g_hash_table_lookup(self->cache->table, pic) == NULL);
+
+ /* grow the list */
+ *list = g_renew(RrImagePic*, *list, ++*len);
+
+ /* move everything else down one */
+ for (i = *len-1; i > 0; --i)
+ (*list)[i] = (*list)[i-1];
+
+ /* set the new picture up at the front of the list */
+ (*list)[0] = pic;
+
+ /* add the picture as a key to point to this image in the cache */
+ g_hash_table_insert(self->cache->table, (*list)[0], self);
+
+#ifdef DEBUG
+ g_print("Adding %s picture to the cache: "
+ "Image 0x%x, w %d h %d Hash %u\n",
+ (*list == self->original ? "ORIGINAL" : "RESIZED"),
+ (guint)self, pic->width, pic->height, RrImagePicHash(pic));
+#endif
+}
+
+static void RemovePicture(RrImage *self, RrImagePic ***list,
+ gint i, gint *len)
+{
+ gint j;
+
+#ifdef DEBUG
+ g_print("Removing %s picture from the cache: "
+ "Image 0x%x, w %d h %d Hash %u\n",
+ (*list == self->original ? "ORIGINAL" : "RESIZED"),
+ (guint)self, (*list)[i]->width, (*list)[i]->height,
+ RrImagePicHash((*list)[i]));
+#endif
+
+ /* remove the picture as a key in the cache */
+ g_hash_table_remove(self->cache->table, (*list)[i]);
+
+ /* free the picture (and its rgba data) */
+ g_free((*list)[i]);
+ g_free((*list)[i]->data);
+ /* shift everything down one */
+ for (j = i; j < *len-1; ++j)
+ (*list)[j] = (*list)[j+1];
+ /* shrink the list */
+ *list = g_renew(RrImagePic*, *list, --*len);
+}
+
+static RrImagePic* ResizeImage(RrPixel32 *src,
+ gulong srcW, gulong srcH,
+ gulong dstW, gulong dstH)
+{
+ RrPixel32 *dst;
+ RrImagePic *pic;
gulong dstX, dstY, srcX, srcY;
gulong srcX1, srcX2, srcY1, srcY2;
gulong ratioX, ratioY;
+ gulong aspectW, aspectH;
+
+ /* keep the aspect ratio */
+ aspectW = dstW;
+ aspectH = (gint)(dstW * ((gdouble)srcH / srcW));
+ if (aspectH > dstH) {
+ aspectH = dstH;
+ aspectW = (gint)(dstH * ((gdouble)srcW / srcH));
+ }
+ dstW = aspectW;
+ dstH = aspectH;
+
+ if (srcW == dstW && srcH == dstH)
+ return NULL; /* no scaling needed ! */
+
+ pic = g_new(RrImagePic, 1);
+ dst = g_new(RrPixel32, dstW * dstH);
+ pic->width = dstW;
+ pic->height = dstH;
+ pic->data = dst;
ratioX = (srcW << FRACTION) / dstW;
ratioY = (srcH << FRACTION) / dstH;
@@ -104,56 +183,42 @@ static void ImageCopyResampled(RrPixel32 *dst, RrPixel32 *src,
(alpha << RrDefaultAlphaOffset);
}
}
+
+ return pic;
}
-void RrImageDraw(RrPixel32 *target, RrTextureRGBA *rgba,
- gint target_w, gint target_h,
- RrRect *area)
+/*! This drawns an RGBA picture into the target, within the rectangle specified
+ by the area parameter. If the area's size differs from the source's then it
+ will be centered within the rectangle */
+void DrawRGBA(RrPixel32 *target, gint target_w, gint target_h,
+ RrPixel32 *source, gint source_w, gint source_h,
+ gint alpha, RrRect *area)
{
RrPixel32 *dest;
- RrPixel32 *source;
- gint sw, sh, dw, dh;
gint col, num_pixels;
+ gint dw, dh;
- sw = rgba->width;
- sh = rgba->height;
+ g_assert(source_w <= area->width && source_h <= area->height);
- /* keep the ratio */
+ /* keep the aspect ratio */
dw = area->width;
- dh = (gint)(dw * ((gdouble)sh / sw));
+ dh = (gint)(dw * ((gdouble)source_h / source_w));
if (dh > area->height) {
dh = area->height;
- dw = (gint)(dh * ((gdouble)sw / sh));
- }
-
- if (!(dw && dh))
- return; /* XXX sanity check */
-
- if (sw != dw || sh != dh) {
- /*if (!(rgba->cache && dw == rgba->cwidth && dh == rgba->cheight))*/ {
- g_free(rgba->cache);
- rgba->cache = g_new(RrPixel32, dw * dh);
- ImageCopyResampled(rgba->cache, rgba->data, dw, dh, sw, sh);
- rgba->cwidth = dw;
- rgba->cheight = dh;
- }
- source = rgba->cache;
- } else {
- source = rgba->data;
+ dw = (gint)(dh * ((gdouble)source_w / source_h));
}
/* copy source -> dest, and apply the alpha channel.
-
center the image if it is smaller than the area */
col = 0;
num_pixels = dw * dh;
dest = target + area->x + (area->width - dw) / 2 +
(target_w * (area->y + (area->height - dh) / 2));
while (num_pixels-- > 0) {
- guchar alpha, r, g, b, bgr, bgg, bgb;
+ guchar a, r, g, b, bgr, bgg, bgb;
/* apply the rgba's opacity as well */
- alpha = ((*source >> RrDefaultAlphaOffset) * rgba->alpha) >> 8;
+ a = ((*source >> RrDefaultAlphaOffset) * alpha) >> 8;
r = *source >> RrDefaultRedOffset;
g = *source >> RrDefaultGreenOffset;
b = *source >> RrDefaultBlueOffset;
@@ -163,9 +228,9 @@ void RrImageDraw(RrPixel32 *target, RrTextureRGBA *rgba,
bgg = *dest >> RrDefaultGreenOffset;
bgb = *dest >> RrDefaultBlueOffset;
- r = bgr + (((r - bgr) * alpha) >> 8);
- g = bgg + (((g - bgg) * alpha) >> 8);
- b = bgb + (((b - bgb) * alpha) >> 8);
+ r = bgr + (((r - bgr) * a) >> 8);
+ g = bgg + (((g - bgg) * a) >> 8);
+ b = bgb + (((b - bgb) * a) >> 8);
*dest = ((r << RrDefaultRedOffset) |
(g << RrDefaultGreenOffset) |
@@ -180,3 +245,185 @@ void RrImageDraw(RrPixel32 *target, RrTextureRGBA *rgba,
}
}
}
+
+void RrImageDrawRGBA(RrPixel32 *target, RrTextureRGBA *rgba,
+ gint target_w, gint target_h,
+ RrRect *area)
+{
+ RrImagePic *scaled;
+
+ scaled = ResizeImage(rgba->data, rgba->width, rgba->height,
+ area->width, area->height);
+
+ if (scaled) {
+#ifdef DEBUG
+ g_warning("Scaling an RGBA! You should avoid this and just make "
+ "it the right size yourself!");
+#endif
+ DrawRGBA(target, target_w, target_h,
+ scaled->data, scaled->width, scaled->height,
+ rgba->alpha, area);
+ }
+ else
+ DrawRGBA(target, target_w, target_h,
+ rgba->data, rgba->width, rgba->height,
+ rgba->alpha, area);
+}
+
+RrImage* RrImageNew(RrImageCache *cache)
+{
+ RrImage *self;
+
+ self = g_new0(RrImage, 1);
+ self->ref = 1;
+ self->cache = cache;
+ return self;
+}
+
+void RrImageRef(RrImage *self)
+{
+ ++self->ref;
+}
+
+void RrImageUnref(RrImage *self)
+{
+ if (self && --self->ref == 0) {
+#ifdef DEBUG
+ g_print("Refcount to 0, removing ALL pictures from the cache: "
+ "Image 0x%x\n", (guint)self);
+#endif
+ while (self->n_original > 0)
+ RemovePicture(self, &self->original, 0, &self->n_original);
+ while (self->n_resized > 0)
+ RemovePicture(self, &self->resized, 0, &self->n_resized);
+ g_free(self);
+ }
+}
+
+void RrImageAddPicture(RrImage *self, RrPixel32 *data, gint w, gint h)
+{
+ gint i;
+ RrImagePic *pic;
+
+ /* make sure we don't already have this size.. */
+ for (i = 0; i < self->n_original; ++i)
+ if (self->original[i]->width == w && self->original[i]->height == h) {
+#ifdef DEBUG
+ g_print("Found duplicate ORIGINAL image: "
+ "Image 0x%x, w %d h %d\n", (guint)self, w, h);
+#endif
+ return;
+ }
+
+ /* remove any resized pictures of this same size */
+ for (i = 0; i < self->n_resized; ++i)
+ if (self->resized[i]->width == w || self->resized[i]->height == h) {
+ RemovePicture(self, &self->resized, i, &self->n_resized);
+ break;
+ }
+
+ /* add the new picture */
+ pic = g_new(RrImagePic, 1);
+ pic->width = w;
+ pic->height = h;
+ pic->data = g_memdup(data, w*h*sizeof(RrPixel32));
+ AddPicture(self, &self->original, &self->n_original, pic);
+}
+
+void RrImageRemovePicture(RrImage *self, gint w, gint h)
+{
+ gint i;
+
+ /* remove any resized pictures of this same size */
+ for (i = 0; i < self->n_original; ++i)
+ if (self->original[i]->width == w && self->original[i]->height == h) {
+ RemovePicture(self, &self->original, i, &self->n_original);
+ break;
+ }
+}
+
+void RrImageDrawImage(RrPixel32 *target, RrTextureImage *img,
+ gint target_w, gint target_h,
+ RrRect *area)
+{
+ gint i, min_diff, min_i;
+ RrImage *self;
+ RrImagePic *pic;
+
+ self = img->image;
+ pic = NULL;
+
+ /* is there an original of this size? (only w or h has to be right cuz
+ we maintain aspect ratios) */
+ for (i = 0; i < self->n_original; ++i)
+ if (self->original[i]->width == area->width ||
+ self->original[i]->height == area->height)
+ {
+ pic = self->original[i];
+ break;
+ }
+
+ /* is there a resize of this size? */
+ for (i = 0; i < self->n_resized; ++i)
+ if (self->resized[i]->width == area->width ||
+ self->resized[i]->height == area->height)
+ {
+ gint j;
+ RrImagePic *saved;
+
+ /* save the selected one */
+ saved = self->resized[i];
+
+ /* shift all the others down */
+ for (j = i; j > 0; --j)
+ self->resized[j] = self->resized[j-1];
+
+ /* and move the selected one to the top of the list */
+ self->resized[0] = saved;
+
+ pic = self->resized[0];
+ break;
+ }
+
+ if (!pic) {
+ /* find an original with a close size */
+ min_diff = -1;
+ min_i = 0;
+ for (i = 0; i < self->n_original; ++i) {
+ gint diff;
+ gint wdiff, hdiff;
+
+ /* our size difference metric.. */
+ wdiff = self->original[i]->width - area->width;
+ hdiff = self->original[i]->height - area->height;
+ diff = (wdiff * wdiff) + (hdiff * hdiff);
+
+ if (min_diff < 0 || diff < min_diff) {
+ min_diff = diff;
+ min_i = i;
+ }
+ }
+
+ /* resize the original to the given area */
+ pic = ResizeImage(self->original[min_i]->data,
+ self->original[min_i]->width,
+ self->original[min_i]->height,
+ area->width, area->height);
+
+ /* add the resized image to the image, as the first in the resized
+ list */
+ if (self->n_resized >= MAX_CACHE_RESIZED) {
+ /* remove the last one (last used one) */
+ RemovePicture(self, &self->resized, self->n_resized - 1,
+ &self->n_resized);
+ }
+ /* add it to the top of the resized list */
+ AddPicture(self, &self->resized, &self->n_resized, pic);
+ }
+
+ g_assert(pic != NULL);
+
+ DrawRGBA(target, target_w, target_h,
+ pic->data, pic->width, pic->height,
+ img->alpha, area);
+}
diff --git a/render/image.h b/render/image.h
index 1c535960..28f29c2c 100644
--- a/render/image.h
+++ b/render/image.h
@@ -22,8 +22,11 @@
#include "render.h"
#include "geom.h"
-void RrImageDraw(RrPixel32 *target, RrTextureRGBA *rgba,
- gint target_w, gint target_h,
- RrRect *area);
+void RrImageDrawImage(RrPixel32 *target, RrTextureImage *img,
+ gint target_w, gint target_h,
+ RrRect *area);
+void RrImageDrawRGBA(RrPixel32 *target, RrTextureRGBA *rgba,
+ gint target_w, gint target_h,
+ RrRect *area);
#endif
diff --git a/render/imagecache.c b/render/imagecache.c
new file mode 100644
index 00000000..ec2ff4d0
--- /dev/null
+++ b/render/imagecache.c
@@ -0,0 +1,149 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ imagecache.c for the Openbox window manager
+ Copyright (c) 2008 Dana Jansens
+
+ 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.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "render.h"
+#include "imagecache.h"
+
+static gboolean RrImagePicEqual(const RrImagePic *p1,
+ const RrImagePic *p2);
+
+RrImageCache* RrImageCacheNew()
+{
+ RrImageCache *self;
+
+ self = g_new(RrImageCache, 1);
+ self->ref = 1;
+ self->table = g_hash_table_new((GHashFunc)RrImagePicHash,
+ (GEqualFunc)RrImagePicEqual);
+ return self;
+}
+
+void RrImageCacheRef(RrImageCache *self)
+{
+ ++self->ref;
+}
+
+void RrImageCacheUnref(RrImageCache *self)
+{
+ if (self && --self->ref == 0) {
+ g_assert(g_hash_table_size(self->table) == 0);
+ g_hash_table_unref(self->table);
+
+ g_free(self);
+ }
+}
+
+/*! Finds an image in the cache, if it is already in there */
+RrImage* RrImageCacheFind(RrImageCache *self,
+ RrPixel32 *data, gint w, gint h)
+{
+ RrImagePic pic;
+ pic.width = w;
+ pic.height = h;
+ pic.data = data;
+ return g_hash_table_lookup(self->table, &pic);
+}
+
+/* This is a fast, reversable hash function called "lookup3", found here:
+ http://burtleburtle.net/bob/c/lookup3.c
+*/
+#define hashsize(n) ((RrPixel32)1<<(n))
+#define hashmask(n) (hashsize(n)-1)
+#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
+/* mix -- mix 3 32-bit values reversibly. */
+#define mix(a,b,c) \
+{ \
+ a -= c; a ^= rot(c, 4); c += b; \
+ b -= a; b ^= rot(a, 6); a += c; \
+ c -= b; c ^= rot(b, 8); b += a; \
+ a -= c; a ^= rot(c,16); c += b; \
+ b -= a; b ^= rot(a,19); a += c; \
+ c -= b; c ^= rot(b, 4); b += a; \
+}
+/* final -- final mixing of 3 32-bit values (a,b,c) into c */
+#define final(a,b,c) \
+{ \
+ c ^= b; c -= rot(b,14); \
+ a ^= c; a -= rot(c,11); \
+ b ^= a; b -= rot(a,25); \
+ c ^= b; c -= rot(b,16); \
+ a ^= c; a -= rot(c,4); \
+ b ^= a; b -= rot(a,14); \
+ c ^= b; c -= rot(b,24); \
+}
+
+guint32 hashword(const guint32 *key, gint length, guint32 initval)
+{
+ guint32 a,b,c;
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + (((guint32)length)<<2) + initval;
+
+ /* handle most of the key */
+ while (length > 3)
+ {
+ a += key[0];
+ b += key[1];
+ c += key[2];
+ mix(a,b,c);
+ length -= 3;
+ key += 3;
+ }
+
+ /* handle the last 3 guint32's */
+ switch(length) /* all the case statements fall through */
+ {
+ case 3: c+=key[2];
+ case 2: b+=key[1];
+ case 1: a+=key[0];
+ final(a,b,c);
+ case 0: /* case 0: nothing left to add */
+ break;
+ }
+ /* report the result */
+ return c;
+}
+
+#define HASH_INITVAL 0xf00d
+
+guint RrImagePicHash(const RrImagePic *p)
+{
+ return hashword(p->data, p->width * p->height, HASH_INITVAL);
+}
+
+static gboolean RrImagePicEqual(const RrImagePic *p1,
+ const RrImagePic *p2)
+{
+ guint s1, s2;
+ RrPixel32 *data1, *data2;
+ gint i;
+
+ if (p1->width != p2->width || p1->height != p2->height) return FALSE;
+
+ /* strcmp() would probably suck on 4k of data.. sum all their values and
+ see if they get the same thing. they already matched on their hashes
+ at this point. */
+ s1 = s2 = 0;
+ data1 = p1->data;
+ data2 = p2->data;
+ for (i = 0; i < p1->width * p1->height; ++i, ++data1)
+ s1 += *data1;
+ for (i = 0; i < p2->width * p2->height; ++i, ++data2)
+ s2 += *data2;
+ return s1 == s2;
+}
diff --git a/render/imagecache.h b/render/imagecache.h
new file mode 100644
index 00000000..8c96caf0
--- /dev/null
+++ b/render/imagecache.h
@@ -0,0 +1,37 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ imagecache.h for the Openbox window manager
+ Copyright (c) 2008 Dana Jansens
+
+ 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.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __imagecache_h
+#define __imagecache_h
+
+#include <glib.h>
+
+/* the number of resized pictures to cache for an image */
+#define MAX_CACHE_RESIZED 3
+
+struct _RrImagePic;
+
+guint RrImagePicHash(const struct _RrImagePic *p);
+
+struct _RrImageCache {
+ gint ref;
+
+ GHashTable *table;
+};
+
+#endif
diff --git a/render/render.c b/render/render.c
index 49485c8a..304148de 100644
--- a/render/render.c
+++ b/render/render.c
@@ -130,6 +130,24 @@ Pixmap RrPaintPixmap(RrAppearance *a, gint w, gint h)
}
RrPixmapMaskDraw(a->pixmap, &a->texture[i].data.mask, &tarea);
break;
+ case RR_TEXTURE_IMAGE:
+ g_assert(!transferred);
+ {
+ RrRect narea = tarea;
+ RrTextureImage *img = &a->texture[i].data.image;
+ if (img->twidth)
+ narea.width = MIN(tarea.width, img->twidth);
+ if (img->theight)
+ narea.height = MIN(tarea.height, img->theight);
+ narea.x += img->tx;
+ narea.y += img->ty;
+ RrImageDrawImage(a->surface.pixel_data,
+ &a->texture[i].data.image,
+ a->w, a->h,
+ &narea);
+ }
+ force_transfer = 1;
+ break;
case RR_TEXTURE_RGBA:
g_assert(!transferred);
{
@@ -141,10 +159,10 @@ Pixmap RrPaintPixmap(RrAppearance *a, gint w, gint h)
narea.height = MIN(tarea.height, rgb->theight);
narea.x += rgb->tx;
narea.y += rgb->ty;
- RrImageDraw(a->surface.pixel_data,
- &a->texture[i].data.rgba,
- a->w, a->h,
- &narea);
+ RrImageDrawRGBA(a->surface.pixel_data,
+ &a->texture[i].data.rgba,
+ a->w, a->h,
+ &narea);
}
force_transfer = 1;
break;
@@ -202,11 +220,15 @@ void RrAppearanceAddTextures(RrAppearance *a, gint numtex)
if (numtex) a->texture = g_new0(RrTexture, numtex);
}
+void RrAppearanceClearTextures(RrAppearance *a)
+{
+ memset(a->texture, 0, a->textures * sizeof(RrTexture));
+}
+
RrAppearance *RrAppearanceCopy(RrAppearance *orig)
{
RrSurface *spo, *spc;
RrAppearance *copy = g_new(RrAppearance, 1);
- gint i;
copy->inst = orig->inst;
@@ -282,10 +304,6 @@ RrAppearance *RrAppearanceCopy(RrAppearance *orig)
copy->textures = orig->textures;
copy->texture = g_memdup(orig->texture,
orig->textures * sizeof(RrTexture));
- for (i = 0; i < copy->textures; ++i)
- if (copy->texture[i].type == RR_TEXTURE_RGBA) {
- copy->texture[i].data.rgba.cache = NULL;
- }
copy->pixmap = None;
copy->xftdraw = NULL;
copy->w = copy->h = 0;
@@ -294,17 +312,10 @@ RrAppearance *RrAppearanceCopy(RrAppearance *orig)
void RrAppearanceFree(RrAppearance *a)
{
- gint i;
-
if (a) {
RrSurface *p;
if (a->pixmap != None) XFreePixmap(RrDisplay(a->inst), a->pixmap);
if (a->xftdraw != NULL) XftDrawDestroy(a->xftdraw);
- for (i = 0; i < a->textures; ++i)
- if (a->texture[i].type == RR_TEXTURE_RGBA) {
- g_free(a->texture[i].data.rgba.cache);
- a->texture[i].data.rgba.cache = NULL;
- }
if (a->textures)
g_free(a->texture);
p = &a->surface;
@@ -403,6 +414,9 @@ gint RrMinWidth(RrAppearance *a)
case RR_TEXTURE_RGBA:
w += MAX(w, a->texture[i].data.rgba.width);
break;
+ case RR_TEXTURE_IMAGE:
+ /* images resize so they don't contribute anything to the min */
+ break;
case RR_TEXTURE_LINE_ART:
w += MAX(w, MAX(a->texture[i].data.lineart.x1,
a->texture[i].data.lineart.x2));
@@ -455,6 +469,9 @@ gint RrMinHeight(RrAppearance *a)
case RR_TEXTURE_RGBA:
h += MAX(h, a->texture[i].data.rgba.height);
break;
+ case RR_TEXTURE_IMAGE:
+ /* images resize so they don't contribute anything to the min */
+ break;
case RR_TEXTURE_LINE_ART:
h += MAX(h, MAX(a->texture[i].data.lineart.y1,
a->texture[i].data.lineart.y2));
diff --git a/render/render.h b/render/render.h
index 260ebeb0..5d1d1552 100644
--- a/render/render.h
+++ b/render/render.h
@@ -37,11 +37,15 @@ typedef struct _RrFont RrFont;
typedef struct _RrTexture RrTexture;
typedef struct _RrTextureMask RrTextureMask;
typedef struct _RrTextureRGBA RrTextureRGBA;
+typedef struct _RrTextureImage RrTextureImage;
typedef struct _RrTextureText RrTextureText;
typedef struct _RrTextureLineArt RrTextureLineArt;
typedef struct _RrPixmapMask RrPixmapMask;
typedef struct _RrInstance RrInstance;
typedef struct _RrColor RrColor;
+typedef struct _RrImage RrImage;
+typedef struct _RrImagePic RrImagePic;
+typedef struct _RrImageCache RrImageCache;
typedef guint32 RrPixel32;
typedef guint16 RrPixel16;
@@ -76,7 +80,8 @@ typedef enum {
RR_TEXTURE_MASK,
RR_TEXTURE_TEXT,
RR_TEXTURE_LINE_ART,
- RR_TEXTURE_RGBA
+ RR_TEXTURE_RGBA,
+ RR_TEXTURE_IMAGE
} RrTextureType;
typedef enum {
@@ -163,11 +168,19 @@ struct _RrTextureRGBA {
gint height;
gint alpha;
RrPixel32 *data;
-/* cached scaled so we don't have to scale often */
- gint cwidth;
- gint cheight;
- RrPixel32 *cache;
-/* size and position to draw at */
+ /* size and position to draw at (if these are zero, then it will be
+ drawn to fill the entire texture */
+ gint tx;
+ gint ty;
+ gint twidth;
+ gint theight;
+};
+
+struct _RrTextureImage {
+ RrImage *image;
+ gint alpha;
+ /* size and position to draw at (if these are zero, then it will be
+ drawn to fill the entire texture */
gint tx;
gint ty;
gint twidth;
@@ -184,12 +197,15 @@ struct _RrTextureLineArt {
union _RrTextureData {
RrTextureRGBA rgba;
+ RrTextureImage image;
RrTextureText text;
RrTextureMask mask;
RrTextureLineArt lineart;
};
struct _RrTexture {
+ /* If changing the type of a texture, you should DEFINITELY call
+ RrAppearanceClearTextures() first! */
RrTextureType type;
RrTextureData data;
};
@@ -207,6 +223,25 @@ struct _RrAppearance {
gint w, h;
};
+/*! Holds a RGBA image picture */
+struct _RrImagePic {
+ gint width, height;
+ RrPixel32 *data;
+};
+
+/*! An RrImage is a sort of meta-image. It can contain multiple versions of
+ an image at different sizes, which may or may not be completely different
+ pictures */
+struct _RrImage {
+ gint ref;
+ struct _RrImageCache *cache;
+
+ struct _RrImagePic **original;
+ gint n_original;
+ struct _RrImagePic **resized;
+ gint n_resized;
+};
+
/* these are the same on all endian machines because it seems to be dependant
on the endianness of the gfx card, not the cpu. */
#define RrDefaultAlphaOffset 24
@@ -253,6 +288,8 @@ RrAppearance *RrAppearanceCopy (RrAppearance *a);
void RrAppearanceFree (RrAppearance *a);
void RrAppearanceRemoveTextures(RrAppearance *a);
void RrAppearanceAddTextures(RrAppearance *a, gint numtex);
+/*! Always call this when changing the type of a texture in an appearance */
+void RrAppearanceClearTextures(RrAppearance *a);
RrFont *RrFontOpen (const RrInstance *inst, const gchar *name,
gint size, RrFontWeight weight, RrFontSlant slant);
@@ -280,6 +317,21 @@ gboolean RrPixmapToRGBA(const RrInstance *inst,
Pixmap pmap, Pixmap mask,
gint *w, gint *h, RrPixel32 **data);
+RrImageCache* RrImageCacheNew();
+void RrImageCacheRef(RrImageCache *self);
+void RrImageCacheUnref(RrImageCache *self);
+
+/*! Finds an image in the cache, if it is already in there */
+RrImage* RrImageCacheFind(RrImageCache *self,
+ RrPixel32 *data, gint w, gint h);
+
+RrImage* RrImageNew(RrImageCache *cache);
+void RrImageRef(RrImage *im);
+void RrImageUnref(RrImage *im);
+
+void RrImageAddPicture(RrImage *im, RrPixel32 *data, gint w, gint h);
+void RrImageRemovePicture(RrImage *im, gint w, gint h);
+
G_END_DECLS
#endif /*__render_h*/
diff --git a/render/theme.c b/render/theme.c
index a6931be6..e1cff0cd 100644
--- a/render/theme.c
+++ b/render/theme.c
@@ -552,6 +552,9 @@ RrTheme* RrThemeNew(const RrInstance *inst, const gchar *name,
theme->def_win_icon = read_c_image(OB_DEFAULT_ICON_WIDTH,
OB_DEFAULT_ICON_HEIGHT,
OB_DEFAULT_ICON_pixel_data);
+ theme->def_win_icon_w = OB_DEFAULT_ICON_WIDTH;
+ theme->def_win_icon_h = OB_DEFAULT_ICON_HEIGHT;
+
/* read the decoration textures */
if (!read_appearance(db, inst,
diff --git a/render/theme.h b/render/theme.h
index 9f51fabd..5b4e785e 100644
--- a/render/theme.h
+++ b/render/theme.h
@@ -106,7 +106,9 @@ struct _RrTheme {
gchar menu_text_disabled_selected_shadow_alpha;
/* style settings - pics */
- RrPixel32 *def_win_icon; /* 48x48 RGBA */
+ RrPixel32 *def_win_icon; /* RGBA */
+ gint def_win_icon_w;
+ gint def_win_icon_h;
/* style settings - masks */
RrPixmapMask *max_mask;
diff --git a/tests/Makefile b/tests/Makefile
index 6374b392..9fc5fe81 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -3,4 +3,4 @@ files=$(wildcard *.c)
all: $(files:.c=)
%: %.c
- $(CC) $(CFLAGS) -o $@ $^ -lX11 -lXext -L/usr/X11R6/lib -I/usr/X11R6/include
+ $(CC) `pkg-config --cflags --libs glib-2.0` $(CFLAGS) -o $@ $^ -lX11 -lXext -L/usr/X11R6/lib -I/usr/X11R6/include
diff --git a/tests/icons.c b/tests/icons.c
index 315a10fb..af13e0b5 100644
--- a/tests/icons.c
+++ b/tests/icons.c
@@ -23,6 +23,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
+#include <glib.h>
Window findClient(Display *d, Window win)
{
@@ -156,6 +157,70 @@ int main(int argc, char **argv)
++image;
} while (ret_bytesleft > 0 && image < MAX_IMAGES);
+#define hashsize(n) ((guint32)1<<(n))
+#define hashmask(n) (hashsize(n)-1)
+#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
+
+#define mix(a,b,c) \
+{ \
+ a -= c; a ^= rot(c, 4); c += b; \
+ b -= a; b ^= rot(a, 6); a += c; \
+ c -= b; c ^= rot(b, 8); b += a; \
+ a -= c; a ^= rot(c,16); c += b; \
+ b -= a; b ^= rot(a,19); a += c; \
+ c -= b; c ^= rot(b, 4); b += a; \
+}
+
+#define final(a,b,c) \
+{ \
+ c ^= b; c -= rot(b,14); \
+ a ^= c; a -= rot(c,11); \
+ b ^= a; b -= rot(a,25); \
+ c ^= b; c -= rot(b,16); \
+ a ^= c; a -= rot(c,4); \
+ b ^= a; b -= rot(a,14); \
+ c ^= b; c -= rot(b,24); \
+}
+
+ /* hash the images */
+ for (j = 0; j < image; ++j) {
+ unsigned int w, h, length;
+ guint32 a,b,c;
+ guint32 initval = 0xf00d;
+ const guint32 *k = (guint32*)i[j]->data;
+
+ w = i[j]->width;
+ h = i[j]->height;
+ length = w * h;
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + (((guint32)length)<<2) + initval;
+
+ /*---------------------------------------- handle most of the key */
+ while (length > 3)
+ {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a,b,c);
+ length -= 3;
+ k += 3;
+ }
+
+ /*--------------------------------- handle the last 3 uint32_t's */
+ switch(length) /* all the case statements fall through */
+ {
+ case 3 : c+=k[2];
+ case 2 : b+=k[1];
+ case 1 : a+=k[0];
+ final(a,b,c);
+ case 0: /* case 0: nothing left to add */
+ break;
+ }
+ /*------------------------------------ report the result */
+ printf("image[%d] %ux%u %lu\n", j, w, h, c);
+ }
+
win = XCreateSimpleWindow(d, RootWindow(d, s), 0, 0, winw, winh,
0, 0, 0);
assert(win);