]> 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)
committerDana Jansens <danakj@orodu.net>
Thu, 14 Feb 2008 20:09:24 +0000 (15:09 -0500)
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 df891ac06f11d763f6a9df80004b27f314d31f04..051be7d76101e3483e4dfbfd144ca7dba0dafffc 100644 (file)
@@ -85,6 +85,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 62489c1d86af20b5e07fd4e30e46d2d49905aeae..9fa311cfbdb1fce62123c416284cabe45b527cb6 100644 (file)
@@ -68,9 +68,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);
@@ -110,6 +111,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();
@@ -117,6 +131,9 @@ void client_startup(gboolean reconfig)
 
 void client_shutdown(gboolean reconfig)
 {
+    RrImageUnref(client_default_icon);
+    client_default_icon = NULL;
+
     if (reconfig) return;
 }
 
@@ -670,7 +687,6 @@ void client_unmanage_all(void)
 
 void client_unmanage(ObClient *self)
 {
-    guint j;
     GSList *it;
     gulong ignore_start;
 
@@ -802,11 +818,8 @@ void client_unmanage(ObClient *self)
     ob_debug("Unmanaged window 0x%lx\n", 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);
@@ -2090,143 +2103,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 (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(ob_display, self->window))) {
             if (hints->flags & IconPixmapHint) {
-                self->nicons = 1;
-                self->icons = g_new(ObClientIcon, self->nicons);
+                gboolean xicon;
                 xerror_set_ignore(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);
                 xerror_set_ignore(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);
-        PROP_SETA32(self->window, net_wm_icon, cardinal, data, 48*48+2);
-        g_free(data);
+        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)
@@ -3919,53 +3923,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 c34d0ab628a29456d1a7168e3e4c6921e6204a4a..c2461cb9a1a750973783e982242525da2f372faa 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;
@@ -644,7 +633,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 f0cf25e2e3cd9e908c13bb89f1eff91c53c94d4b..df3558dff5110e59e91f3cb562cd8ed8be614af3 100644 (file)
@@ -42,6 +42,7 @@ typedef struct _ObFocusCyclePopupTarget ObFocusCyclePopupTarget;
 struct _ObFocusCyclePopupTarget
 {
     ObClient *client;
+    RrImage *icon;
     gchar *text;
     Window win;
 };
@@ -106,7 +107,8 @@ void focus_cycle_popup_startup(gboolean reconfig)
     popup.a_text->surface.parent = popup.a_bg;
     popup.a_icon->surface.parent = popup.a_bg;
 
-    popup.a_icon->texture[0].type = RR_TEXTURE_RGBA;
+    RrAppearanceClearTextures(popup.a_icon);
+    popup.a_icon->texture[0].type = RR_TEXTURE_IMAGE;
 
     RrAppearanceAddTextures(popup.a_bg, 1);
     popup.a_bg->texture[0].type = RR_TEXTURE_RGBA;
@@ -141,8 +143,10 @@ void focus_cycle_popup_shutdown(gboolean reconfig)
     while(popup.targets) {
         ObFocusCyclePopupTarget *t = popup.targets->data;
 
+        RrImageUnref(t->icon);
         g_free(t->text);
         XDestroyWindow(ob_display, t->win);
+        g_free(t);
 
         popup.targets = g_list_delete_link(popup.targets, popup.targets);
     }
@@ -196,6 +200,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->win = create_window(p->bg, 0, 0, NULL);
 
                 XMapWindow(ob_display, t->win);
@@ -399,7 +405,6 @@ static void popup_render(ObFocusCyclePopup *p, const ObClient *c)
         /* have to redraw the targetted icon and last targetted icon,
            they can pick up the hilite changes in the backgroud */
         if (!p->mapped || newtarget == target || p->last_target == target) {
-            const ObClientIcon *icon;
             const gint row = i / icons_per_row; /* starting from 0 */
             const gint col = i % icons_per_row; /* starting from 0 */
             gint innerx, innery;
@@ -415,12 +420,9 @@ static void popup_render(ObFocusCyclePopup *p, const ObClient *c)
                               innerx, innery, innerw, innerh);
 
             /* get the icon from the client */
-            icon = client_icon(target->client, innerw, innerh);
-            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.alpha =
+            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 icon */
             p->a_icon->surface.parentx = innerx;
@@ -479,6 +481,7 @@ void focus_cycle_popup_hide(void)
     while(popup.targets) {
         ObFocusCyclePopupTarget *t = popup.targets->data;
 
+        RrImageUnref(t->icon);
         g_free(t->text);
         XDestroyWindow(ob_display, t->win);
         g_free(t);
@@ -522,7 +525,7 @@ void focus_cycle_popup_single_show(struct _ObClient *c,
     }
 
     text = popup_get_name(c);
-    icon_popup_show(single_popup, text, client_icon(c, ICON_SIZE, ICON_SIZE));
+    icon_popup_show(single_popup, text, client_icon(c));
     g_free(text);
     screen_hide_desktop_popup();
 }
index e4db2a06e6734812a8cfbcc60c0e038490a4bf80..4720d763ed4d78ff4b0f534140711b553e8a2ed2 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 d9426e9062dcc17e715a49166576457eb21efed2..f2458fa8fa931c4593980dbbc138d3e2bfc99731 100644 (file)
@@ -504,6 +504,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 8832a1241f5c1d49dbeb5ce07b97abb6903f2bfc..7e11545de0d89318631ca9b2c763f6fb10450772 100644 (file)
@@ -116,11 +116,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 3bbf1be2ebbf83c53b64e6a835391d78c63fe4c7..ce77fc0fe689e9adda7ce242d2976d12f4becbe4 100644 (file)
@@ -334,6 +334,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(ob_display, self->window,
                   self->area.width, self->area.height);
@@ -438,6 +439,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;
@@ -455,7 +457,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;
 
@@ -467,15 +469,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;
@@ -499,6 +498,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;
@@ -690,7 +690,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;
@@ -701,7 +701,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 cb14187c030904926cee89ed67a784b5f4ceb9a5..f2dc7159e60d9d7bf88f1df61433baca27abc93d 100644 (file)
 #include <X11/keysym.h>
 
 
-RrInstance *ob_rr_inst;
-RrTheme    *ob_rr_theme;
-ObMainLoop *ob_main_loop;
-Display    *ob_display;
-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;
+ObMainLoop   *ob_main_loop;
+Display      *ob_display;
+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;
@@ -182,6 +183,7 @@ gint main(gint argc, gchar **argv)
     ob_rr_inst = RrInstanceNew(ob_display, ob_screen);
     if (ob_rr_inst == NULL)
         ob_exit_with_error(_("Failed to initialize the obrender library."));
+    ob_rr_icons = RrImageCacheNew();
 
     XSynchronize(ob_display, xsync);
 
@@ -398,6 +400,7 @@ gint main(gint argc, gchar **argv)
     XSync(ob_display, FALSE);
 
     RrThemeFree(ob_rr_theme);
+    RrImageCacheUnref(ob_rr_icons);
     RrInstanceFree(ob_rr_inst);
 
     session_shutdown(being_replaced);
index b768fee31f22ed0d0d669c80f13405545eccdf7d..4f2310f8eaa530dd9c584c91398fb94160fc8bcf 100644 (file)
@@ -30,6 +30,7 @@
 struct _ObMainLoop;
 
 extern RrInstance *ob_rr_inst;
+extern RrImageCache *ob_rr_icons;
 extern RrTheme    *ob_rr_theme;
 
 extern struct _ObMainLoop *ob_main_loop;
index 15d1bf52bed291cb342b3ffee40263003e407468..770f33d0ac73047d1a04d7f0b174a6892a766e02 100644 (file)
@@ -367,16 +367,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 fa630b749a235784dc9b847b0dabdae0084ab9f5..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,53 +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 (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;
@@ -160,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) |
@@ -177,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 b81e80a69d37bd627b38ea0b9a6ecac2dc6976fd..fd442d26b40bc43b83e074fa120cc1060437ca55 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;
@@ -405,6 +416,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 - l - r,
                            a->texture[i].data.lineart.x2 - l - r));
@@ -457,6 +471,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 - t - b,
                            a->texture[i].data.lineart.y2 - t - b));
index 10eafc6e5724416a61735d680f495181e6ca0602..e79727df0b751499695844c7292b1ade2d05228f 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 b1b15ff249c08496890332e885014c556cb6e172..cd2352c3ca5bb43f8935e80d9b52e61cdda83be8 100644 (file)
@@ -547,6 +547,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 3f87ce93282a3a0d7f4dd36e566a17ae9bc38814..1940970a121fbfa6dc09b2d0a8e4d6cbc3db50f0 100644 (file)
@@ -107,7 +107,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.071712 seconds and 4 git commands to generate.