From 512d93afcc3e7dd5caa42cdb69508964c6338f3d Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Thu, 14 Feb 2008 14:44:17 -0500 Subject: backport the changes to render/ from the alttab branch (commit 3592046b2b26e05ee94c0dd0fed5b7fd5475c198) in master, as the iconcache changes depend on this git show 3592046b -- render --- render/render.c | 18 ++++++++++++++---- render/render.h | 5 +++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/render/render.c b/render/render.c index 2813dda0..b81e80a6 100644 --- a/render/render.c +++ b/render/render.c @@ -132,10 +132,20 @@ Pixmap RrPaintPixmap(RrAppearance *a, gint w, gint h) break; case RR_TEXTURE_RGBA: g_assert(!transferred); - RrImageDraw(a->surface.pixel_data, - &a->texture[i].data.rgba, - a->w, a->h, - &tarea); + { + RrRect narea = tarea; + RrTextureRGBA *rgb = &a->texture[i].data.rgba; + if (rgb->twidth) + narea.width = MIN(tarea.width, rgb->twidth); + if (rgb->theight) + 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); + } force_transfer = 1; break; } diff --git a/render/render.h b/render/render.h index d9438edc..10eafc6e 100644 --- a/render/render.h +++ b/render/render.h @@ -167,6 +167,11 @@ struct _RrTextureRGBA { gint cwidth; gint cheight; RrPixel32 *cache; +/* size and position to draw at */ + gint tx; + gint ty; + gint twidth; + gint theight; }; struct _RrTextureLineArt { -- cgit v1.2.3 From 4c7cc1cfa64bf5722f059eae0528d510c2ae636f Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Thu, 14 Feb 2008 10:47:49 +0100 Subject: Introducing the icon cache. 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! --- Makefile.am | 2 + openbox/client.c | 266 ++++++++++++++---------------- openbox/client.h | 20 +-- openbox/client_list_combined_menu.c | 10 +- openbox/client_list_menu.c | 9 +- openbox/focus_cycle_popup.c | 19 ++- openbox/framerender.c | 20 +-- openbox/menu.c | 1 + openbox/menu.h | 8 +- openbox/menuframe.c | 22 +-- openbox/openbox.c | 25 +-- openbox/openbox.h | 1 + openbox/popup.c | 15 +- openbox/popup.h | 2 +- render/image.c | 314 ++++++++++++++++++++++++++++++++---- render/image.h | 9 +- render/imagecache.c | 149 +++++++++++++++++ render/imagecache.h | 37 +++++ render/render.c | 49 ++++-- render/render.h | 64 +++++++- render/theme.c | 3 + render/theme.h | 4 +- tests/Makefile | 2 +- tests/icons.c | 65 ++++++++ 24 files changed, 830 insertions(+), 286 deletions(-) create mode 100644 render/imagecache.c create mode 100644 render/imagecache.h diff --git a/Makefile.am b/Makefile.am index df891ac0..051be7d7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 \ diff --git a/openbox/client.c b/openbox/client.c index 62489c1d..9fa311cf 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -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; } diff --git a/openbox/client.h b/openbox/client.h index c34d0ab6..c2461cb9 100644 --- a/openbox/client.h +++ b/openbox/client.h @@ -40,15 +40,6 @@ struct _ObSessionState; struct _ObPrompt; typedef struct _ObClient ObClient; -typedef struct _ObClientIcon ObClientIcon; - -/*! Holds an icon in ARGB format */ -struct _ObClientIcon -{ - gint width; - gint height; - RrPixel32 *data; -}; /*! Possible window types */ typedef enum @@ -307,10 +298,8 @@ struct _ObClient */ guint functions; - /*! Icons for the client as specified on the client window */ - ObClientIcon *icons; - /*! The number of icons in icons */ - guint nicons; + /* The window's icon, in a variety of shapes and sizes */ + RrImage *icon_set; /*! Where the window should iconify to/from */ Rect icon_geometry; @@ -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 diff --git a/openbox/client_list_combined_menu.c b/openbox/client_list_combined_menu.c index 76a819fc..a04d07d6 100644 --- a/openbox/client_list_combined_menu.c +++ b/openbox/client_list_combined_menu.c @@ -56,8 +56,6 @@ static gboolean self_update(ObMenuFrame *frame, gpointer data) if (client_normal(c) && (!c->skip_taskbar || c->iconic) && (c->desktop == desktop || c->desktop == DESKTOP_ALL)) { - const ObClientIcon *icon; - empty = FALSE; if (c->iconic) { @@ -69,11 +67,9 @@ static gboolean self_update(ObMenuFrame *frame, gpointer data) e = menu_add_normal(menu, desktop, c->title, NULL, FALSE); } - if (config_menu_client_list_icons - && (icon = client_icon(c, 32, 32))) { - e->data.normal.icon_width = icon->width; - e->data.normal.icon_height = icon->height; - e->data.normal.icon_data = icon->data; + if (config_menu_client_list_icons) { + e->data.normal.icon = client_icon(c); + RrImageRef(e->data.normal.icon); e->data.normal.icon_alpha = c->iconic ? OB_ICONIC_ALPHA : 0xff; } diff --git a/openbox/client_list_menu.c b/openbox/client_list_menu.c index e6521a0a..4ec6e785 100644 --- a/openbox/client_list_menu.c +++ b/openbox/client_list_menu.c @@ -58,7 +58,6 @@ static gboolean desk_menu_update(ObMenuFrame *frame, gpointer data) (c->desktop == d->desktop || c->desktop == DESKTOP_ALL)) { ObMenuEntry *e; - const ObClientIcon *icon; empty = FALSE; @@ -71,11 +70,9 @@ static gboolean desk_menu_update(ObMenuFrame *frame, gpointer data) e = menu_add_normal(menu, d->desktop, c->title, NULL, FALSE); } - if (config_menu_client_list_icons - && (icon = client_icon(c, 32, 32))) { - e->data.normal.icon_width = icon->width; - e->data.normal.icon_height = icon->height; - e->data.normal.icon_data = icon->data; + if (config_menu_client_list_icons) { + e->data.normal.icon = client_icon(c); + RrImageRef(e->data.normal.icon); e->data.normal.icon_alpha = c->iconic ? OB_ICONIC_ALPHA : 0xff; } diff --git a/openbox/focus_cycle_popup.c b/openbox/focus_cycle_popup.c index f0cf25e2..df3558df 100644 --- a/openbox/focus_cycle_popup.c +++ b/openbox/focus_cycle_popup.c @@ -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(); } diff --git a/openbox/framerender.c b/openbox/framerender.c index e4db2a06..4720d763 100644 --- a/openbox/framerender.c +++ b/openbox/framerender.c @@ -354,21 +354,21 @@ static void framerender_label(ObFrame *self, RrAppearance *a) static void framerender_icon(ObFrame *self, RrAppearance *a) { - const ObClientIcon *icon; + RrImage *icon; if (!self->icon_on) return; - icon = client_icon(self->client, - ob_rr_theme->button_size + 2, - ob_rr_theme->button_size + 2); + icon = client_icon(self->client); + if (icon) { - a->texture[0].type = RR_TEXTURE_RGBA; - a->texture[0].data.rgba.width = icon->width; - a->texture[0].data.rgba.height = icon->height; - a->texture[0].data.rgba.alpha = 0xff; - a->texture[0].data.rgba.data = icon->data; - } else + RrAppearanceClearTextures(a); + a->texture[0].type = RR_TEXTURE_IMAGE; + a->texture[0].data.image.alpha = 0xff; + a->texture[0].data.image.image = icon; + } else { + RrAppearanceClearTextures(a); a->texture[0].type = RR_TEXTURE_NONE; + } RrPaint(a, self->icon, ob_rr_theme->button_size + 2, ob_rr_theme->button_size + 2); diff --git a/openbox/menu.c b/openbox/menu.c index d9426e90..f2458fa8 100644 --- a/openbox/menu.c +++ b/openbox/menu.c @@ -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); diff --git a/openbox/menu.h b/openbox/menu.h index 8832a124..7e11545d 100644 --- a/openbox/menu.h +++ b/openbox/menu.h @@ -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; diff --git a/openbox/menuframe.c b/openbox/menuframe.c index 3bbf1be2..ce77fc0f 100644 --- a/openbox/menuframe.c +++ b/openbox/menuframe.c @@ -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; diff --git a/openbox/openbox.c b/openbox/openbox.c index cb14187c..f2dc7159 100644 --- a/openbox/openbox.c +++ b/openbox/openbox.c @@ -86,17 +86,18 @@ #include -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); diff --git a/openbox/openbox.h b/openbox/openbox.h index b768fee3..4f2310f8 100644 --- a/openbox/openbox.h +++ b/openbox/openbox.h @@ -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; diff --git a/openbox/popup.c b/openbox/popup.c index 15d1bf52..770f33d0 100644 --- a/openbox/popup.c +++ b/openbox/popup.c @@ -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); } diff --git a/openbox/popup.h b/openbox/popup.h index 0072f53e..2b01ce24 100644 --- a/openbox/popup.h +++ b/openbox/popup.h @@ -110,7 +110,7 @@ void icon_popup_free(ObIconPopup *self); #define icon_popup_show(s, t, i) icon_popup_delay_show((s),0,(t),(i)) void icon_popup_delay_show(ObIconPopup *self, gulong usec, - gchar *text, const struct _ObClientIcon *icon); + gchar *text, RrImage *icon); #define icon_popup_hide(p) popup_hide((p)->popup) #define icon_popup_position(p, g, x, y) popup_position((p)->popup,(g),(x),(y)) #define icon_popup_text_width(p, w) popup_text_width((p)->popup,(w)) diff --git a/render/image.c b/render/image.c index fa630b74..d22ef9e3 100644 --- a/render/image.c +++ b/render/image.c @@ -20,6 +20,7 @@ #include "geom.h" #include "image.h" #include "color.h" +#include "imagecache.h" #include @@ -27,13 +28,91 @@ #define FLOOR(i) ((i) & (~0UL << FRACTION)) #define AVERAGE(a, b) (((((a) ^ (b)) & 0xfefefefeL) >> 1) + ((a) & (b))) -static void ImageCopyResampled(RrPixel32 *dst, RrPixel32 *src, - gulong dstW, gulong dstH, - gulong srcW, gulong srcH) +static void AddPicture(RrImage *self, RrImagePic ***list, gint *len, + RrImagePic *pic) { + gint i; + + g_assert(pic->width > 0 && pic->height > 0); + + g_assert(g_hash_table_lookup(self->cache->table, pic) == NULL); + + /* grow the list */ + *list = g_renew(RrImagePic*, *list, ++*len); + + /* move everything else down one */ + for (i = *len-1; i > 0; --i) + (*list)[i] = (*list)[i-1]; + + /* set the new picture up at the front of the list */ + (*list)[0] = pic; + + /* add the picture as a key to point to this image in the cache */ + g_hash_table_insert(self->cache->table, (*list)[0], self); + +#ifdef DEBUG + g_print("Adding %s picture to the cache: " + "Image 0x%x, w %d h %d Hash %u\n", + (*list == self->original ? "ORIGINAL" : "RESIZED"), + (guint)self, pic->width, pic->height, RrImagePicHash(pic)); +#endif +} + +static void RemovePicture(RrImage *self, RrImagePic ***list, + gint i, gint *len) +{ + gint j; + +#ifdef DEBUG + g_print("Removing %s picture from the cache: " + "Image 0x%x, w %d h %d Hash %u\n", + (*list == self->original ? "ORIGINAL" : "RESIZED"), + (guint)self, (*list)[i]->width, (*list)[i]->height, + RrImagePicHash((*list)[i])); +#endif + + /* remove the picture as a key in the cache */ + g_hash_table_remove(self->cache->table, (*list)[i]); + + /* free the picture (and its rgba data) */ + g_free((*list)[i]); + g_free((*list)[i]->data); + /* shift everything down one */ + for (j = i; j < *len-1; ++j) + (*list)[j] = (*list)[j+1]; + /* shrink the list */ + *list = g_renew(RrImagePic*, *list, --*len); +} + +static RrImagePic* ResizeImage(RrPixel32 *src, + gulong srcW, gulong srcH, + gulong dstW, gulong dstH) +{ + RrPixel32 *dst; + RrImagePic *pic; gulong dstX, dstY, srcX, srcY; gulong srcX1, srcX2, srcY1, srcY2; gulong ratioX, ratioY; + gulong aspectW, aspectH; + + /* keep the aspect ratio */ + aspectW = dstW; + aspectH = (gint)(dstW * ((gdouble)srcH / srcW)); + if (aspectH > dstH) { + aspectH = dstH; + aspectW = (gint)(dstH * ((gdouble)srcW / srcH)); + } + dstW = aspectW; + dstH = aspectH; + + if (srcW == dstW && srcH == dstH) + return NULL; /* no scaling needed ! */ + + pic = g_new(RrImagePic, 1); + dst = g_new(RrPixel32, dstW * dstH); + pic->width = dstW; + pic->height = dstH; + pic->data = dst; ratioX = (srcW << FRACTION) / dstW; ratioY = (srcH << FRACTION) / dstH; @@ -104,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); +} diff --git a/render/image.h b/render/image.h index 1c535960..28f29c2c 100644 --- a/render/image.h +++ b/render/image.h @@ -22,8 +22,11 @@ #include "render.h" #include "geom.h" -void RrImageDraw(RrPixel32 *target, RrTextureRGBA *rgba, - gint target_w, gint target_h, - RrRect *area); +void RrImageDrawImage(RrPixel32 *target, RrTextureImage *img, + gint target_w, gint target_h, + RrRect *area); +void RrImageDrawRGBA(RrPixel32 *target, RrTextureRGBA *rgba, + gint target_w, gint target_h, + RrRect *area); #endif diff --git a/render/imagecache.c b/render/imagecache.c new file mode 100644 index 00000000..ec2ff4d0 --- /dev/null +++ b/render/imagecache.c @@ -0,0 +1,149 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + imagecache.c for the Openbox window manager + Copyright (c) 2008 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "render.h" +#include "imagecache.h" + +static gboolean RrImagePicEqual(const RrImagePic *p1, + const RrImagePic *p2); + +RrImageCache* RrImageCacheNew() +{ + RrImageCache *self; + + self = g_new(RrImageCache, 1); + self->ref = 1; + self->table = g_hash_table_new((GHashFunc)RrImagePicHash, + (GEqualFunc)RrImagePicEqual); + return self; +} + +void RrImageCacheRef(RrImageCache *self) +{ + ++self->ref; +} + +void RrImageCacheUnref(RrImageCache *self) +{ + if (self && --self->ref == 0) { + g_assert(g_hash_table_size(self->table) == 0); + g_hash_table_unref(self->table); + + g_free(self); + } +} + +/*! Finds an image in the cache, if it is already in there */ +RrImage* RrImageCacheFind(RrImageCache *self, + RrPixel32 *data, gint w, gint h) +{ + RrImagePic pic; + pic.width = w; + pic.height = h; + pic.data = data; + return g_hash_table_lookup(self->table, &pic); +} + +/* This is a fast, reversable hash function called "lookup3", found here: + http://burtleburtle.net/bob/c/lookup3.c +*/ +#define hashsize(n) ((RrPixel32)1<<(n)) +#define hashmask(n) (hashsize(n)-1) +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) +/* mix -- mix 3 32-bit values reversibly. */ +#define mix(a,b,c) \ +{ \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} +/* final -- final mixing of 3 32-bit values (a,b,c) into c */ +#define final(a,b,c) \ +{ \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ +} + +guint32 hashword(const guint32 *key, gint length, guint32 initval) +{ + guint32 a,b,c; + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + (((guint32)length)<<2) + initval; + + /* handle most of the key */ + while (length > 3) + { + a += key[0]; + b += key[1]; + c += key[2]; + mix(a,b,c); + length -= 3; + key += 3; + } + + /* handle the last 3 guint32's */ + switch(length) /* all the case statements fall through */ + { + case 3: c+=key[2]; + case 2: b+=key[1]; + case 1: a+=key[0]; + final(a,b,c); + case 0: /* case 0: nothing left to add */ + break; + } + /* report the result */ + return c; +} + +#define HASH_INITVAL 0xf00d + +guint RrImagePicHash(const RrImagePic *p) +{ + return hashword(p->data, p->width * p->height, HASH_INITVAL); +} + +static gboolean RrImagePicEqual(const RrImagePic *p1, + const RrImagePic *p2) +{ + guint s1, s2; + RrPixel32 *data1, *data2; + gint i; + + if (p1->width != p2->width || p1->height != p2->height) return FALSE; + + /* strcmp() would probably suck on 4k of data.. sum all their values and + see if they get the same thing. they already matched on their hashes + at this point. */ + s1 = s2 = 0; + data1 = p1->data; + data2 = p2->data; + for (i = 0; i < p1->width * p1->height; ++i, ++data1) + s1 += *data1; + for (i = 0; i < p2->width * p2->height; ++i, ++data2) + s2 += *data2; + return s1 == s2; +} diff --git a/render/imagecache.h b/render/imagecache.h new file mode 100644 index 00000000..8c96caf0 --- /dev/null +++ b/render/imagecache.h @@ -0,0 +1,37 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + imagecache.h for the Openbox window manager + Copyright (c) 2008 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __imagecache_h +#define __imagecache_h + +#include + +/* the number of resized pictures to cache for an image */ +#define MAX_CACHE_RESIZED 3 + +struct _RrImagePic; + +guint RrImagePicHash(const struct _RrImagePic *p); + +struct _RrImageCache { + gint ref; + + GHashTable *table; +}; + +#endif diff --git a/render/render.c b/render/render.c index b81e80a6..fd442d26 100644 --- a/render/render.c +++ b/render/render.c @@ -130,6 +130,24 @@ Pixmap RrPaintPixmap(RrAppearance *a, gint w, gint h) } RrPixmapMaskDraw(a->pixmap, &a->texture[i].data.mask, &tarea); break; + case RR_TEXTURE_IMAGE: + g_assert(!transferred); + { + RrRect narea = tarea; + RrTextureImage *img = &a->texture[i].data.image; + if (img->twidth) + narea.width = MIN(tarea.width, img->twidth); + if (img->theight) + narea.height = MIN(tarea.height, img->theight); + narea.x += img->tx; + narea.y += img->ty; + RrImageDrawImage(a->surface.pixel_data, + &a->texture[i].data.image, + a->w, a->h, + &narea); + } + force_transfer = 1; + break; case RR_TEXTURE_RGBA: g_assert(!transferred); { @@ -141,10 +159,10 @@ Pixmap RrPaintPixmap(RrAppearance *a, gint w, gint h) narea.height = MIN(tarea.height, rgb->theight); narea.x += rgb->tx; narea.y += rgb->ty; - RrImageDraw(a->surface.pixel_data, - &a->texture[i].data.rgba, - a->w, a->h, - &narea); + RrImageDrawRGBA(a->surface.pixel_data, + &a->texture[i].data.rgba, + a->w, a->h, + &narea); } force_transfer = 1; break; @@ -202,11 +220,15 @@ void RrAppearanceAddTextures(RrAppearance *a, gint numtex) if (numtex) a->texture = g_new0(RrTexture, numtex); } +void RrAppearanceClearTextures(RrAppearance *a) +{ + memset(a->texture, 0, a->textures * sizeof(RrTexture)); +} + RrAppearance *RrAppearanceCopy(RrAppearance *orig) { RrSurface *spo, *spc; RrAppearance *copy = g_new(RrAppearance, 1); - gint i; copy->inst = orig->inst; @@ -282,10 +304,6 @@ RrAppearance *RrAppearanceCopy(RrAppearance *orig) copy->textures = orig->textures; copy->texture = g_memdup(orig->texture, orig->textures * sizeof(RrTexture)); - for (i = 0; i < copy->textures; ++i) - if (copy->texture[i].type == RR_TEXTURE_RGBA) { - copy->texture[i].data.rgba.cache = NULL; - } copy->pixmap = None; copy->xftdraw = NULL; copy->w = copy->h = 0; @@ -294,17 +312,10 @@ RrAppearance *RrAppearanceCopy(RrAppearance *orig) void RrAppearanceFree(RrAppearance *a) { - gint i; - if (a) { RrSurface *p; if (a->pixmap != None) XFreePixmap(RrDisplay(a->inst), a->pixmap); if (a->xftdraw != NULL) XftDrawDestroy(a->xftdraw); - for (i = 0; i < a->textures; ++i) - if (a->texture[i].type == RR_TEXTURE_RGBA) { - g_free(a->texture[i].data.rgba.cache); - a->texture[i].data.rgba.cache = NULL; - } if (a->textures) g_free(a->texture); p = &a->surface; @@ -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)); diff --git a/render/render.h b/render/render.h index 10eafc6e..e79727df 100644 --- a/render/render.h +++ b/render/render.h @@ -37,11 +37,15 @@ typedef struct _RrFont RrFont; typedef struct _RrTexture RrTexture; typedef struct _RrTextureMask RrTextureMask; typedef struct _RrTextureRGBA RrTextureRGBA; +typedef struct _RrTextureImage RrTextureImage; typedef struct _RrTextureText RrTextureText; typedef struct _RrTextureLineArt RrTextureLineArt; typedef struct _RrPixmapMask RrPixmapMask; typedef struct _RrInstance RrInstance; typedef struct _RrColor RrColor; +typedef struct _RrImage RrImage; +typedef struct _RrImagePic RrImagePic; +typedef struct _RrImageCache RrImageCache; typedef guint32 RrPixel32; typedef guint16 RrPixel16; @@ -76,7 +80,8 @@ typedef enum { RR_TEXTURE_MASK, RR_TEXTURE_TEXT, RR_TEXTURE_LINE_ART, - RR_TEXTURE_RGBA + RR_TEXTURE_RGBA, + RR_TEXTURE_IMAGE } RrTextureType; typedef enum { @@ -163,11 +168,19 @@ struct _RrTextureRGBA { gint height; gint alpha; RrPixel32 *data; -/* cached scaled so we don't have to scale often */ - gint cwidth; - gint cheight; - RrPixel32 *cache; -/* size and position to draw at */ + /* size and position to draw at (if these are zero, then it will be + drawn to fill the entire texture */ + gint tx; + gint ty; + gint twidth; + gint theight; +}; + +struct _RrTextureImage { + RrImage *image; + gint alpha; + /* size and position to draw at (if these are zero, then it will be + drawn to fill the entire texture */ gint tx; gint ty; gint twidth; @@ -184,12 +197,15 @@ struct _RrTextureLineArt { union _RrTextureData { RrTextureRGBA rgba; + RrTextureImage image; RrTextureText text; RrTextureMask mask; RrTextureLineArt lineart; }; struct _RrTexture { + /* If changing the type of a texture, you should DEFINITELY call + RrAppearanceClearTextures() first! */ RrTextureType type; RrTextureData data; }; @@ -207,6 +223,25 @@ struct _RrAppearance { gint w, h; }; +/*! Holds a RGBA image picture */ +struct _RrImagePic { + gint width, height; + RrPixel32 *data; +}; + +/*! An RrImage is a sort of meta-image. It can contain multiple versions of + an image at different sizes, which may or may not be completely different + pictures */ +struct _RrImage { + gint ref; + struct _RrImageCache *cache; + + struct _RrImagePic **original; + gint n_original; + struct _RrImagePic **resized; + gint n_resized; +}; + /* these are the same on all endian machines because it seems to be dependant on the endianness of the gfx card, not the cpu. */ #define RrDefaultAlphaOffset 24 @@ -253,6 +288,8 @@ RrAppearance *RrAppearanceCopy (RrAppearance *a); void RrAppearanceFree (RrAppearance *a); void RrAppearanceRemoveTextures(RrAppearance *a); void RrAppearanceAddTextures(RrAppearance *a, gint numtex); +/*! Always call this when changing the type of a texture in an appearance */ +void RrAppearanceClearTextures(RrAppearance *a); RrFont *RrFontOpen (const RrInstance *inst, const gchar *name, gint size, RrFontWeight weight, RrFontSlant slant); @@ -280,6 +317,21 @@ gboolean RrPixmapToRGBA(const RrInstance *inst, Pixmap pmap, Pixmap mask, gint *w, gint *h, RrPixel32 **data); +RrImageCache* RrImageCacheNew(); +void RrImageCacheRef(RrImageCache *self); +void RrImageCacheUnref(RrImageCache *self); + +/*! Finds an image in the cache, if it is already in there */ +RrImage* RrImageCacheFind(RrImageCache *self, + RrPixel32 *data, gint w, gint h); + +RrImage* RrImageNew(RrImageCache *cache); +void RrImageRef(RrImage *im); +void RrImageUnref(RrImage *im); + +void RrImageAddPicture(RrImage *im, RrPixel32 *data, gint w, gint h); +void RrImageRemovePicture(RrImage *im, gint w, gint h); + G_END_DECLS #endif /*__render_h*/ diff --git a/render/theme.c b/render/theme.c index b1b15ff2..cd2352c3 100644 --- a/render/theme.c +++ b/render/theme.c @@ -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, diff --git a/render/theme.h b/render/theme.h index 3f87ce93..1940970a 100644 --- a/render/theme.h +++ b/render/theme.h @@ -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; diff --git a/tests/Makefile b/tests/Makefile index 6374b392..9fc5fe81 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -3,4 +3,4 @@ files=$(wildcard *.c) all: $(files:.c=) %: %.c - $(CC) $(CFLAGS) -o $@ $^ -lX11 -lXext -L/usr/X11R6/lib -I/usr/X11R6/include + $(CC) `pkg-config --cflags --libs glib-2.0` $(CFLAGS) -o $@ $^ -lX11 -lXext -L/usr/X11R6/lib -I/usr/X11R6/include diff --git a/tests/icons.c b/tests/icons.c index 315a10fb..af13e0b5 100644 --- a/tests/icons.c +++ b/tests/icons.c @@ -23,6 +23,7 @@ #include #include #include +#include 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); -- cgit v1.2.3 From 35b36fc3771452cf980cb0a59aa05f3e2aa2aa67 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Tue, 12 Feb 2008 22:29:23 -0500 Subject: when resizing images, pick a source image with the same aspect ratio if possible --- render/image.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/render/image.c b/render/image.c index d22ef9e3..8ce3a57c 100644 --- a/render/image.c +++ b/render/image.c @@ -346,7 +346,7 @@ void RrImageDrawImage(RrPixel32 *target, RrTextureImage *img, gint target_w, gint target_h, RrRect *area) { - gint i, min_diff, min_i; + gint i, min_diff, min_i, min_aspect_diff, min_aspect_i; RrImage *self; RrImagePic *pic; @@ -386,24 +386,43 @@ void RrImageDrawImage(RrPixel32 *target, RrTextureImage *img, } if (!pic) { + gdouble aspect; + /* find an original with a close size */ - min_diff = -1; - min_i = 0; + min_diff = min_aspect_diff = -1; + min_i = min_aspect_i = 0; + aspect = ((gdouble)area->width) / area->height; for (i = 0; i < self->n_original; ++i) { gint diff; gint wdiff, hdiff; + gdouble myasp; /* our size difference metric.. */ wdiff = self->original[i]->width - area->width; hdiff = self->original[i]->height - area->height; diff = (wdiff * wdiff) + (hdiff * hdiff); + /* find the smallest difference */ if (min_diff < 0 || diff < min_diff) { min_diff = diff; min_i = i; } + /* and also find the smallest difference with the same aspect + ratio (and prefer this one) */ + myasp = ((gdouble)self->original[i]->width) / + self->original[i]->height; + if (ABS(aspect - myasp) < 0.0000001 && + (min_aspect_diff < 0 || diff < min_aspect_diff)) + { + min_aspect_diff = diff; + min_aspect_i = i; + } } + /* use the aspect ratio correct source if there is one */ + if (min_aspect_i >= 0) + min_i = min_aspect_i; + /* resize the original to the given area */ pic = ResizeImage(self->original[min_i]->data, self->original[min_i]->width, -- cgit v1.2.3 From a2e3026d8a398a4d08c05610c3f652dd14fcdf45 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Wed, 13 Feb 2008 08:43:57 -0500 Subject: add a bunch of comments for images and image caches. and make the number of resized pictures saved in an image tunable per-icon cache. --- openbox/openbox.c | 6 +++++- render/image.c | 39 +++++++++++++++++++++++++++++++++------ render/imagecache.c | 18 ++++++++++++++---- render/imagecache.h | 20 +++++++++++++++++--- render/render.h | 18 ++++++++++++++---- 5 files changed, 83 insertions(+), 18 deletions(-) diff --git a/openbox/openbox.c b/openbox/openbox.c index f2dc7159..46da8fee 100644 --- a/openbox/openbox.c +++ b/openbox/openbox.c @@ -183,7 +183,11 @@ 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(); + /* Saving 3 resizes of an RrImage makes a lot of sense for icons, as there + are generally 3 icon sizes needed: the titlebar icon, the menu icon, + and the alt-tab icon + */ + ob_rr_icons = RrImageCacheNew(3); XSynchronize(ob_display, xsync); diff --git a/render/image.c b/render/image.c index 8ce3a57c..a618f782 100644 --- a/render/image.c +++ b/render/image.c @@ -28,6 +28,9 @@ #define FLOOR(i) ((i) & (~0UL << FRACTION)) #define AVERAGE(a, b) (((((a) ^ (b)) & 0xfefefefeL) >> 1) + ((a) & (b))) +/*! Add a picture to an Image, that is, add another copy of the image at + another size. This may add it to the "originals" list or to the + "resized" list. */ static void AddPicture(RrImage *self, RrImagePic ***list, gint *len, RrImagePic *pic) { @@ -58,6 +61,8 @@ static void AddPicture(RrImage *self, RrImagePic ***list, gint *len, #endif } +/*! Remove a picture from an Image. This may remove it from the "originals" + list or the "resized" list. */ static void RemovePicture(RrImage *self, RrImagePic ***list, gint i, gint *len) { @@ -84,9 +89,14 @@ static void RemovePicture(RrImage *self, RrImagePic ***list, *list = g_renew(RrImagePic*, *list, --*len); } +/*! Given a picture in RGBA format, of a specified size, resize it to the new + requested size (but keep its aspect ratio). If the image does not need to + be resized (it is already the right size) then this returns NULL. Otherwise + it returns a newly allocated RrImagePic with the resized picture inside it +*/ static RrImagePic* ResizeImage(RrPixel32 *src, - gulong srcW, gulong srcH, - gulong dstW, gulong dstH) + gulong srcW, gulong srcH, + gulong dstW, gulong dstH) { RrPixel32 *dst; RrImagePic *pic; @@ -199,6 +209,8 @@ void DrawRGBA(RrPixel32 *target, gint target_w, gint target_h, gint dw, dh; g_assert(source_w <= area->width && source_h <= area->height); + g_assert(area->x + area->width <= target_w); + g_assert(area->y + area->height <= target_h); /* keep the aspect ratio */ dw = area->width; @@ -246,6 +258,7 @@ void DrawRGBA(RrPixel32 *target, gint target_w, gint target_h, } } +/*! Draw an RGBA texture into a target pixel buffer. */ void RrImageDrawRGBA(RrPixel32 *target, RrTextureRGBA *rgba, gint target_w, gint target_h, RrRect *area) @@ -270,10 +283,13 @@ void RrImageDrawRGBA(RrPixel32 *target, RrTextureRGBA *rgba, rgba->alpha, area); } +/*! Create a new RrImage, which is linked to an image cache */ RrImage* RrImageNew(RrImageCache *cache) { RrImage *self; + g_assert(cache != NULL); + self = g_new0(RrImage, 1); self->ref = 1; self->cache = cache; @@ -300,6 +316,9 @@ void RrImageUnref(RrImage *self) } } +/*! Add a new picture with the given RGBA pixel data and dimensions into the + RrImage. This adds an "original" picture to the image. +*/ void RrImageAddPicture(RrImage *self, RrPixel32 *data, gint w, gint h) { gint i; @@ -330,6 +349,9 @@ void RrImageAddPicture(RrImage *self, RrPixel32 *data, gint w, gint h) AddPicture(self, &self->original, &self->n_original, pic); } +/*! Remove the picture from the RrImage which has the given dimensions. This + removes an "original" picture from the image. +*/ void RrImageRemovePicture(RrImage *self, gint w, gint h) { gint i; @@ -342,6 +364,11 @@ void RrImageRemovePicture(RrImage *self, gint w, gint h) } } +/*! Draw an RrImage texture into a target pixel buffer. If the RrImage does + not contain a picture of the appropriate size, then one of its "original" + pictures will be resized and used (and stored in the RrImage as a "resized" + picture). + */ void RrImageDrawImage(RrPixel32 *target, RrTextureImage *img, gint target_w, gint target_h, RrRect *area) @@ -431,13 +458,13 @@ void RrImageDrawImage(RrPixel32 *target, RrTextureImage *img, /* add the resized image to the image, as the first in the resized list */ - if (self->n_resized >= MAX_CACHE_RESIZED) { + if (self->n_resized >= self->cache->max_resized_saved) /* 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); + if (self->cache->max_resized_saved) + /* add it to the top of the resized list */ + AddPicture(self, &self->resized, &self->n_resized, pic); } g_assert(pic != NULL); diff --git a/render/imagecache.c b/render/imagecache.c index ec2ff4d0..9ebaec13 100644 --- a/render/imagecache.c +++ b/render/imagecache.c @@ -22,12 +22,15 @@ static gboolean RrImagePicEqual(const RrImagePic *p1, const RrImagePic *p2); -RrImageCache* RrImageCacheNew() +RrImageCache* RrImageCacheNew(gint max_resized_saved) { RrImageCache *self; + g_assert(max_resized_saved >= 0); + self = g_new(RrImageCache, 1); self->ref = 1; + self->max_resized_saved = max_resized_saved; self->table = g_hash_table_new((GHashFunc)RrImagePicHash, (GEqualFunc)RrImagePicEqual); return self; @@ -59,9 +62,6 @@ RrImage* RrImageCacheFind(RrImageCache *self, 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)))) @@ -87,6 +87,13 @@ RrImage* RrImageCacheFind(RrImageCache *self, c ^= b; c -= rot(b,24); \ } +/* This is a fast, reversable hash function called "lookup3", found here: + http://burtleburtle.net/bob/c/lookup3.c, by Bob Jenkins + + This hashing algorithm is "reversible", that is, not cryptographically + secure at all. But we don't care about that, we just want something to + tell when images are the same or different relatively quickly. +*/ guint32 hashword(const guint32 *key, gint length, guint32 initval) { guint32 a,b,c; @@ -119,6 +126,9 @@ guint32 hashword(const guint32 *key, gint length, guint32 initval) return c; } +/*! This is some arbitrary initial value for the hashing function. It's + constant so that you get the same result from the same data each time. +*/ #define HASH_INITVAL 0xf00d guint RrImagePicHash(const RrImagePic *p) diff --git a/render/imagecache.h b/render/imagecache.h index 8c96caf0..4ad2deae 100644 --- a/render/imagecache.h +++ b/render/imagecache.h @@ -21,15 +21,29 @@ #include -/* the number of resized pictures to cache for an image */ -#define MAX_CACHE_RESIZED 3 - struct _RrImagePic; guint RrImagePicHash(const struct _RrImagePic *p); +/*! Create a new image cache. An image cache is basically a hash table to look + up RrImages. Each RrImage in the cache may contain one or more Pictures, + that is one or more actual copies of image data at various sizes. For eg, + for a window, all of its various icons are loaded into the same RrImage. + When an RrImage is drawn and a picture inside it needs to be resized, that + is also saved within the RrImage. + + For each picture that an RrImage has, the picture is hashed and that is used + as a key to find the RrImage. So, given any picture in any RrImage in the + cache, if you hash it, you will find the RrImage. +*/ struct _RrImageCache { gint ref; + /*! When an original picture is resized for an RrImage, the resized picture + is saved in the RrImage. This specifies how many pictures should be + saved at a time. When this is exceeded, the least recently used + "resized" picture is deleted. + */ + gint max_resized_saved; GHashTable *table; }; diff --git a/render/render.h b/render/render.h index e79727df..7053664d 100644 --- a/render/render.h +++ b/render/render.h @@ -234,11 +234,18 @@ struct _RrImagePic { pictures */ struct _RrImage { gint ref; - struct _RrImageCache *cache; + RrImageCache *cache; - struct _RrImagePic **original; + /*! An array of "originals", that is of RrPictures that have been added + to the image in various sizes, and that have not been resized. These + are explicitly added to the RrImage. */ + RrImagePic **original; gint n_original; - struct _RrImagePic **resized; + /*! An array of "resized" pictures. When an "original" RrPicture + needs to be resized for drawing, it is saved in here so that it doesn't + need to be resized again. These are automatically added to the + RrImage. */ + RrImagePic **resized; gint n_resized; }; @@ -317,7 +324,10 @@ gboolean RrPixmapToRGBA(const RrInstance *inst, Pixmap pmap, Pixmap mask, gint *w, gint *h, RrPixel32 **data); -RrImageCache* RrImageCacheNew(); +/*! Create a new image cache for RrImages. + @param max_resized_saved The number of resized copies of an image to save +*/ +RrImageCache* RrImageCacheNew(gint max_resized_saved); void RrImageCacheRef(RrImageCache *self); void RrImageCacheUnref(RrImageCache *self); -- cgit v1.2.3 From 38c96413b375516a25086cf169d2d3cb7c6075f7 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Wed, 13 Feb 2008 09:14:58 -0500 Subject: pre-calc the sum of a picture added to an RrImage rather than calculating it every time --- render/image.c | 27 ++++++++++++++++++--------- render/image.h | 3 +++ render/imagecache.c | 25 +++++-------------------- render/render.h | 3 +++ 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/render/image.c b/render/image.c index a618f782..a374ceb4 100644 --- a/render/image.c +++ b/render/image.c @@ -28,6 +28,18 @@ #define FLOOR(i) ((i) & (~0UL << FRACTION)) #define AVERAGE(a, b) (((((a) ^ (b)) & 0xfefefefeL) >> 1) + ((a) & (b))) +void RrImagePicInit(RrImagePic *pic, gint w, gint h, RrPixel32 *data) +{ + gint i; + + pic->width = w; + pic->height = h; + pic->data = data; + pic->sum = 0; + for (i = w*h; i > 0; --i) + pic->sum += *(data++); +} + /*! Add a picture to an Image, that is, add another copy of the image at another size. This may add it to the "originals" list or to the "resized" list. */ @@ -98,7 +110,7 @@ static RrImagePic* ResizeImage(RrPixel32 *src, gulong srcW, gulong srcH, gulong dstW, gulong dstH) { - RrPixel32 *dst; + RrPixel32 *dst, *dststart; RrImagePic *pic; gulong dstX, dstY, srcX, srcY; gulong srcX1, srcX2, srcY1, srcY2; @@ -118,11 +130,7 @@ static RrImagePic* ResizeImage(RrPixel32 *src, 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; + dststart = dst = g_new(RrPixel32, dstW * dstH); ratioX = (srcW << FRACTION) / dstW; ratioY = (srcH << FRACTION) / dstH; @@ -194,6 +202,9 @@ static RrImagePic* ResizeImage(RrPixel32 *src, } } + pic = g_new(RrImagePic, 1); + RrImagePicInit(pic, dstW, dstH, dststart); + return pic; } @@ -343,9 +354,7 @@ void RrImageAddPicture(RrImage *self, RrPixel32 *data, gint w, gint h) /* add the new picture */ pic = g_new(RrImagePic, 1); - pic->width = w; - pic->height = h; - pic->data = g_memdup(data, w*h*sizeof(RrPixel32)); + RrImagePicInit(pic, w, h, g_memdup(data, w*h*sizeof(RrPixel32))); AddPicture(self, &self->original, &self->n_original, pic); } diff --git a/render/image.h b/render/image.h index 28f29c2c..b478daf9 100644 --- a/render/image.h +++ b/render/image.h @@ -22,6 +22,9 @@ #include "render.h" #include "geom.h" +/*! Initialize an RrImagePicture to the specified dimensions and pixel data */ +void RrImagePicInit(RrImagePic *pic, gint w, gint h, RrPixel32 *data); + void RrImageDrawImage(RrPixel32 *target, RrTextureImage *img, gint target_w, gint target_h, RrRect *area); diff --git a/render/imagecache.c b/render/imagecache.c index 9ebaec13..9c605f9d 100644 --- a/render/imagecache.c +++ b/render/imagecache.c @@ -18,6 +18,7 @@ #include "render.h" #include "imagecache.h" +#include "image.h" static gboolean RrImagePicEqual(const RrImagePic *p1, const RrImagePic *p2); @@ -56,9 +57,8 @@ RrImage* RrImageCacheFind(RrImageCache *self, RrPixel32 *data, gint w, gint h) { RrImagePic pic; - pic.width = w; - pic.height = h; - pic.data = data; + + RrImagePicInit(&pic, w, h, data); return g_hash_table_lookup(self->table, &pic); } @@ -139,21 +139,6 @@ guint RrImagePicHash(const RrImagePic *p) 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; + return p1->width == p2->width && p1->height == p2->height && + p1->sum == p2->sum; } diff --git a/render/render.h b/render/render.h index 7053664d..f7bc5041 100644 --- a/render/render.h +++ b/render/render.h @@ -227,6 +227,9 @@ struct _RrAppearance { struct _RrImagePic { gint width, height; RrPixel32 *data; + /* The sum of all the pixels. This is used to compare pictures if their + hashes match. */ + gint sum; }; /*! An RrImage is a sort of meta-image. It can contain multiple versions of -- cgit v1.2.3 From e7a86f5fe47906ef845af83fc419dc540cf19daf Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Wed, 13 Feb 2008 10:28:56 -0500 Subject: use g_message not g_print so it can go to the log --- render/image.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/render/image.c b/render/image.c index a374ceb4..d0633d94 100644 --- a/render/image.c +++ b/render/image.c @@ -66,10 +66,10 @@ static void AddPicture(RrImage *self, RrImagePic ***list, gint *len, 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)); + g_message("Adding %s picture to the cache:\n " + "Image 0x%x, w %d h %d Hash %u\n", + (*list == self->original ? "ORIGINAL" : "RESIZED"), + (guint)self, pic->width, pic->height, RrImagePicHash(pic)); #endif } @@ -81,11 +81,11 @@ static void RemovePicture(RrImage *self, RrImagePic ***list, 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])); + g_message("Removing %s picture from the cache:\n " + "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 */ @@ -316,7 +316,7 @@ void RrImageUnref(RrImage *self) { if (self && --self->ref == 0) { #ifdef DEBUG - g_print("Refcount to 0, removing ALL pictures from the cache: " + g_message("Refcount to 0, removing ALL pictures from the cache:\n " "Image 0x%x\n", (guint)self); #endif while (self->n_original > 0) @@ -339,8 +339,8 @@ void RrImageAddPicture(RrImage *self, RrPixel32 *data, gint w, gint h) 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); + g_message("Found duplicate ORIGINAL image:\n " + "Image 0x%x, w %d h %d\n", (guint)self, w, h); #endif return; } -- cgit v1.2.3 From 66a897d9b24682e9d243e4d452800b8a5c1237f6 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Wed, 13 Feb 2008 21:30:18 -0500 Subject: don't reference free'd memory, and don't leak memory if the image cache is not saving any resized pictures --- render/image.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/render/image.c b/render/image.c index d0633d94..c7452fce 100644 --- a/render/image.c +++ b/render/image.c @@ -40,6 +40,14 @@ void RrImagePicInit(RrImagePic *pic, gint w, gint h, RrPixel32 *data) pic->sum += *(data++); } +static void RrImagePicFree(RrImagePic *pic) +{ + if (pic) { + g_free(pic->data); + g_free(pic); + } +} + /*! Add a picture to an Image, that is, add another copy of the image at another size. This may add it to the "originals" list or to the "resized" list. */ @@ -91,9 +99,8 @@ static void RemovePicture(RrImage *self, RrImagePic ***list, /* 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); + /* free the picture */ + RrImagePicFree((*list)[i]); /* shift everything down one */ for (j = i; j < *len-1; ++j) (*list)[j] = (*list)[j+1]; @@ -385,9 +392,11 @@ void RrImageDrawImage(RrPixel32 *target, RrTextureImage *img, gint i, min_diff, min_i, min_aspect_diff, min_aspect_i; RrImage *self; RrImagePic *pic; + gboolean free_pic; self = img->image; pic = NULL; + free_pic = FALSE; /* is there an original of this size? (only w or h has to be right cuz we maintain aspect ratios) */ @@ -474,6 +483,8 @@ void RrImageDrawImage(RrPixel32 *target, RrTextureImage *img, if (self->cache->max_resized_saved) /* add it to the top of the resized list */ AddPicture(self, &self->resized, &self->n_resized, pic); + else + free_pic = TRUE; /* don't leak mem! */ } g_assert(pic != NULL); @@ -481,4 +492,6 @@ void RrImageDrawImage(RrPixel32 *target, RrTextureImage *img, DrawRGBA(target, target_w, target_h, pic->data, pic->width, pic->height, img->alpha, area); + if (free_pic) + RrImagePicFree(pic); } -- cgit v1.2.3