]> Dogcows Code - chaz/openbox/commitdiff
Introducing the icon cache.
authorDana Jansens <danakj@orodu.net>
Thu, 14 Feb 2008 09:47:49 +0000 (10:47 +0100)
committerMikael Magnusson <mikachu@comhem.se>
Thu, 14 Feb 2008 10:44:28 +0000 (11:44 +0100)
If an icon is the same as one in the cache, then it uses that one.
icons of different sizes (from the same client) are linked together into
one, and resizes of icons are cached and linked to all the various sizes.
so you only need one icon in memory for all your terminals now. ya!

24 files changed:
Makefile.am
openbox/client.c
openbox/client.h
openbox/client_list_combined_menu.c
openbox/client_list_menu.c
openbox/focus_cycle_popup.c
openbox/framerender.c
openbox/menu.c
openbox/menu.h
openbox/menuframe.c
openbox/openbox.c
openbox/openbox.h
openbox/popup.c
openbox/popup.h
render/image.c
render/image.h
render/imagecache.c [new file with mode: 0644]
render/imagecache.h [new file with mode: 0644]
render/render.c
render/render.h
render/theme.c
render/theme.h
tests/Makefile
tests/icons.c

index d8dba2c7a27cdbcfce52c614bc8e2632b88e0703..dd5bb6c542f8dbe315eebb253114b9d8bcbc4647 100644 (file)
@@ -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 \
index e65ed07c36d3505b5f3c746ad65213a97286bcd8..c7a82d16e8c1969b00bce69f7f60bb06c0275f5a 100644 (file)
@@ -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;
 }
 
index bb63137f9df534024a6a77ae920fbfcf7752d6cc..c24fcb20ca0d66aaa9b91e19e363264245a6a031 100644 (file)
@@ -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
index 76a819fcbaf8afee80aa31f84ed9787059524d04..a04d07d6fd5295ae2ac0615c65e7e518804bb8be 100644 (file)
@@ -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;
                 }
index e6521a0aafe8761dcce6a2b392dbe2139af69d9e..4ec6e7855f8830128189683869919a711a52c4e7 100644 (file)
@@ -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;
             }
 
index 4d1dfaa35a87533dd9ab3b6bf66c2efcb1d9f684..3feac6ab512469b5d299f65514a5f8614fc918ce 100644 (file)
@@ -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();
 }
index 87706b2b2921d84ea6eb7a3fa35085f16143bb2f..bf71d2c34a937097f59557a6f320a49196839aa0 100644 (file)
@@ -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);
index 768630bd2939a9b80c35a0492c0380cd96b71ef3..63b74b9520aae55962c5b17ee342022920d3f89b 100644 (file)
@@ -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);
index 752a6771461de01d61d08976fe40fa0cb1e08097..c488f67d873eacb41da411c99c4ed91909afce8f 100644 (file)
@@ -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;
index 598bc005f6c5e4a32b60272897e1c66698dfebb1..c25e485f06c6c94d5eab803de7d213fe8f61e1cd 100644 (file)
@@ -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;
 
index a6a81cef7dec11e813e67f9ac8dd5d3b23b0fd1d..ec6cc44c11214e6c274fbdbd14956d99d27cba15 100644 (file)
 #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);
index 47d92042e2eed946931c7a0dcb49ba3fe2a18683..471aa779a5ea5063ced0d2fa18bd7ba13205148f 100644 (file)
@@ -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;
index 02c87848675c3b30fe5f455d716c1a48510b0797..fd31846eb8c309b65f3ef2939a54472cad794761 100644 (file)
@@ -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);
 }
index 0072f53e1c081f7998333eff01270538c2148e5d..2b01ce24bf857396131e0560126c44b1addf4d94 100644 (file)
@@ -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))
index 2eb043a25c72ddec769f0a2ac1d82e770de89a7c..d22ef9e39d023f7e3d2a2b175b05c15b8e319482 100644 (file)
@@ -20,6 +20,7 @@
 #include "geom.h"
 #include "image.h"
 #include "color.h"
+#include "imagecache.h"
 
 #include <glib.h>
 
 #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);
+}
index 1c53596077873be6649def4cbc05b6a1957a20e9..28f29c2c57dd089373c39d267684a93cc6d58e43 100644 (file)
 #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 (file)
index 0000000..ec2ff4d
--- /dev/null
@@ -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 (file)
index 0000000..8c96caf
--- /dev/null
@@ -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
index 49485c8a1dea41f0de7c3c6781e2e51b884754f2..304148dec8054af7f7b1f34dff6e8a2838f393fc 100644 (file)
@@ -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));
index 260ebeb0bf9f931fe63b1690fd2ba4de3cca7659..5d1d15522cc1fce3c2084bb1bb20c6fe01a59b8d 100644 (file)
@@ -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*/
index a6931be664923a4980c23d736592671976917766..e1cff0cd67eca89a026b94107fc51a99a73077cb 100644 (file)
@@ -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,
index 9f51fabd905793c75a1fe61264348bb04871494a..5b4e785e80527499a04bb77b66c0f0d549b9dd89 100644 (file)
@@ -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;
index 6374b3920c10c0ba5cfa6f5de46949ebcd20418b..9fc5fe8104114b16d0d65a18905cab4eccb64dc8 100644 (file)
@@ -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
index 315a10fb23d2b1012ffcebff7768838df4c2e0e4..af13e0b5b1b6172541392eb8dd1ad2fc51452d1a 100644 (file)
@@ -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);
This page took 0.071612 seconds and 4 git commands to generate.