From f027b0f012e6ea1e694dd0618be0d9c18c840dc5 Mon Sep 17 00:00:00 2001 From: Cyril Hrubis Date: Fri, 8 Jun 2012 18:40:43 +0200 Subject: [PATCH] spiv: Add image cache. --- demos/spiv/Makefile | 2 +- demos/spiv/image_cache.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++ demos/spiv/image_cache.h | 64 ++++++++++++++ demos/spiv/spiv.c | 82 +++++++++-------- 4 files changed, 329 insertions(+), 43 deletions(-) create mode 100644 demos/spiv/image_cache.c create mode 100644 demos/spiv/image_cache.h diff --git a/demos/spiv/Makefile b/demos/spiv/Makefile index 13b416cb..431e6cea 100644 --- a/demos/spiv/Makefile +++ b/demos/spiv/Makefile @@ -9,7 +9,7 @@ LDLIBS+=-lGP -lGP_backends -lSDL -L$(TOPDIR)/build/ APPS=spiv -spiv: cpu_timer.o +spiv: cpu_timer.o image_cache.o include $(TOPDIR)/pre.mk include $(TOPDIR)/app.mk diff --git a/demos/spiv/image_cache.c b/demos/spiv/image_cache.c new file mode 100644 index 00000000..f2520139 --- /dev/null +++ b/demos/spiv/image_cache.c @@ -0,0 +1,224 @@ +/***************************************************************************** + * This file is part of gfxprim library. * + * * + * Gfxprim is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * Gfxprim 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with gfxprim; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, * + * Boston, MA 02110-1301 USA * + * * + * Copyright (C) 2009-2012 Cyril Hrubis * + * * + *****************************************************************************/ + +#include +#include +#include "image_cache.h" + +struct image { + GP_Context *ctx; + int cookie; + + struct image *prev; + struct image *next; + + char path[]; +}; + +struct image_cache { + unsigned int max_size; + unsigned int cur_size; + + struct image *root; + struct image *end; +}; + +static size_t read_total_memory(void) +{ + FILE *f; + size_t ret; + + f = fopen("/proc/meminfo", "r"); + + if (f == NULL) { + GP_WARN("Failed to read /proc/meminfo"); + return 0; + } + + if (fscanf(f, "MemTotal: %zu", &ret) != 1) { + fclose(f); + GP_WARN("Failed to read /proc/meminfo"); + return 0; + } + + fclose(f); + + return ret * 1024 / 10; +} + +struct image_cache *image_cache_create(unsigned int max_size) +{ + struct image_cache *self; + + self = malloc(sizeof(struct image_cache)); + + if (self == NULL) + return NULL; + + self->max_size = max_size ? max_size : read_total_memory(); + self->cur_size = 0; + + self->root = NULL; + self->end = NULL; + + GP_DEBUG(1, "Created image cache size %u bytes", self->max_size); + + return self; +} + +static void remove_img(struct image_cache *self, struct image *img) +{ + size_t size = img->ctx->bytes_per_row * img->ctx->h; + + if (img == self->end) + self->end = img->prev; + + if (img->prev) + img->prev->next = img->next; + + if (img->next) + img->next->prev = img->prev; + + if (img == self->root) + self->root = img->next; + + self->cur_size -= size; +} + +static void remove_img_free(struct image_cache *self, struct image *img) +{ + GP_DEBUG(2, "Freeing image '%s:%i' size %u", + img->path, img->cookie, + img->ctx->bytes_per_row * img->ctx->h); + + remove_img(self, img); + GP_ContextFree(img->ctx); + free(img); +} + +/* + * Adds image to the start of the double linked list + */ +static void add_img(struct image_cache *self, struct image *img) +{ + size_t size = img->ctx->bytes_per_row * img->ctx->h; + + img->next = self->root; + + if (img->next) + img->next->prev = img; + + img->prev = NULL; + + self->root = img; + self->cur_size += size; + + if (self->end == NULL) + self->end = img; +} + +GP_Context *image_cache_get(struct image_cache *self, + const char *path, int cookie) +{ + struct image *i; + + GP_DEBUG(2, "Looking for image '%s:%i'", path, cookie); + + for (i = self->root; i != NULL; i = i->next) + if (!strcmp(path, i->path) && i->cookie == cookie) + break; + + if (i == NULL) + return NULL; + + /* Push the image to the root of the list */ + GP_DEBUG(2, "Refreshing image '%s:%i", path, cookie); + remove_img(self, i); + add_img(self, i); + + return i->ctx; +} + +void image_cache_print(struct image_cache *self) +{ + struct image *i; + + printf("Image cache size %u used %u\n", self->max_size, self->cur_size); + + for (i = self->root; i != NULL; i = i->next) + printf(" Image '%s:%i' size %u\n", i->path, i->cookie, + i->ctx->bytes_per_row * i->ctx->h); +} + +static int assert_size(struct image_cache *self, size_t size) +{ + if (self->cur_size + size < self->max_size) + return 0; + + while (self->cur_size + size > self->max_size) { + + if (self->end == NULL) { + GP_WARN("Cache too small for image size %zu", size); + return 1; + } + + remove_img_free(self, self->end); + } + + return 0; +} + +int image_cache_put(struct image_cache *self, + GP_Context *ctx, const char *path, int cookie) +{ + size_t size = ctx->bytes_per_row * ctx->h; + + if (assert_size(self, size)) + return 1; + + struct image *img = malloc(sizeof(struct image) + strlen(path) + 1); + + if (img == NULL) { + GP_WARN("Malloc failed :("); + return 1; + } + + GP_DEBUG(2, "Adding image '%s:%i' size %zu", + img->path, img->cookie, size); + + img->ctx = ctx; + img->cookie = cookie; + strcpy(img->path, path); + + add_img(self, img); + return 0; +} + +void image_cache_destroy(struct image_cache *self) +{ + GP_DEBUG(1, "Destroying image cache"); + + while (self->end != NULL) + remove_img_free(self, self->end); + + free(self); +} diff --git a/demos/spiv/image_cache.h b/demos/spiv/image_cache.h new file mode 100644 index 00000000..ad3ecdd0 --- /dev/null +++ b/demos/spiv/image_cache.h @@ -0,0 +1,64 @@ +/***************************************************************************** + * This file is part of gfxprim library. * + * * + * Gfxprim is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * Gfxprim 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with gfxprim; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, * + * Boston, MA 02110-1301 USA * + * * + * Copyright (C) 2009-2012 Cyril Hrubis * + * * + *****************************************************************************/ + + /* + + Image loader chache. + + */ + +#ifndef __IMAGE_CACHE_H__ +#define __IMAGE_CACHE_H__ + +struct image_cache; + +/* + * Creates an image cache with maximal memory size. + * + * When memory size is set to zero, the size is read from /proc/meminfo + * as 10% of total memory. + */ +struct image_cache *image_cache_create(unsigned int max_size); + +/* + * Returns cached image, or NULL. + */ +GP_Context *image_cache_get(struct image_cache *self, + const char *path, int cookie); + +/* + * Puts an image into a cache. + */ +int image_cache_put(struct image_cache *self, + GP_Context *img, const char *path, int cookie); + +/* + * Destroys image cache and all it's images. + */ +void image_cache_destroy(struct image_cache *self); + +/* + * Print the image cache content. + */ +void image_cache_print(struct image_cache *self); + +#endif /* __IMAGE_CACHE_H__ */ diff --git a/demos/spiv/spiv.c b/demos/spiv/spiv.c index 6e035311..26eb45bc 100644 --- a/demos/spiv/spiv.c +++ b/demos/spiv/spiv.c @@ -36,6 +36,7 @@ #include #include +#include "image_cache.h" #include "cpu_timer.h" static GP_Pixel black_pixel; @@ -88,8 +89,8 @@ struct loader_params { int show_progress_once; int show_info; - /* cached loaded image */ - GP_Context *img; + /* cached loaded images */ + struct image_cache *image_cache; }; static float calc_img_size(uint32_t img_w, uint32_t img_h, @@ -125,46 +126,46 @@ static void set_caption(const char *path, float rat) /* * Loads image */ -int load_image(struct loader_params *params) +GP_Context *load_image(struct loader_params *params) { struct cpu_timer timer; GP_Context *img, *context = backend->context; - - if (params->img != NULL) { - fprintf(stderr, "Image cached!\n"); - return 0; - } GP_ProgressCallback callback = {.callback = image_loader_callback, .priv = "Loading image"}; - - show_progress = params->show_progress || params->show_progress_once; - params->show_progress_once = 0; - fprintf(stderr, "Loading '%s'\n", params->img_path); + img = image_cache_get(params->image_cache, params->img_path, 0); - cpu_timer_start(&timer, "Loading"); - if ((img = GP_LoadImage(params->img_path, &callback)) == NULL) { - GP_Fill(context, black_pixel); - GP_Print(context, NULL, context->w/2, context->h/2, - GP_ALIGN_CENTER|GP_VALIGN_CENTER, white_pixel, black_pixel, - "Failed to load image :( (%s)", strerror(errno)); - GP_BackendFlip(backend); - return 1; - } + /* Image not cached, load it */ + if (img == NULL) { + show_progress = params->show_progress || params->show_progress_once; + params->show_progress_once = 0; + + fprintf(stderr, "Loading '%s'\n", params->img_path); + + cpu_timer_start(&timer, "Loading"); + if ((img = GP_LoadImage(params->img_path, &callback)) == NULL) { + GP_Fill(context, black_pixel); + GP_Print(context, NULL, context->w/2, context->h/2, + GP_ALIGN_CENTER|GP_VALIGN_CENTER, white_pixel, black_pixel, + "Failed to load image :( (%s)", strerror(errno)); + GP_BackendFlip(backend); + return NULL; + } + + /* Workaround */ + if (img->pixel_type != GP_PIXEL_RGB888) { + GP_Context *tmp = GP_ContextConvert(img, GP_PIXEL_RGB888); + GP_ContextFree(img); + img = tmp; + } - /* Workaround */ - if (img->pixel_type != GP_PIXEL_RGB888) { - GP_Context *tmp = GP_ContextConvert(img, GP_PIXEL_RGB888); - GP_ContextFree(img); - img = tmp; + image_cache_put(params->image_cache, img, params->img_path, 0); + + cpu_timer_stop(&timer); } - - cpu_timer_stop(&timer); - - params->img = img; - return 0; + return img; } /* @@ -173,7 +174,7 @@ int load_image(struct loader_params *params) */ static int resize_backend_and_blit(struct loader_params *params) { - GP_Context *img = params->img; + GP_Context *img = load_image(params); if (GP_BackendResize(backend, img->w, img->h)) return 1; @@ -196,10 +197,10 @@ static void *image_loader(void *ptr) cpu_timer_start(&sum_timer, "sum"); /* Load Image */ - if (load_image(params)) - return NULL; + img = load_image(params); - img = params->img; + if (img == NULL) + return NULL; /* if (img->w < 320 && img->h < 240) { @@ -351,13 +352,8 @@ static void show_image(struct loader_params *params, const char *path) abort_flag = 0; } - /* invalidate cached image if path has changed */ - if (params->img_path == NULL || - (path != NULL && strcmp(params->img_path, path))) { - GP_ContextFree(params->img); - params->img = NULL; + if (path != NULL) params->img_path = path; - } ret = pthread_create(&loader_thread, NULL, image_loader, (void*)params); @@ -465,10 +461,12 @@ int main(int argc, char *argv[]) GP_Context *context = NULL; const char *backend_opts = "X11"; int sleep_sec = -1; - struct loader_params params = {NULL, 0, 0, 0, .img = NULL}; + struct loader_params params = {NULL, 0, 0, 0, NULL}; int opt, debug_level = 0; GP_PixelType emul_type = GP_PIXEL_UNKNOWN; + params.image_cache = image_cache_create(0); + while ((opt = getopt(argc, argv, "b:cd:e:fIPs:r:")) != -1) { switch (opt) { case 'I': -- 2.11.4.GIT