/* pixmap-cache.c -- Caching pixmaps $Id: pixmap-cache.c,v 1.3 2000/11/07 00:42:32 jsh Exp $ Copyright (C) 2000 John Harper This file is part of sawmill. sawmill 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, or (at your option) any later version. sawmill 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. You should have received a copy of the GNU General Public License along with sawmill; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Define this to disable all pixmap caching */ #undef DISABLE_CACHE /* Commentary: This file provides the pixmap cache, a mapping from (image, width, height) tuples to rendered pixmaps (both image and mask). This is only required when not using Imlib (Imlib has a built-in cache). It's implemented as two doubly-linked lists, each containing the same component nodes, but one list chains all pixmaps associated with a particular image object, the other chains all pixmaps in order of allocation/use (so that the oldest unused node can be discarded quickly when the cache overflows its upper bound). XXX Need to measure average length of image lists; if too long XXX they should be changed to doubly-linked hash tables. */ #include "sawmill.h" static u_long cached_pixels, max_cached_pixels = 64 * 1024; static u_long hits, misses; #ifdef NEED_PIXMAP_CACHE struct pixmap_cache_node_struct { pixmap_cache_node *next, *pred; pixmap_cache_node *newer, *older; Lisp_Image *im; int width, height; Pixmap p1, p2; int ref_count; }; static pixmap_cache_node *oldest, *newest; /* list manipulators */ static void remove_from_age_list (pixmap_cache_node *n) { if (n->newer != 0) n->newer->older = n->older; else newest = n->older; if (n->older != 0) n->older->newer = n->newer; else oldest = n->newer; } static void prepend_to_age_list (pixmap_cache_node *n) { n->newer = oldest; if (n->newer != 0) n->newer->older = n; oldest = n; n->older = 0; if (newest == 0) newest = n; } static void remove_from_image (pixmap_cache_node *n) { if (n->next != 0) n->next->pred = n->pred; else n->im->pixmap_last = n->pred; if (n->pred != 0) n->pred->next = n->next; else n->im->pixmap_first = n->next; } static void prepend_to_image (pixmap_cache_node *n) { Lisp_Image *im = n->im; n->next = im->pixmap_first; if (n->next != 0) n->next->pred = n; im->pixmap_first = n; n->pred = 0; if (im->pixmap_last == 0) im->pixmap_last = n; } static void free_node (pixmap_cache_node *n, bool dealloc) { if (n->p1 != 0) XFreePixmap (dpy, n->p1); if (n->p2 != 0) XFreePixmap (dpy, n->p2); if (dealloc) rep_free (n); } static void delete_node (pixmap_cache_node *n, bool dealloc) { remove_from_image (n); remove_from_age_list (n); cached_pixels -= n->width * n->height; free_node (n, dealloc); } /* public interface */ bool pixmap_cache_ref (Lisp_Image *im, int width, int height, Pixmap *p1, Pixmap *p2) { pixmap_cache_node *n; for (n = im->pixmap_first; n != 0; n = n->next) { if (n->width == width && n->height == height) { remove_from_image (n); prepend_to_image (n); remove_from_age_list (n); prepend_to_age_list (n); n->ref_count++; *p1 = n->p1; *p2 = n->p2; hits++; return TRUE; } } misses++; return FALSE; } void pixmap_cache_unref (Lisp_Image *im, Pixmap p1, Pixmap p2) { pixmap_cache_node *n; for (n = im->pixmap_first; n != 0; n = n->next) { if (n->p1 == p1 && n->p2 == p2) { n->ref_count--; #ifdef DISABLE_CACHE if (n->ref_count == 0) delete_node (n, TRUE); #endif return; } } fprintf (stderr, "warning: unref'ing unknown image in pixmap-cache\n"); } void pixmap_cache_set (Lisp_Image *im, int width, int height, Pixmap p1, Pixmap p2) { int pixel_count = width * height; pixmap_cache_node *n = 0; while (pixel_count + cached_pixels > max_cached_pixels) { /* remove oldest node */ pixmap_cache_node *this = oldest; while (this != 0 && this->ref_count > 0) this = this->newer; if (this == 0) break; delete_node (this, n != 0); if (n == 0) n = this; } if (n == 0) n = rep_alloc (sizeof (pixmap_cache_node)); n->im = im; n->width = width; n->height = height; n->p1 = p1; n->p2 = p2; n->ref_count = 1; prepend_to_image (n); prepend_to_age_list (n); cached_pixels += pixel_count; } void pixmap_cache_flush_image (Lisp_Image *im) { pixmap_cache_node *n, *next; for (n = im->pixmap_first; n != 0; n = next) { next = n->next; remove_from_age_list (n); free_node (n, TRUE); } im->pixmap_first = im->pixmap_last = 0; } #endif /* NEED_PIXMAP_CACHE */ DEFUN ("pixmap-cache-control", Fpixmap_cache_control, Spixmap_cache_control, (repv max), rep_Subr1) { if (rep_INTP (max) && rep_INT (max) > 0) max_cached_pixels = rep_INT (max); return rep_list_4 (rep_MAKE_INT (max_cached_pixels), rep_MAKE_INT (cached_pixels), rep_MAKE_INT (hits), rep_MAKE_INT (misses)); } void pixmap_cache_init (void) { rep_ADD_SUBR (Spixmap_cache_control); }