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