/* 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 <john@dcs.warwick.ac.uk>
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);
}
syntax highlighted by Code2HTML, v. 0.9.1