1 /* imgcache.c
2  * Image cache
3  * (c) 2002 Mikulas Patocka
4  * This file is a part of the Links program, released under GPL.
5  */
6 
7 #include "cfg.h"
8 
9 #ifdef G
10 
11 #include "links.h"
12 
13 static struct list_head image_cache_ref = { &image_cache_ref, &image_cache_ref };
14 static struct list_head image_cache_deref = { &image_cache_deref, &image_cache_deref };
15 
16 #define CACHED_IMAGE_HASH_SIZE	1024
17 static struct cached_image *image_hash[CACHED_IMAGE_HASH_SIZE];
18 
hash_image(int bg,unsigned char * url,int xw,int yw,int xyw_meaning)19 static unsigned hash_image(int bg, unsigned char *url, int xw, int yw, int xyw_meaning)
20 {
21 	unsigned hash = hash_string(url);
22 	hash = hash * 0x55 + bg;
23 	hash = hash * 0x55 + xw;
24 	hash = hash * 0x55 + yw;
25 	hash = hash * 0x55 + xyw_meaning;
26 	return hash % CACHED_IMAGE_HASH_SIZE;
27 }
28 
29 /* xyw_meaning either MEANING_DIMS or MEANING_AUTOSCALE. */
find_cached_image(int bg,unsigned char * url,int xw,int yw,int xyw_meaning,int scale,unsigned aspect)30 struct cached_image *find_cached_image(int bg, unsigned char *url, int xw, int yw, int xyw_meaning, int scale, unsigned aspect)
31 {
32 	struct cached_image *i;
33 	unsigned hash = hash_image(bg, url, xw, yw, xyw_meaning);
34 	if (xw >= 0 && yw >= 0 && xyw_meaning == MEANING_DIMS) {
35 		/* The xw and yw is already scaled so that scale and
36 		 * aspect don't matter.
37 		 */
38 		for (i = image_hash[hash]; i; i = i->hash_fwdlink) {
39 			if (i->background_color == bg
40 				&& !strcmp(cast_const_char i->url, cast_const_char url)
41 				&& i->wanted_xw == xw
42 				&& i->wanted_yw == yw
43 				&& i->wanted_xyw_meaning == xyw_meaning
44 				) goto hit;
45 		}
46 	} else {
47 		for (i = image_hash[hash]; i; i = i->hash_fwdlink) {
48 			if (i->background_color == bg
49 				&& !strcmp(cast_const_char i->url, cast_const_char url)
50 				&& i->wanted_xw == xw
51 				&& i->wanted_yw == yw
52 				&& i->wanted_xyw_meaning == xyw_meaning
53 				&& i->scale == scale
54 				&& i->aspect == aspect
55 				) goto hit;
56 		}
57 	}
58 	return NULL;
59 
60 hit:
61 	i->cimg_refcount++;
62 	del_from_list(i);
63 	add_to_list(image_cache_ref, i);
64 	return i;
65 }
66 
add_image_to_cache(struct cached_image * ci)67 void add_image_to_cache(struct cached_image *ci)
68 {
69 	unsigned hash = hash_image(ci->background_color, ci->url, (int)ci->wanted_xw, (int)ci->wanted_yw, ci->wanted_xyw_meaning);
70 	ci->hash_fwdlink = image_hash[hash];
71 	if (ci->hash_fwdlink)
72 		ci->hash_fwdlink->hash_backlink = &ci->hash_fwdlink;
73 	ci->hash_backlink = &image_hash[hash];
74 	image_hash[hash] = ci;
75 	ci->cimg_refcount = 1;
76 	add_to_list(image_cache_ref, ci);
77 }
78 
deref_cached_image(struct cached_image * ci)79 void deref_cached_image(struct cached_image *ci)
80 {
81 	if (ci->cimg_refcount <= 0)
82 		internal_error("deref_cached_image: refcount underflow: %d", ci->cimg_refcount);
83 	ci->cimg_refcount--;
84 	if (!ci->cimg_refcount) {
85 		del_from_list(ci);
86 		add_to_list(image_cache_deref, ci);
87 	}
88 }
89 
image_size(struct cached_image * cimg)90 static unsigned long image_size(struct cached_image *cimg)
91 {
92 	unsigned long siz = sizeof(struct cached_image);
93 	switch (cimg->state) {
94 		case 0:
95 		case 1:
96 		case 2:
97 		case 3:
98 		case 8:
99 		case 9:
100 		case 10:
101 		case 11:
102 		break;
103 
104 		case 12:
105 		case 14:
106 		siz += (unsigned long)cimg->width * cimg->height * cimg->buffer_bytes_per_pixel;
107 		if (cimg->bmp_used){
108 			case 13:
109 			case 15:
110 			siz += (unsigned long)cimg->bmp.x * cimg->bmp.y * (drv->depth & 7);
111 		}
112 		break;
113 
114 #ifdef DEBUG
115 		default:
116 		fprintf(stderr, "cimg->state=%d\n", cimg->state);
117 		internal_error("Invalid cimg->state in image_size\n");
118 		break;
119 #endif /* #ifdef DEBUG */
120 	}
121 	return siz;
122 }
123 
shrink_image_cache(int u)124 static int shrink_image_cache(int u)
125 {
126 	struct cached_image *i;
127 	struct list_head *li;
128 	longlong si = 0;
129 	int r = 0;
130 	foreach(struct cached_image, i, li, image_cache_deref) si += image_size(i);
131 	foreachback(struct cached_image, i, li, image_cache_deref) {
132 		if (si <= image_cache_size && u == SH_CHECK_QUOTA)
133 			break;
134 		li = li->next;
135 		r = ST_SOMETHING_FREED;
136 		si -= image_size(i);
137 		del_from_list(i);
138 		if (i->hash_fwdlink)
139 			i->hash_fwdlink->hash_backlink = i->hash_backlink;
140 		*i->hash_backlink = i->hash_fwdlink;
141 		img_destruct_cached_image(i);
142 		if (u == SH_FREE_SOMETHING) break;
143 	}
144 	return r | (list_empty(image_cache_deref) && list_empty(image_cache_ref) ? ST_CACHE_EMPTY : 0);
145 }
146 
imgcache_info(int type)147 my_uintptr_t imgcache_info(int type)
148 {
149 	struct cached_image *i;
150 	struct list_head *li;
151 	my_uintptr_t n = 0;
152 	foreach(struct cached_image, i, li, image_cache_ref) {
153 		switch (type) {
154 			case CI_BYTES:
155 				n += image_size(i);
156 				break;
157 			case CI_LOCKED:
158 			case CI_FILES:
159 				n++;
160 				break;
161 			default:
162 				internal_error("imgcache_info: query %d", type);
163 		}
164 	}
165 	foreach(struct cached_image, i, li, image_cache_deref) {
166 		switch (type) {
167 			case CI_BYTES:
168 				n += image_size(i);
169 				break;
170 			case CI_LOCKED:
171 				break;
172 			case CI_FILES:
173 				n++;
174 				break;
175 			default:
176 				internal_error("imgcache_info: query %d", type);
177 		}
178 	}
179 	return n;
180 }
181 
init_imgcache(void)182 void init_imgcache(void)
183 {
184 	unsigned i;
185 	register_cache_upcall(shrink_image_cache, MF_GPI, cast_uchar "imgcache");
186 	for (i = 0; i < CACHED_IMAGE_HASH_SIZE; i++)
187 		image_hash[i] = NULL;
188 }
189 
190 #endif
191