commits
tags
#include "image_cache.h"
#include <SDL_image.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/stat.h>
bool image_resolve_path(const char *docFilePath, const char *imageRef, char *outBuf, int outBufSize) {
if (!imageRef || imageRef[0] == '\0') return false;
int n;
if (imageRef[0] == '/') {
n = snprintf(outBuf, (size_t)outBufSize, "%s", imageRef);
return n > 0 && n < outBufSize;
}
if (!docFilePath) return false;
const char *slash = strrchr(docFilePath, '/');
if (slash) {
int dirLen = (int)(slash - docFilePath) + 1; /* includes the trailing '/' */
n = snprintf(outBuf, (size_t)outBufSize, "%.*s%s", dirLen, docFilePath, imageRef);
} else {
/* docFilePath has no directory component -- it's a bare filename in the CWD. */
n = snprintf(outBuf, (size_t)outBufSize, "%s", imageRef);
}
return n > 0 && n < outBufSize;
}
typedef struct {
char *path;
SDL_Texture *texture;
int width, height;
time_t mtime;
bool loaded; /* true once a load has been attempted for the current mtime (success or fail) */
bool failed; /* meaningful only when loaded == true */
} ImageCacheEntry;
struct ImageCache {
ImageCacheEntry *items;
int count, cap;
};
static char *dup_str(const char *s) {
size_t n = strlen(s);
char *copy = malloc(n + 1);
memcpy(copy, s, n + 1);
return copy;
}
void image_cache_init(ImageCache **cache) {
ImageCache *c = malloc(sizeof(ImageCache));
c->items = NULL;
c->count = 0;
c->cap = 0;
*cache = c;
}
void image_cache_free(ImageCache *cache) {
if (!cache) return;
for (int i = 0; i < cache->count; i++) {
if (cache->items[i].texture) SDL_DestroyTexture(cache->items[i].texture);
free(cache->items[i].path);
}
free(cache->items);
free(cache);
}
static ImageCacheEntry *find_entry(ImageCache *cache, const char *path) {
for (int i = 0; i < cache->count; i++) {
if (strcmp(cache->items[i].path, path) == 0) return &cache->items[i];
}
return NULL;
}
static ImageCacheEntry *add_entry(ImageCache *cache, const char *path) {
if (cache->count >= cache->cap) {
int newCap = cache->cap > 0 ? cache->cap * 2 : 8;
cache->items = realloc(cache->items, (size_t)newCap * sizeof(ImageCacheEntry));
cache->cap = newCap;
}
ImageCacheEntry *e = &cache->items[cache->count++];
memset(e, 0, sizeof(*e));
e->path = dup_str(path);
return e;
}
static void load_entry(ImageCacheEntry *e, SDL_Renderer *renderer, time_t mtime) {
if (e->texture) { SDL_DestroyTexture(e->texture); e->texture = NULL; }
e->loaded = true;
e->mtime = mtime;
SDL_Surface *surface = IMG_Load(e->path);
if (!surface) { e->failed = true; return; }
SDL_Texture *tex = SDL_CreateTextureFromSurface(renderer, surface);
int w = surface->w, h = surface->h;
SDL_FreeSurface(surface);
if (!tex) { e->failed = true; return; }
e->texture = tex;
e->width = w;
e->height = h;
e->failed = false;
}
bool image_cache_get(ImageCache *cache, SDL_Renderer *renderer, const char *resolvedPath,
SDL_Texture **outTexture, int *outW, int *outH) {
struct stat st;
if (stat(resolvedPath, &st) != 0) {
ImageCacheEntry *e = find_entry(cache, resolvedPath);
if (e) {
if (e->texture) { SDL_DestroyTexture(e->texture); e->texture = NULL; }
e->loaded = false;
}
return false;
}
ImageCacheEntry *e = find_entry(cache, resolvedPath);
if (!e) e = add_entry(cache, resolvedPath);
if (!e->loaded || e->mtime != st.st_mtime) {
load_entry(e, renderer, st.st_mtime);
}
if (e->failed || !e->texture) return false;
*outTexture = e->texture;
*outW = e->width;
*outH = e->height;
return true;
}