efl/src/lib/evas/cache/evas_cache_engine_image.c

677 lines
17 KiB
C

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <assert.h>
#include "evas_common_private.h"
#include "evas_private.h"
static void
_evas_cache_engine_image_make_dirty(Evas_Cache_Engine_Image *cache,
Engine_Image_Entry *eim)
{
eim->flags.cached = 1;
eim->flags.dirty = 1;
eim->flags.loaded = 1;
eim->flags.activ = 0;
cache->dirty = eina_inlist_prepend(cache->dirty, EINA_INLIST_GET(eim));
}
static void
_evas_cache_engine_image_make_active(Evas_Cache_Engine_Image *cache,
Engine_Image_Entry *eim,
const char *key)
{
eim->flags.cached = 1;
eim->flags.activ = 1;
eim->flags.dirty = 0;
eina_hash_add(cache->activ, key, eim);
}
static void
_evas_cache_engine_image_make_inactive(Evas_Cache_Engine_Image *cache,
Engine_Image_Entry *eim,
const char *key)
{
eim->flags.cached = 1;
eim->flags.dirty = 0;
eim->flags.activ = 0;
eina_hash_add(cache->inactiv, key, eim);
cache->lru = eina_inlist_prepend(cache->lru, EINA_INLIST_GET(eim));
cache->usage += cache->func.mem_size_get(eim);
}
static void
_evas_cache_engine_image_remove_activ(Evas_Cache_Engine_Image *cache,
Engine_Image_Entry *eim)
{
if (eim->flags.cached)
{
if (eim->flags.dirty)
cache->dirty = eina_inlist_remove(cache->dirty,
EINA_INLIST_GET(eim));
else
{
if (eim->flags.activ)
eina_hash_del(cache->activ, eim->cache_key, eim);
else
{
cache->usage -= cache->func.mem_size_get(eim);
eina_hash_del(cache->inactiv, eim->cache_key, eim);
cache->lru = eina_inlist_remove(cache->lru,
EINA_INLIST_GET(eim));
}
}
eim->flags.cached = 0;
eim->flags.dirty = 0;
eim->flags.activ = 0;
}
}
static Engine_Image_Entry *
_evas_cache_engine_image_alloc(Evas_Cache_Engine_Image *cache,
Image_Entry *ie,
const char *hkey)
{
Engine_Image_Entry *eim;
assert(cache);
if (cache->func.alloc) eim = cache->func.alloc();
else eim = malloc(sizeof (Engine_Image_Entry));
if (!eim) goto on_error;
memset(eim, 0, sizeof (Engine_Image_Entry));
eim->cache = cache;
if (ie)
{
eim->w = ie->w;
eim->h = ie->h;
eim->src = ie;
eim->flags.need_parent = 1;
}
else
{
eim->w = -1;
eim->h = -1;
eim->flags.need_parent = 0;
eim->src = NULL;
}
eim->flags.cached = 0;
eim->references = 0;
eim->cache_key = hkey;
if (hkey) _evas_cache_engine_image_make_active(cache, eim, hkey);
else _evas_cache_engine_image_make_dirty(cache, eim);
return eim;
on_error:
eina_stringshare_del(hkey);
if (ie) evas_cache_image_drop(ie);
return NULL;
}
static void
_evas_cache_engine_image_dealloc(Evas_Cache_Engine_Image *cache,
Engine_Image_Entry *eim)
{
Image_Entry *im;
if (cache->func.debug) cache->func.debug("delete", eim);
_evas_cache_engine_image_remove_activ(cache, eim);
im = eim->src;
cache->func.destructor(eim);
if (im) evas_cache_image_drop(im);
if (cache->func.dealloc) cache->func.dealloc(eim);
else
{
memset(eim, 0, sizeof(Engine_Image_Entry));
eina_freeq_ptr_add(eina_freeq_main_get(), eim, free, sizeof(*eim));
}
}
EAPI int
evas_cache_engine_image_usage_get(Evas_Cache_Engine_Image *cache)
{
assert(cache != NULL);
return cache->usage;
}
EAPI int
evas_cache_engine_image_get(Evas_Cache_Engine_Image *cache)
{
assert(cache != NULL);
return cache->limit;
}
EAPI void
evas_cache_engine_image_set(Evas_Cache_Engine_Image *cache, int limit)
{
assert(cache != NULL);
cache->limit = limit;
}
EAPI Evas_Cache_Engine_Image *
evas_cache_engine_image_init(const Evas_Cache_Engine_Image_Func *cb,
Evas_Cache_Image *parent)
{
Evas_Cache_Engine_Image *new;
new = malloc(sizeof (Evas_Cache_Engine_Image));
if (!new) return NULL;
new->func = *cb;
new->limit = 0;
new->usage = 0;
new->dirty = NULL;
new->lru = NULL;
new->activ = eina_hash_string_superfast_new(NULL);
new->inactiv = eina_hash_string_superfast_new(NULL);
new->parent = parent;
parent->references++;
new->brother = NULL;
return new;
}
EAPI Evas_Cache_Engine_Image *
evas_cache_engine_image_dup(const Evas_Cache_Engine_Image_Func *cb,
Evas_Cache_Engine_Image *brother)
{
Evas_Cache_Engine_Image *new;
new = calloc(1, sizeof(Evas_Cache_Engine_Image));
if (!new) return NULL;
new->func = brother->func;
#define ORD(Func) if (cb->Func) new->func.Func = cb->Func;
ORD(key);
ORD(constructor);
ORD(destructor);
ORD(dirty_region);
ORD(dirty);
ORD(size_set);
ORD(update_data);
ORD(load);
ORD(mem_size_get);
ORD(debug);
#undef ORD
new->limit = -1;
new->usage = 0;
new->references = 1;
new->dirty = NULL;
new->activ = NULL;
new->parent = brother->parent;
new->parent->references++;
new->brother = brother;
brother->references++;
return new;
}
static Eina_Bool
_evas_cache_engine_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_cache_engine_image_flush(Evas_Cache_Engine_Image *cache)
{
assert(cache != NULL);
while ((cache->lru) && (cache->limit < cache->usage))
{
Engine_Image_Entry *eim;
eim = (Engine_Image_Entry *)cache->lru->last;
_evas_cache_engine_image_dealloc(cache, eim);
}
}
EAPI void
evas_cache_engine_image_shutdown(Evas_Cache_Engine_Image *cache)
{
Engine_Image_Entry *eim;
Eina_List *delete_list = NULL;
assert(cache != NULL);
if (cache->func.debug) cache->func.debug("shutdown-engine", NULL);
eina_hash_foreach(cache->inactiv, _evas_cache_engine_image_free_cb,
&delete_list);
eina_hash_foreach(cache->activ, _evas_cache_engine_image_free_cb,
&delete_list);
while (delete_list)
{
_evas_cache_engine_image_dealloc(cache,
eina_list_data_get(delete_list));
delete_list = eina_list_remove_list(delete_list, delete_list);
}
eina_hash_free(cache->inactiv);
eina_hash_free(cache->activ);
/* This is mad, I am about to destroy image still alive, but we need to
* prevent leak. */
while (cache->dirty)
{
eim = (Engine_Image_Entry *)cache->dirty;
_evas_cache_engine_image_dealloc(cache, eim);
}
evas_cache_image_shutdown(cache->parent);
if (cache->brother) evas_cache_engine_image_shutdown(cache->brother);
free(cache);
}
EAPI Engine_Image_Entry *
evas_cache_engine_image_request(Evas_Cache_Engine_Image *cache,
const char *file, const char *key,
Evas_Image_Load_Opts *lo, void *data,
int *error)
{
Engine_Image_Entry *eim;
Image_Entry *im;
const char *ekey;
assert(cache != NULL);
*error = EVAS_LOAD_ERROR_NONE;
ekey = NULL;
eim = NULL;
im = evas_cache_image_request(cache->parent, file, key, lo, error);
if (!im) goto on_error;
if (cache->func.key) ekey = cache->func.key(im, file, key, lo, data);
else ekey = eina_stringshare_add(im->cache_key);
if (!ekey)
{
*error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
goto on_error;
}
eim = eina_hash_find(cache->activ, ekey);
if (eim)
{
evas_cache_image_drop(im);
goto on_ok;
}
eim = eina_hash_find(cache->inactiv, ekey);
if (eim)
{
_evas_cache_engine_image_remove_activ(cache, eim);
_evas_cache_engine_image_make_active(cache, eim, ekey);
evas_cache_image_drop(im);
goto on_ok;
}
eim = _evas_cache_engine_image_alloc(cache, im, ekey);
if (!eim)
{
*error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
return NULL;
}
*error = cache->func.constructor(eim, data);
if (*error != EVAS_LOAD_ERROR_NONE) goto on_error;
if (cache->func.debug) cache->func.debug("constructor-engine", eim);
on_ok:
eim->references++;
return eim;
on_error:
if (!eim)
{
if (im) evas_cache_image_drop(im);
}
else _evas_cache_engine_image_dealloc(cache, eim);
return NULL;
}
EAPI void
evas_cache_engine_image_drop(Engine_Image_Entry *eim)
{
Evas_Cache_Engine_Image *cache;
assert(eim);
assert(eim->cache);
eim->references--;
cache = eim->cache;
if (eim->flags.dirty)
{
_evas_cache_engine_image_dealloc(cache, eim);
return;
}
if (eim->references == 0)
{
_evas_cache_engine_image_remove_activ(cache, eim);
_evas_cache_engine_image_make_inactive(cache, eim, eim->cache_key);
evas_cache_engine_image_flush(cache);
return;
}
}
EAPI Engine_Image_Entry *
evas_cache_engine_image_dirty(Engine_Image_Entry *eim,
unsigned int x, unsigned int y,
unsigned int w, unsigned int h)
{
Engine_Image_Entry *eim_dirty = eim;
Image_Entry *im_dirty = NULL;
Image_Entry *im;
Evas_Cache_Engine_Image *cache;
unsigned char alloc_eim;
assert(eim);
assert(eim->cache);
cache = eim->cache;
if (!(eim->flags.dirty))
{
alloc_eim = 0;
if (eim->flags.need_parent == 1)
{
im = eim->src;
im_dirty = evas_cache_image_dirty(im, x, y, w, h);
/* If im == im_dirty, this meens that we have only one
* reference to the eim. */
if (im != im_dirty)
{
if (eim->references == 1)
{
_evas_cache_engine_image_remove_activ(cache, eim);
_evas_cache_engine_image_make_dirty(cache, eim);
eim->src = im_dirty;
}
else alloc_eim = 1;
}
}
else
{
if (eim->references > 1) alloc_eim = 1;
else
{
_evas_cache_engine_image_remove_activ(cache, eim_dirty);
_evas_cache_engine_image_make_dirty(cache, eim_dirty);
}
}
if (alloc_eim == 1)
{
int error;
eim_dirty = _evas_cache_engine_image_alloc(cache, im_dirty, NULL);
if (!eim_dirty) goto on_error;
eim_dirty->w = eim->w;
eim_dirty->h = eim->h;
eim_dirty->references = 1;
error = cache->func.dirty(eim_dirty, eim);
if (cache->func.debug)
cache->func.debug("dirty-engine", eim_dirty);
if (error != 0) goto on_error;
evas_cache_engine_image_drop(eim);
}
}
if (cache->func.dirty_region)
cache->func.dirty_region(eim_dirty, x, y, w, h);
if (cache->func.debug)
cache->func.debug("dirty-region-engine", eim_dirty);
return eim_dirty;
on_error:
evas_cache_engine_image_drop(eim);
if (eim_dirty && eim_dirty != eim) evas_cache_engine_image_drop(eim_dirty);
else if (im_dirty) evas_cache_image_drop(im_dirty);
return NULL;
}
EAPI Engine_Image_Entry *
evas_cache_engine_image_alone(Engine_Image_Entry *eim, void *data)
{
Evas_Cache_Engine_Image *cache;
Image_Entry *im;
assert(eim);
assert(eim->cache);
cache = eim->cache;
im = evas_cache_image_alone(eim->src);
if (im != eim->src)
{
eim = _evas_cache_engine_image_alloc(cache, im, NULL);
if (!eim) goto on_error;
eim->references = 1;
if (cache->func.constructor(eim, data) != EVAS_LOAD_ERROR_NONE)
goto on_error;
}
/* FIXME */
return eim;
on_error:
evas_cache_image_drop(im);
return NULL;
}
static Engine_Image_Entry *
_evas_cache_engine_image_push_dirty(Evas_Cache_Engine_Image *cache,
Image_Entry *im, void *engine_data)
{
Engine_Image_Entry *eim;
int error;
eim = _evas_cache_engine_image_alloc(cache, im, NULL);
if (!eim) goto on_error;
eim->references = 1;
error = cache->func.update_data(eim, engine_data);
if (cache->func.debug) cache->func.debug("dirty-update_data-engine", eim);
if (error != 0) goto on_error;
return eim;
on_error:
if (eim) evas_cache_engine_image_drop(eim);
return NULL;
}
EAPI Engine_Image_Entry *
evas_cache_engine_image_copied_data(Evas_Cache_Engine_Image *cache,
unsigned int w, unsigned int h,
DATA32 *image_data, int alpha,
Evas_Colorspace cspace, void *engine_data)
{
Image_Entry *im;
assert(cache);
im = evas_cache_image_copied_data(cache->parent, w, h, image_data,
alpha, cspace);
return _evas_cache_engine_image_push_dirty(cache, im, engine_data);
}
EAPI Engine_Image_Entry *
evas_cache_engine_image_data(Evas_Cache_Engine_Image *cache,
unsigned int w, unsigned int h,
DATA32 *image_data, int alpha,
Evas_Colorspace cspace, void *engine_data)
{
Image_Entry *im;
assert(cache);
im = evas_cache_image_data(cache->parent, w, h, image_data, alpha, cspace);
return _evas_cache_engine_image_push_dirty(cache, im, engine_data);
}
EAPI Engine_Image_Entry *
evas_cache_engine_image_size_set(Engine_Image_Entry *eim,
unsigned int w, unsigned int h)
{
Evas_Cache_Engine_Image *cache;
Engine_Image_Entry *new;
Image_Entry *im;
const char *hkey;
int error;
assert(eim);
assert(eim->cache);
assert(eim->references > 0);
im = NULL;
cache = eim->cache;
if (eim->flags.need_parent == 1)
{
assert(eim->src);
if ((eim->src->w == w) && (eim->src->h == h)) return eim;
im = evas_cache_image_size_set(eim->src, w, h);
/* FIXME: Good idea to call update_data ? */
if (im == eim->src) return eim;
eim->src = NULL;
}
hkey = (eim->references > 1 ) ? eina_stringshare_add(eim->cache_key) : NULL;
new = _evas_cache_engine_image_alloc(cache, im, hkey);
if (!new) goto on_error;
new->w = w;
new->h = h;
new->references = 1;
error = cache->func.size_set(new, eim);
if (error) goto on_error;
evas_cache_engine_image_drop(eim);
return new;
on_error:
if (new) evas_cache_engine_image_drop(new);
else if (im) evas_cache_image_drop(im);
evas_cache_engine_image_drop(eim);
return NULL;
}
EAPI void
evas_cache_engine_image_load_data(Engine_Image_Entry *eim)
{
Evas_Cache_Engine_Image *cache;
int size = 0;
assert(eim);
assert(eim->src);
assert(eim->cache);
if (eim->flags.loaded) return;
if (eim->src) evas_cache_image_load_data(eim->src);
cache = eim->cache;
if (cache->func.debug) cache->func.debug("load-engine", eim);
if (eim->flags.dirty) size = cache->func.mem_size_get(eim);
cache = eim->cache;
cache->func.load(eim, eim->src);
if (eim->flags.dirty) cache->usage += cache->func.mem_size_get(eim) - size;
eim->flags.loaded = 1;
}
EAPI Engine_Image_Entry *
evas_cache_engine_image_engine(Evas_Cache_Engine_Image *cache,
void *engine_data)
{
Engine_Image_Entry *eim;
Image_Entry *ie;
int error;
ie = evas_cache_image_empty(cache->parent);
if (!ie) return NULL;
eim = _evas_cache_engine_image_alloc(cache, ie, NULL);
if (!eim) goto on_error;
eim->references = 1;
error = cache->func.update_data(eim, engine_data);
if (cache->func.debug) cache->func.debug("update_data-engine", eim);
if (error != 0) goto on_error;
return eim;
on_error:
if (!eim) evas_cache_image_drop(ie);
else evas_cache_engine_image_drop(eim);
return NULL;
}
EAPI void
evas_cache_engine_image_colorspace(Engine_Image_Entry *eim,
Evas_Colorspace cspace, void *engine_data)
{
Evas_Cache_Engine_Image *cache = eim->cache;
assert(cache);
cache->func.destructor(eim);
evas_cache_image_colorspace(eim->src, cspace);
cache->func.constructor(eim, engine_data);
if (cache->func.debug)
cache->func.debug("cosntructor-colorspace-engine", eim);
}
EAPI void
evas_cache_engine_parent_not_needed(Engine_Image_Entry *eim)
{
assert(eim);
assert(eim->cache);
eim->flags.need_parent = 0;
evas_cache_image_data_not_needed(eim->src);
}