efl/src/lib/evas/cache2/evas_cache2.c

1125 lines
29 KiB
C

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#ifdef HAVE_EVIL
# include <Evil.h>
#endif
#include "evas_common.h"
#include "evas_private.h"
#include "evas_cache2.h"
#include "evas_cs2.h"
#include "evas_cs2_private.h"
#define FREESTRC(Var) \
if (Var) \
{ \
eina_stringshare_del(Var); \
Var = NULL; \
}
/* Size of characters used to determine a string that'll be used for load
* options in hash keys.
*/
#define HKEY_LOAD_OPTS_STR_LEN 215
static void _evas_cache_image_dirty_add(Image_Entry *im);
static void _evas_cache_image_dirty_del(Image_Entry *im);
static void _evas_cache_image_activ_add(Image_Entry *im);
static void _evas_cache_image_activ_del(Image_Entry *im);
static void _evas_cache_image_lru_add(Image_Entry *im);
static void _evas_cache_image_lru_del(Image_Entry *im);
static void _evas_cache2_image_entry_preload_remove(Image_Entry *ie, const void *target);
// static void _evas_cache_image_lru_nodata_add(Image_Entry *im);
// static void _evas_cache_image_lru_nodata_del(Image_Entry *im);
static void
_evas_cache_image_dirty_add(Image_Entry *im)
{
if (im->flags.dirty) return;
_evas_cache_image_activ_del(im);
_evas_cache_image_lru_del(im);
// _evas_cache_image_lru_nodata_del(im);
im->flags.dirty = 1;
im->flags.cached = 1;
im->cache2->dirty = eina_inlist_prepend(im->cache2->dirty, EINA_INLIST_GET(im));
if (im->cache_key)
{
eina_stringshare_del(im->cache_key);
im->cache_key = NULL;
}
}
static void
_evas_cache_image_dirty_del(Image_Entry *im)
{
if (!im->flags.dirty) return;
if (!im->cache2) return;
im->flags.dirty = 0;
im->flags.cached = 0;
im->cache2->dirty = eina_inlist_remove(im->cache2->dirty, EINA_INLIST_GET(im));
}
static void
_evas_cache_image_activ_add(Image_Entry *im)
{
if (im->flags.activ) return;
_evas_cache_image_dirty_del(im);
_evas_cache_image_lru_del(im);
// _evas_cache_image_lru_nodata_del(im);
if (!im->cache_key) return;
im->flags.activ = 1;
im->flags.cached = 1;
eina_hash_direct_add(im->cache2->activ, im->cache_key, im);
}
static void
_evas_cache_image_activ_del(Image_Entry *im)
{
if (!im->flags.activ) return;
if (!im->cache_key) return;
im->flags.activ = 0;
im->flags.cached = 0;
eina_hash_del(im->cache2->activ, im->cache_key, im);
}
static void
_evas_cache_image_lru_add(Image_Entry *im)
{
if (im->flags.lru) return;
_evas_cache_image_dirty_del(im);
_evas_cache_image_activ_del(im);
// _evas_cache_image_lru_nodata_del(im);
if (!im->cache_key) return;
im->flags.lru = 1;
im->flags.cached = 1;
eina_hash_direct_add(im->cache2->inactiv, im->cache_key, im);
im->cache2->lru = eina_inlist_prepend(im->cache2->lru, EINA_INLIST_GET(im));
im->cache2->usage += im->cache2->func.mem_size_get(im);
}
static void
_evas_cache_image_lru_del(Image_Entry *im)
{
if (!im->flags.lru) return;
if (!im->cache_key) return;
im->flags.lru = 0;
im->flags.cached = 0;
eina_hash_del(im->cache2->inactiv, im->cache_key, im);
im->cache2->lru = eina_inlist_remove(im->cache2->lru, EINA_INLIST_GET(im));
im->cache2->usage -= im->cache2->func.mem_size_get(im);
}
/*
static void
_evas_cache_image_lru_nodata_add(Image_Entry *im)
{
if (im->flags.lru_nodata) return;
_evas_cache_image_dirty_del(im);
_evas_cache_image_activ_del(im);
_evas_cache_image_lru_del(im);
im->flags.lru = 1;
im->flags.cached = 1;
im->cache2->lru_nodata = eina_inlist_prepend(im->cache2->lru_nodata, EINA_INLIST_GET(im));
}
static void
_evas_cache_image_lru_nodata_del(Image_Entry *im)
{
if (!im->flags.lru_nodata) return;
im->flags.lru = 0;
im->flags.cached = 0;
im->cache2->lru_nodata = eina_inlist_remove(im->cache2->lru_nodata, EINA_INLIST_GET(im));
}
*/
static Eina_Bool
_timestamp_compare(Image_Timestamp *tstamp, struct stat *st)
{
if (tstamp->mtime != st->st_mtime) return EINA_FALSE;
if (tstamp->size != st->st_size) return EINA_FALSE;
if (tstamp->ino != st->st_ino) return EINA_FALSE;
#ifdef _STAT_VER_LINUX
#if (defined __USE_MISC && defined st_mtime)
if (tstamp->mtime_nsec != (unsigned long int)st->st_mtim.tv_nsec)
return EINA_FALSE;
#else
if (tstamp->mtime_nsec != (unsigned long int)st->st_mtimensec)
return EINA_FALSE;
#endif
#endif
return EINA_TRUE;
}
static void
_timestamp_build(Image_Timestamp *tstamp, struct stat *st)
{
tstamp->mtime = st->st_mtime;
tstamp->size = st->st_size;
tstamp->ino = st->st_ino;
#ifdef _STAT_VER_LINUX
#if (defined __USE_MISC && defined st_mtime)
tstamp->mtime_nsec = (unsigned long int)st->st_mtim.tv_nsec;
#else
tstamp->mtime_nsec = (unsigned long int)st->st_mtimensec;
#endif
#endif
}
static void
_evas_cache_image_entry_delete(Evas_Cache2 *cache, Image_Entry *ie)
{
if (!ie) return;
if (ie->flags.delete_me == 1)
return;
if (ie->preload_rid)
{
ie->flags.delete_me = 1;
_evas_cache2_image_entry_preload_remove(ie, NULL);
return;
}
_evas_cache_image_dirty_del(ie);
_evas_cache_image_activ_del(ie);
_evas_cache_image_lru_del(ie);
// _evas_cache_image_lru_nodata_del(ie);
if (ie->data1)
{
evas_cserve2_image_unload(ie);
evas_cache2_image_unload_data(ie);
evas_cserve2_image_free(ie);
}
else
{
if (cache)
cache->func.surface_delete(ie);
}
FREESTRC(ie->cache_key);
FREESTRC(ie->file);
FREESTRC(ie->key);
ie->cache2 = NULL;
evas_common_rgba_image_scalecache_shutdown(ie);
free(ie);
}
static Image_Entry *
_evas_cache_image_entry_new(Evas_Cache2 *cache,
const char *hkey,
Image_Timestamp *tstamp,
const char *file,
const char *key,
RGBA_Image_Loadopts *lo,
int *error)
{
Image_Entry *ie;
RGBA_Image *im;
// ie = cache->func.alloc();
im = calloc(1, sizeof(RGBA_Image));
if (!im)
{
*error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
return NULL;
}
im->flags = RGBA_IMAGE_NOTHING;
evas_common_rgba_image_scalecache_init(&im->cache_entry);
ie = &im->cache_entry;
ie->cache2 = cache;
if (hkey) ie->cache_key = eina_stringshare_add(hkey);
ie->flags.need_data = 1;
ie->space = EVAS_COLORSPACE_ARGB8888;
ie->w = -1;
ie->h = -1;
ie->scale = 1;
if (file) ie->file = eina_stringshare_add(file);
if (key) ie->key = eina_stringshare_add(key);
if (tstamp) ie->tstamp = *tstamp;
else memset(&ie->tstamp, 0, sizeof(Image_Timestamp));
if (lo) ie->load_opts = *lo;
if (ie->file)
{
if (!evas_cserve2_image_load(ie, ie->file, ie->key, &(ie->load_opts)))
{
ERR("couldn't load '%s' '%s' with cserve2!",
ie->file, ie->key ? ie->key : "");
_evas_cache_image_entry_delete(cache, ie);
return NULL;
}
}
if (ie->cache_key) _evas_cache_image_activ_add(ie);
else _evas_cache_image_dirty_add(ie);
return ie;
}
EAPI void
evas_cache2_image_surface_alloc(Image_Entry *ie, int w, int h)
{
Evas_Cache2 *cache = ie->cache2;
int wmin = w > 0 ? w : 1;
int hmin = h > 0 ? h : 1;
if (cache->func.surface_alloc(ie, wmin, hmin))
{
wmin = 0;
hmin = 0;
}
ie->w = wmin;
ie->h = hmin;
ie->allocated.w = wmin;
ie->allocated.h = hmin;
ie->flags.loaded = EINA_TRUE;
}
static void
_evas_cache2_image_preloaded_cb(void *data, Eina_Bool success)
{
Image_Entry *ie = data;
Evas_Cache_Target *tmp;
ie->cache2->preload = eina_list_remove(ie->cache2->preload, ie);
ie->flags.preload_done = success;
while ((tmp = ie->targets))
{
ie->targets = (Evas_Cache_Target *)
eina_inlist_remove(EINA_INLIST_GET(ie->targets),
EINA_INLIST_GET(ie->targets));
if (!ie->flags.delete_me)
evas_object_inform_call_image_preloaded((Evas_Object *) tmp->target);
free(tmp);
}
if (ie->flags.delete_me)
_evas_cache_image_entry_delete(ie->cache2, ie);
}
static Eina_Bool
_evas_cache2_image_entry_preload_add(Image_Entry *ie, const void *target)
{
Evas_Cache_Target *tg;
if (ie->flags.preload_done)
return EINA_FALSE;
tg = malloc(sizeof(Evas_Cache_Target));
if (!tg)
return EINA_TRUE;
tg->target = target;
ie->targets = (Evas_Cache_Target *)
eina_inlist_append(EINA_INLIST_GET(ie->targets), EINA_INLIST_GET(tg));
if (!ie->preload_rid)
{
ie->cache2->preload = eina_list_append(ie->cache2->preload, ie);
evas_cserve2_image_preload(ie, _evas_cache2_image_preloaded_cb);
}
return EINA_TRUE;
}
static void
_evas_cache2_image_entry_preload_remove(Image_Entry *ie, const void *target)
{
if (target)
{
Evas_Cache_Target *tg;
EINA_INLIST_FOREACH(ie->targets, tg)
{
if (tg->target == target)
{
ie->targets = (Evas_Cache_Target *)
eina_inlist_remove(EINA_INLIST_GET(ie->targets),
EINA_INLIST_GET(tg));
free(tg);
break;
}
}
}
else
{
Evas_Cache_Target *tg;
while (ie->targets)
{
tg = ie->targets;
ie->targets = (Evas_Cache_Target *)
eina_inlist_remove(EINA_INLIST_GET(ie->targets),
EINA_INLIST_GET(tg));
free(tg);
}
}
// FIXME: Should also send message to the server to cancel the request.
}
EAPI Image_Entry *
evas_cache2_image_copied_data(Evas_Cache2 *cache, unsigned int w, unsigned int h, DATA32 *image_data, int alpha, int cspace)
{
Image_Entry *im;
if ((cspace == EVAS_COLORSPACE_YCBCR422P601_PL) ||
(cspace == EVAS_COLORSPACE_YCBCR422P709_PL) ||
(cspace == EVAS_COLORSPACE_YCBCR422601_PL))
w &= ~0x1;
im = _evas_cache_image_entry_new(cache, NULL, NULL, NULL, NULL, NULL, NULL);
if (!im)
return NULL;
im->space = cspace;
im->flags.alpha = alpha;
evas_cache2_image_surface_alloc(im, w, h);
if (cache->func.copied_data(im, w, h, image_data, alpha, cspace) != 0)
{
_evas_cache_image_entry_delete(cache, im);
return NULL;
}
im->references = 1;
im->flags.loaded = EINA_TRUE;
if (cache->func.debug) cache->func.debug("copied-data", im);
return im;
}
EAPI Image_Entry *
evas_cache2_image_data(Evas_Cache2 *cache, unsigned int w, unsigned int h, DATA32 *image_data, int alpha, int cspace)
{
Image_Entry *im;
if ((cspace == EVAS_COLORSPACE_YCBCR422P601_PL) ||
(cspace == EVAS_COLORSPACE_YCBCR422P709_PL) ||
(cspace == EVAS_COLORSPACE_YCBCR422601_PL))
w &= ~0x1;
im = _evas_cache_image_entry_new(cache, NULL, NULL, NULL, NULL, NULL, NULL);
if (!im) return NULL;
im->w = w;
im->h = h;
im->flags.alpha = alpha;
im->flags.loaded = 1;
if (cache->func.data(im, w, h, image_data, alpha, cspace) != 0)
{
_evas_cache_image_entry_delete(cache, im);
return NULL;
}
im->references = 1;
if (cache->func.debug) cache->func.debug("data", im);
return im;
}
EAPI Image_Entry *
evas_cache2_image_empty(Evas_Cache2 *cache)
{
Image_Entry *im;
im = _evas_cache_image_entry_new(cache, NULL, NULL, NULL, NULL, NULL, NULL);
if (!im)
return NULL;
im->references = 1;
return im;
}
EAPI Image_Entry *
evas_cache2_image_size_set(Image_Entry *im, unsigned int w, unsigned h)
{
Evas_Cache2 *cache;
Image_Entry *im2 = NULL;
int error;
if ((im->space == EVAS_COLORSPACE_YCBCR422P601_PL) ||
(im->space == EVAS_COLORSPACE_YCBCR422P709_PL) ||
(im->space == EVAS_COLORSPACE_YCBCR422601_PL))
w &= ~0x1;
if ((im->w == w) && (im->h == h)) return im;
cache = im->cache2;
im2 = _evas_cache_image_entry_new(cache, NULL, NULL, NULL, NULL, NULL,
NULL);
if (!im2) goto on_error;
im2->flags.alpha = im->flags.alpha;
im2->space = im->space;
im2->load_opts = im->load_opts;
evas_cache2_image_surface_alloc(im2, w, h);
error = cache->func.size_set(im2, im, w, h);
if (error != 0) goto on_error;
im2->references = 1;
evas_cache2_image_close(im);
return im2;
on_error:
if (im2)
_evas_cache_image_entry_delete(cache, im2);
return NULL;
}
EAPI Evas_Cache2 *
evas_cache2_init(const Evas_Cache2_Image_Func *cb)
{
Evas_Cache2 *cache = calloc(1, sizeof(Evas_Cache2));
cache->func = *cb;
cache->activ = eina_hash_string_superfast_new(NULL);
cache->inactiv = eina_hash_string_superfast_new(NULL);
return cache;
}
static Eina_Bool
_evas_cache_image_free_cb(EINA_UNUSED const Eina_Hash *hash, EINA_UNUSED const void *key, void *data, void *fdata)
{
Eina_List **delete_list = fdata;
*delete_list = eina_list_prepend(*delete_list, data);
return EINA_TRUE;
}
EAPI void
evas_cache2_shutdown(Evas_Cache2 *cache)
{
Eina_List *delete_list;
Image_Entry *im;
while (cache->lru)
{
im = (Image_Entry *)cache->lru;
_evas_cache_image_entry_delete(cache, im);
}
/* This is mad, I am about to destroy image still alive, but we need to prevent leak. */
while (cache->dirty)
{
im = (Image_Entry *)cache->dirty;
_evas_cache_image_entry_delete(cache, im);
}
delete_list = NULL;
eina_hash_foreach(cache->activ, _evas_cache_image_free_cb, &delete_list);
while (delete_list)
{
_evas_cache_image_entry_delete(cache, eina_list_data_get(delete_list));
delete_list = eina_list_remove_list(delete_list, delete_list);
}
eina_hash_free(cache->activ);
eina_hash_free(cache->inactiv);
free(cache);
}
static void
_create_hash_key(char *hkey, const char *path, size_t pathlen, const char *key, size_t keylen, RGBA_Image_Loadopts *lo)
{
const char *ckey = "(null)";
size_t size;
/* generate hkey from file+key+load opts */
memcpy(hkey, path, pathlen);
size = pathlen;
memcpy(hkey + size, "//://", 5);
size += 5;
if (key) ckey = key;
memcpy(hkey + size, ckey, keylen);
size += keylen;
if (lo)
{
memcpy(hkey + size, "//@/", 4);
size += 4;
size += eina_convert_xtoa(lo->scale_down_by, hkey + size);
hkey[size] = '/';
size += 1;
size += eina_convert_dtoa(lo->dpi, hkey + size);
hkey[size] = '/';
size += 1;
size += eina_convert_xtoa(lo->w, hkey + size);
hkey[size] = 'x';
size += 1;
size += eina_convert_xtoa(lo->h, hkey + size);
hkey[size] = '/';
size += 1;
size += eina_convert_xtoa(lo->region.x, hkey + size);
hkey[size] = '+';
size += 1;
size += eina_convert_xtoa(lo->region.y, hkey + size);
hkey[size] = '.';
size += 1;
size += eina_convert_xtoa(lo->region.w, hkey + size);
hkey[size] = 'x';
size += 1;
size += eina_convert_xtoa(lo->region.h, hkey + size);
hkey[size++] = '!';
hkey[size++] = '(';
hkey[size] = '[';
size += 1;
size += eina_convert_xtoa(lo->scale_load.src_x, hkey + size);
hkey[size] = ',';
size += 1;
size += eina_convert_xtoa(lo->scale_load.src_y, hkey + size);
hkey[size] = ':';
size += 1;
size += eina_convert_xtoa(lo->scale_load.src_w, hkey + size);
hkey[size] = 'x';
size += 1;
size += eina_convert_xtoa(lo->scale_load.src_h, hkey + size);
hkey[size++] = ']';
hkey[size++] = '-';
hkey[size] = '[';
size += 1;
size += eina_convert_xtoa(lo->scale_load.dst_w, hkey + size);
hkey[size] = 'x';
size += 1;
size += eina_convert_xtoa(lo->scale_load.dst_h, hkey + size);
hkey[size] = ':';
size += 1;
size += eina_convert_xtoa(lo->scale_load.smooth, hkey + size);
hkey[size++] = ']';
hkey[size++] = ')';
if (lo->orientation)
{
hkey[size] = '/';
size += 1;
hkey[size] = 'o';
size += 1;
}
}
hkey[size] = '\0';
}
EAPI Image_Entry *
evas_cache2_image_open(Evas_Cache2 *cache, const char *path, const char *key, RGBA_Image_Loadopts *lo, int *error)
{
size_t size;
size_t pathlen;
size_t keylen;
char *hkey;
Image_Entry *im;
int stat_done = 0, stat_failed = 0;
struct stat st;
Image_Timestamp tstamp;
Evas_Image_Load_Opts prevent = { 0, 0.0, 0, 0, 0, { 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0 }, EINA_FALSE };
if ((!path) || ((!path) && (!key)))
{
*error = EVAS_LOAD_ERROR_GENERIC;
return NULL;
}
pathlen = strlen(path);
keylen = key ? strlen(key) : 6;
size = pathlen + keylen + HKEY_LOAD_OPTS_STR_LEN;
hkey = alloca(sizeof(char) * size);
_create_hash_key(hkey, path, pathlen, key, keylen, lo);
DBG("Looking at the hash for key '%s'", hkey);
/* use local var to copy default load options to the image entry */
if ((!lo) ||
(lo &&
(lo->scale_down_by == 0) &&
(lo->dpi == 0.0) &&
((lo->w == 0) || (lo->h == 0)) &&
((lo->region.w == 0) || (lo->region.h == 0)) &&
((lo->scale_load.dst_w == 0) || (lo->scale_load.dst_h == 0)) &&
(lo->orientation == 0)
))
{
lo = &prevent;
}
im = eina_hash_find(cache->activ, hkey);
if (im)
{
int ok = 1;
DBG("Found entry on active hash for key: '%s'", hkey);
stat_done = 1;
if (stat(path, &st) < 0)
{
stat_failed = 1;
ok = 0;
}
else if (!_timestamp_compare(&(im->tstamp), &st)) ok = 0;
if (ok) goto on_ok;
/* image we found doesn't match what's on disk (stat info wise)
* so dirty the active cache entry so we never find it again. this
* also implicitly guarantees that we only have 1 active copy
* of an image at a given key. we wither find it and keep re-reffing
* it or we dirty it and get it out */
DBG("Entry on inactive hash was invalid (file changed or deleted).");
_evas_cache_image_dirty_add(im);
im = NULL;
}
im = eina_hash_find(cache->inactiv, hkey);
if (im)
{
int ok = 1;
DBG("Found entry on inactive hash for key: '%s'", hkey);
if (!stat_done)
{
stat_done = 1;
if (stat(path, &st) < 0)
{
stat_failed = 1;
ok = 0;
}
else if (!_timestamp_compare(&(im->tstamp), &st)) ok = 0;
}
else if (!_timestamp_compare(&(im->tstamp), &st)) ok = 0;
if (ok)
{
/* remove from lru and make it active again */
_evas_cache_image_lru_del(im);
_evas_cache_image_activ_add(im);
goto on_ok;
}
DBG("Entry on inactive hash was invalid (file changed or deleted).");
/* as avtive cache find - if we match in lru and its invalid, dirty */
_evas_cache_image_dirty_add(im);
/* this image never used, so it have to be deleted */
_evas_cache_image_entry_delete(cache, im);
im = NULL;
}
if (stat_failed) goto on_stat_error;
if (!stat_done)
{
if (stat(path, &st) < 0) goto on_stat_error;
}
_timestamp_build(&tstamp, &st);
DBG("Creating a new entry for key '%s'.", hkey);
im = _evas_cache_image_entry_new(cache, hkey, &tstamp, path, key,
lo, error);
if (!im) goto on_stat_error;
on_ok:
*error = EVAS_LOAD_ERROR_NONE;
DBG("Using entry on hash for key '%s'", hkey);
im->references++;
return im;
on_stat_error:
#ifndef _WIN32
if ((errno == ENOENT) || (errno == ENOTDIR) ||
(errno == ENAMETOOLONG) || (errno == ELOOP))
#else
if (errno == ENOENT)
#endif
*error = EVAS_LOAD_ERROR_DOES_NOT_EXIST;
#ifndef _WIN32
else if ((errno == ENOMEM) || (errno == EOVERFLOW))
#else
else if (errno == ENOMEM)
#endif
*error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
else if (errno == EACCES)
*error = EVAS_LOAD_ERROR_PERMISSION_DENIED;
else
*error = EVAS_LOAD_ERROR_GENERIC;
if (im) _evas_cache_image_entry_delete(cache, im);
return NULL;
}
EAPI int
evas_cache2_image_open_wait(Image_Entry *im)
{
DBG("Wait for open image '%s' '%s'", im->file, im->key);
if (evas_cserve2_image_load_wait(im) != CSERVE2_NONE)
return EVAS_LOAD_ERROR_GENERIC;
return EVAS_LOAD_ERROR_NONE;
}
static Image_Entry *
_scaled_image_find(Image_Entry *im, int src_x, int src_y, int src_w, int src_h, int dst_w, int dst_h, int smooth)
{
size_t pathlen, keylen, size;
char *hkey;
RGBA_Image_Loadopts lo;
Image_Entry *ret;
if (((!im->file) || ((!im->file) && (!im->key))) || (!im->data1) ||
((src_w == dst_w) && (src_h == dst_h)) ||
((!im->flags.alpha) && (!smooth))) return NULL;
pathlen = strlen(im->file);
keylen = im->key ? strlen(im->key) : 6;
size = pathlen + keylen + HKEY_LOAD_OPTS_STR_LEN;
hkey = alloca(sizeof(char) * size);
memcpy(&lo, &im->load_opts, sizeof lo);
lo.scale_load.src_x = src_x;
lo.scale_load.src_y = src_y;
lo.scale_load.src_w = src_w;
lo.scale_load.src_h = src_h;
lo.scale_load.dst_w = dst_w;
lo.scale_load.dst_h = dst_h;
lo.scale_load.smooth = smooth;
if (!smooth)
{
lo.scale_load.smooth = 1;
_create_hash_key(hkey, im->file, pathlen, im->key, keylen, &lo);
ret = eina_hash_find(im->cache2->activ, hkey);
if (ret) goto found;
ret = eina_hash_find(im->cache2->inactiv, hkey);
if (ret) goto handle_inactiv;
lo.scale_load.smooth = smooth;
}
_create_hash_key(hkey, im->file, pathlen, im->key, keylen, &lo);
ret = eina_hash_find(im->cache2->activ, hkey);
if (ret) goto found;
ret = eina_hash_find(im->cache2->inactiv, hkey);
handle_inactiv:
if (!ret) return NULL;
/* Remove from lru and make it active again */
_evas_cache_image_lru_del(ret);
_evas_cache_image_activ_add(ret);
found:
evas_cache2_image_load_data(ret);
return ret;
}
EAPI Image_Entry *
evas_cache2_image_scale_load(Image_Entry *im, int src_x, int src_y, int src_w, int src_h, int dst_w, int dst_h, int smooth)
{
size_t pathlen, keylen, size;
char *hkey;
RGBA_Image_Loadopts lo;
int error = EVAS_LOAD_ERROR_NONE;
Image_Entry *ret;
if (((!im->file) || ((!im->file) && (!im->key))) ||
((src_w == 0) || (src_h == 0) || (dst_w == 0) || (dst_h == 0)) ||
(im->scale_hint == EVAS_IMAGE_SCALE_HINT_DYNAMIC)) goto parent_out;
if (((src_w == dst_w) && (src_h == dst_h)) ||
((!im->flags.alpha) && (!smooth))) goto parent_out;
ret = _scaled_image_find(im, src_x, src_y, src_w, src_h,
dst_w, dst_h, smooth);
if (ret) return ret;
pathlen = strlen(im->file);
keylen = im->key ? strlen(im->key) : 6;
size = pathlen + keylen + HKEY_LOAD_OPTS_STR_LEN;
hkey = alloca(sizeof(char) * size);
memcpy(&lo, &im->load_opts, sizeof lo);
lo.scale_load.src_x = src_x;
lo.scale_load.src_y = src_y;
lo.scale_load.src_w = src_w;
lo.scale_load.src_h = src_h;
lo.scale_load.dst_w = dst_w;
lo.scale_load.dst_h = dst_h;
lo.scale_load.smooth = smooth;
lo.scale_load.scale_hint = im->scale_hint;
_create_hash_key(hkey, im->file, pathlen, im->key, keylen, &lo);
ret = _evas_cache_image_entry_new(im->cache2, hkey, NULL, im->file, im->key,
&lo, &error);
if (error != EVAS_LOAD_ERROR_NONE)
{
ERR("Failed to create scale image entry with error code %d.", error);
if (ret) _evas_cache_image_entry_delete(im->cache2, ret);
goto parent_out;
}
evas_cserve2_image_load_wait(ret);
evas_cache2_image_load_data(ret);
ret->references++;
ret->w = dst_w;
ret->h = dst_h;
return ret;
parent_out:
evas_cache2_image_load_data(im);
return im;
}
EAPI void
evas_cache2_image_ref(Image_Entry *im)
{
im->references++;
}
EAPI void
evas_cache2_image_close(Image_Entry *im)
{
Evas_Cache2 *cache;
int references;
im->references--;
if (im->references < 0)
{
ERR("image with negative references: %d", im->references);
im->references = 0;
}
references = im->references;
cache = im->cache2;
if (references > 0)
return;
if (im->flags.dirty)
{
_evas_cache_image_entry_delete(cache, im);
return;
}
_evas_cache_image_lru_add(im);
if (cache)
evas_cache2_flush(cache);
}
EAPI int
evas_cache2_image_load_data(Image_Entry *ie)
{
int error = EVAS_LOAD_ERROR_NONE;
if ((ie->flags.loaded) && (!ie->flags.animated))
return error;
ie->flags.in_progress = EINA_TRUE;
DBG("try cserve2 image data '%s' '%s'",
ie->file, ie->key ? ie->key : "");
if (evas_cserve2_image_data_load(ie))
{
evas_cserve2_image_load_data_wait(ie);
RGBA_Image *im = (RGBA_Image *)ie;
DBG("try cserve2 image data '%s' '%s' loaded!",
ie->file, ie->key ? ie->key : "");
if (im->image.data)
{
error = EVAS_LOAD_ERROR_NONE;
}
else
{
ERR("Failed to load data for image '%s' '%s'.",
ie->file, ie->key ? ie->key : "");
error = EVAS_LOAD_ERROR_GENERIC;
}
}
else
{
ERR("Couldn't send LOAD message to cserve2.");
error = EVAS_LOAD_ERROR_GENERIC;
}
ie->flags.in_progress = EINA_FALSE;
ie->flags.loaded = 1;
if (error != EVAS_LOAD_ERROR_NONE)
ie->flags.loaded = 0;
return error;
}
EAPI void
evas_cache2_image_unload_data(Image_Entry *im)
{
// FIXME: This function seems pretty useless, since we always have
// to send an UNLOAD message to the server when closing an image,
// even if we didn't send a LOAD message before, because the SETOPTS
// message increases the image refcount.
if (im->flags.in_progress)
return;
if ((!im->file))
return;
if (!im->flags.loaded)
return;
}
EAPI void
evas_cache2_image_preload_data(Image_Entry *im, const void *target)
{
RGBA_Image *img = (RGBA_Image *)im;
if ((im->flags.loaded) && (img->image.data))
{
evas_object_inform_call_image_preloaded((Evas_Object *)target);
return;
}
if (!_evas_cache2_image_entry_preload_add(im, target))
evas_object_inform_call_image_preloaded((Evas_Object *)target);
}
EAPI void
evas_cache2_image_preload_cancel(Image_Entry *im, const void *target)
{
if (!target)
return;
_evas_cache2_image_entry_preload_remove(im, target);
}
EAPI DATA32 *
evas_cache2_image_pixels(Image_Entry *im)
{
return im->cache2->func.surface_pixels(im);
}
EAPI Image_Entry *
evas_cache2_image_writable(Image_Entry *im)
{
Evas_Cache2 *cache = im->cache2;
Image_Entry *im2 = NULL;
if (!im->cache_key)
{
if (!im->flags.dirty)
_evas_cache_image_dirty_add(im);
return im;
}
im2 = evas_cache2_image_copied_data(cache, im->w, im->h,
evas_cache2_image_pixels(im),
im->flags.alpha, im->space);
if (!im2)
goto on_error;
evas_cache2_image_close(im);
return im2;
on_error:
if (im2)
_evas_cache_image_entry_delete(cache, im2);
return NULL;
}
EAPI Image_Entry *
evas_cache2_image_dirty(Image_Entry *im, unsigned int x, unsigned int y, unsigned int w, unsigned int h)
{
Evas_Cache2 *cache = im->cache2;
Image_Entry *im2 = NULL;
if (!im->cache_key)
{
if (!im->flags.dirty)
_evas_cache_image_dirty_add(im);
im2 = im;
}
else
{
im2 = evas_cache2_image_copied_data(cache, im->w, im->h,
evas_cache2_image_pixels(im),
im->flags.alpha, im->space);
if (!im2)
goto on_error;
evas_cache2_image_close(im);
}
if (cache->func.dirty_region)
cache->func.dirty_region(im2, x, y, w, h);
return im2;
on_error:
if (im2)
_evas_cache_image_entry_delete(cache, im2);
evas_cache2_image_close(im);
return NULL;
}
EAPI int
evas_cache2_flush(Evas_Cache2 *cache)
{
if (cache->limit == -1) return -1;
while ((cache->lru) && (cache->limit < cache->usage))
{
Image_Entry *im;
im = (Image_Entry *)cache->lru->last;
DBG("Remove unused entry from cache.");
_evas_cache_image_entry_delete(cache, im);
}
return cache->usage;
}
EAPI void
evas_cache2_limit_set(Evas_Cache2 *cache, int limit)
{
if (cache->limit == limit)
return;
DBG("Cache2 limit set to %d", limit);
cache->limit = limit;
evas_cache2_flush(cache);
}
EAPI int
evas_cache2_limit_get(Evas_Cache2 *cache)
{
return cache->limit;
}
EAPI int
evas_cache2_usage_get(Evas_Cache2 *cache)
{
return cache->usage;
}