Rename GP_Context -> GP_Pixmap
[gfxprim.git] / demos / spiv / image_cache.c
blob28ea5bbbfc22002b621c19869dca3e5184688a81
1 /*****************************************************************************
2 * This file is part of gfxprim library. *
3 * *
4 * Gfxprim is free software; you can redistribute it and/or *
5 * modify it under the terms of the GNU Lesser General Public *
6 * License as published by the Free Software Foundation; either *
7 * version 2.1 of the License, or (at your option) any later version. *
8 * *
9 * Gfxprim is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
12 * Lesser General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU Lesser General Public *
15 * License along with gfxprim; if not, write to the Free Software *
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, *
17 * Boston, MA 02110-1301 USA *
18 * *
19 * Copyright (C) 2009-2013 Cyril Hrubis <metan@ucw.cz> *
20 * *
21 *****************************************************************************/
23 #include <stdarg.h>
24 #include <string.h>
25 #include <GP.h>
26 #include "image_cache.h"
28 struct image {
29 GP_Pixmap *pixmap;
30 GP_DataStorage *meta_data;
32 struct image *prev;
33 struct image *next;
35 /* number of elevated get calls */
36 unsigned int elevated;
38 /* this identifies an image */
39 char path[];
42 struct image_cache {
43 unsigned int max_size;
44 unsigned int cur_size;
46 struct image *root;
47 struct image *end;
50 size_t image_cache_get_ram_size(void)
52 FILE *f;
53 size_t ret;
55 f = fopen("/proc/meminfo", "r");
57 if (f == NULL) {
58 GP_WARN("Failed to read /proc/meminfo");
59 return 0;
62 if (fscanf(f, "MemTotal: %zu", &ret) != 1) {
63 fclose(f);
64 GP_WARN("Failed to read /proc/meminfo");
65 return 0;
68 fclose(f);
70 return ret;
74 * Reports correct image record size.
76 static size_t image_size2(GP_Pixmap *pixmap, GP_DataStorage *meta_data,
77 const char *path)
79 size_t meta_data_size = 0;
80 size_t pixmap_size = pixmap->bytes_per_row * pixmap->h + sizeof(GP_Pixmap);
82 //TODO! 4096 is a size of single block, data storage may have more blocks
83 if (meta_data)
84 meta_data_size = 4096;
86 return meta_data_size + pixmap_size +
87 sizeof(struct image) + strlen(path) + 1;
90 static size_t image_size(struct image *img)
92 return image_size2(img->pixmap, NULL, img->path);
95 struct image_cache *image_cache_create(unsigned int max_size_kbytes)
97 struct image_cache *self;
99 self = malloc(sizeof(struct image_cache));
101 if (self == NULL)
102 return NULL;
104 self->max_size = max_size_kbytes * 1024;
105 self->cur_size = sizeof(struct image_cache);
107 self->root = NULL;
108 self->end = NULL;
110 GP_DEBUG(1, "Created image cache max size %ukB", max_size_kbytes);
112 return self;
115 static void remove_img(struct image_cache *self, struct image *img, size_t size)
117 if (img == self->end)
118 self->end = img->prev;
120 if (img->prev)
121 img->prev->next = img->next;
123 if (img->next)
124 img->next->prev = img->prev;
126 if (img == self->root)
127 self->root = img->next;
129 self->cur_size -= size;
132 static void remove_img_free(struct image_cache *self,
133 struct image *img, size_t size)
135 GP_DEBUG(2, "Freeing image '%s' size %zu", img->path, size);
137 remove_img(self, img, size);
138 GP_PixmapFree(img->pixmap);
139 GP_DataStorageDestroy(img->meta_data);
140 free(img);
144 * Adds image to the start of the double linked list
146 static void add_img(struct image_cache *self, struct image *img, size_t size)
148 img->next = self->root;
150 if (img->next)
151 img->next->prev = img;
153 img->prev = NULL;
155 self->root = img;
156 self->cur_size += size;
158 if (self->end == NULL)
159 self->end = img;
162 int image_cache_get(struct image_cache *self, GP_Pixmap **img,
163 GP_DataStorage **meta_data, int elevate, const char *key)
165 struct image *i;
167 if (self == NULL)
168 return 1;
170 GP_DEBUG(2, "Looking for image '%s'", key);
172 for (i = self->root; i != NULL; i = i->next)
173 if (!strcmp(key, i->path))
174 break;
176 if (i == NULL)
177 return 1;
179 /* Push the image to the root of the list */
180 if (elevate) {
181 size_t size = image_size(i);
183 GP_DEBUG(2, "Refreshing image '%s'", key);
185 remove_img(self, i, size);
186 add_img(self, i, size);
188 i->elevated++;
191 if (img)
192 *img = i->pixmap;
194 if (meta_data)
195 *meta_data = i->meta_data;
197 return 0;
200 GP_Pixmap *image_cache_get2(struct image_cache *self, int elevate,
201 const char *fmt, ...)
203 va_list va;
204 size_t len;
205 char buf[512];
206 char *key = buf;
207 struct image *i;
209 if (self == NULL)
210 return NULL;
212 va_start(va, fmt);
213 len = vsnprintf(buf, sizeof(buf), fmt, va);
214 va_end(va);
216 if (len >= sizeof(buf)) {
217 key = malloc(len + 1);
218 if (!key) {
219 GP_WARN("Malloc failed :(");
220 return NULL;
223 va_start(va, fmt);
224 vsprintf(key, fmt, va);
225 va_end(va);
228 GP_DEBUG(2, "Looking for image '%s'", key);
230 for (i = self->root; i != NULL; i = i->next)
231 if (!strcmp(key, i->path))
232 break;
234 /* Push the image to the root of the list */
235 if (i && elevate) {
236 size_t size = image_size(i);
238 GP_DEBUG(2, "Refreshing image '%s'", key);
240 remove_img(self, i, size);
241 add_img(self, i, size);
243 i->elevated++;
246 if (len >= sizeof(buf))
247 free(key);
249 return i ? i->pixmap : NULL;
252 void image_cache_print(struct image_cache *self)
254 struct image *i;
256 if (self == NULL) {
257 printf("Image cache disabled\n");
258 return;
261 printf("Image cache size %u used %u\n", self->max_size, self->cur_size);
263 for (i = self->root; i != NULL; i = i->next)
264 printf(" size=%10zu elevated=%u key='%s'\n",
265 image_size(i), i->elevated, i->path);
268 static int assert_size(struct image_cache *self, size_t size)
270 if (self->cur_size + size < self->max_size)
271 return 0;
273 while (self->cur_size + size > self->max_size) {
275 if (self->end == NULL) {
276 GP_WARN("Cache too small for image size %zu", size);
277 return 1;
280 remove_img_free(self, self->end, image_size(self->end));
283 return 0;
286 int image_cache_put(struct image_cache *self, GP_Pixmap *pixmap,
287 GP_DataStorage *meta_data, const char *key)
289 size_t size;
291 if (self == NULL)
292 return 1;
294 size = image_size2(pixmap, meta_data, key);
297 * We try to create room for the image. If this fails we add the image
298 * anyway because we need to store it while we are showing it (and it
299 * will be removed from cache by next image for sure).
301 assert_size(self, size);
303 struct image *img = malloc(sizeof(struct image) + strlen(key) + 1);
305 if (img == NULL) {
306 GP_WARN("Malloc failed :(");
307 return 1;
310 img->pixmap = pixmap;
311 img->meta_data = meta_data;
312 img->elevated = 0;
313 strcpy(img->path, key);
315 GP_DEBUG(2, "Adding image '%s' size %zu", img->path, size);
317 add_img(self, img, size);
319 return 0;
322 int image_cache_put2(struct image_cache *self, GP_Pixmap *pixmap,
323 GP_DataStorage *meta_data, const char *fmt, ...)
325 size_t size, len;
326 va_list va;
328 if (self == NULL)
329 return 1;
331 va_start(va, fmt);
332 len = vsnprintf(NULL, 0, fmt, va);
333 va_end(va);
335 //TODO: FIX THIS
336 size = image_size2(pixmap, meta_data, "") + len + 1;
339 * We try to create room for the image. If this fails we add the image
340 * anyway because we need to store it while we are showing it (and it
341 * will be removed from cache by next image for sure).
343 assert_size(self, size);
345 struct image *img = malloc(sizeof(struct image) + len + 1);
347 if (img == NULL) {
348 GP_WARN("Malloc failed :(");
349 return 1;
352 img->pixmap = pixmap;
353 img->meta_data = meta_data;
354 img->elevated = 0;
356 va_start(va, fmt);
357 vsprintf(img->path, fmt, va);
358 va_end(va);
360 GP_DEBUG(2, "Adding image '%s' size %zu",
361 img->path, size);
363 add_img(self, img, size);
365 return 0;
368 void image_cache_drop(struct image_cache *self)
370 if (self == NULL)
371 return;
373 GP_DEBUG(1, "Dropping images in cache");
375 while (self->end != NULL)
376 remove_img_free(self, self->end, 0);
378 self->cur_size = sizeof(struct image_cache);
381 void image_cache_destroy(struct image_cache *self)
383 if (self == NULL)
384 return;
386 GP_DEBUG(1, "Destroying image cache");
388 while (self->end != NULL)
389 remove_img_free(self, self->end, 0);
391 free(self);