#ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #ifdef _WIN32 # include #endif #include "evas_common_private.h" #include "evas_private.h" //#define CACHEDUMP 1 typedef struct _Evas_Cache_Preload Evas_Cache_Preload; struct _Evas_Cache_Preload { EINA_INLIST; Image_Entry *ie; }; static SLK(engine_lock); static int _evas_cache_mutex_init = 0; static void _evas_cache_image_entry_preload_remove(Image_Entry *ie, const Eo *target, Eina_Bool force); #define FREESTRC(Var) \ if (Var) \ { \ eina_stringshare_del(Var); \ Var = NULL; \ } 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_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; if (!im->cache) 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->cache->dirty = eina_inlist_prepend(im->cache->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->cache) return; im->flags.dirty = 0; im->flags.cached = 0; im->cache->dirty = eina_inlist_remove(im->cache->dirty, EINA_INLIST_GET(im)); } static void _evas_cache_image_activ_add(Image_Entry *im) { if (im->flags.activ) return; if (!im->cache) 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; if (im->flags.given_mmap) eina_hash_direct_add(im->cache->mmap_activ, im->cache_key, im); else eina_hash_direct_add(im->cache->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; if (!im->cache) return; im->flags.activ = 0; im->flags.cached = 0; if (im->flags.given_mmap) eina_hash_del(im->cache->mmap_activ, im->cache_key, im); else eina_hash_del(im->cache->activ, im->cache_key, im); } static void _evas_cache_image_lru_add(Image_Entry *im) { if (im->flags.lru) return; if (!im->cache) 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; if (im->flags.given_mmap) eina_hash_direct_add(im->cache->mmap_inactiv, im->cache_key, im); else eina_hash_direct_add(im->cache->inactiv, im->cache_key, im); im->cache->lru = eina_inlist_prepend(im->cache->lru, EINA_INLIST_GET(im)); im->cache->usage += im->cache->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; if (!im->cache) return; im->flags.lru = 0; im->flags.cached = 0; if (im->flags.given_mmap) eina_hash_del(im->cache->mmap_inactiv, im->cache_key, im); else eina_hash_del(im->cache->inactiv, im->cache_key, im); im->cache->lru = eina_inlist_remove(im->cache->lru, EINA_INLIST_GET(im)); im->cache->usage -= im->cache->func.mem_size_get(im); } static void _evas_cache_image_lru_nodata_add(Image_Entry *im) { if (im->flags.lru_nodata) return; if (!im->cache) 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->cache->lru_nodata = eina_inlist_prepend(im->cache->lru_nodata, EINA_INLIST_GET(im)); } static void _evas_cache_image_lru_nodata_del(Image_Entry *im) { if (!im->flags.lru_nodata) return; if (!im->cache) return; im->flags.lru = 0; im->flags.cached = 0; im->cache->lru_nodata = eina_inlist_remove(im->cache->lru_nodata, EINA_INLIST_GET(im)); } static void _evas_cache_image_entry_delete(Evas_Cache_Image *cache, Image_Entry *ie) { if (!ie) return; if (!ie->cache) return; //// SLKL(ie->lock); //// SLKU(ie->lock); if ((cache) && (cache->func.debug)) cache->func.debug("deleting", ie); if (ie->flags.delete_me == 1) return; if (ie->preload) { ie->flags.delete_me = 1; _evas_cache_image_entry_preload_remove(ie, NULL, EINA_TRUE); 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); cache->func.destructor(ie); FREESTRC(ie->cache_key); FREESTRC(ie->file); FREESTRC(ie->key); if (ie->f && ie->flags.given_mmap) eina_file_close(ie->f); ie->cache = NULL; cache->func.surface_delete(ie); SLKD(ie->lock); SLKD(ie->lock_cancel); cache->func.dealloc(ie); } #if 0 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 } #endif static Image_Entry * _evas_cache_image_entry_new(Evas_Cache_Image *cache, const char *hkey, Image_Timestamp *tstamp, Eina_File *f, const char *file, const char *key, Evas_Image_Load_Opts *lo, int *error) { Image_Entry *ie; if (!cache) { *error = EVAS_LOAD_ERROR_GENERIC; return NULL; } ie = cache->func.alloc(); if (!ie) { *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; return NULL; } ie->cache = 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; ie->f = eina_file_dup(f); ie->loader_data = NULL; if (ie->f) ie->flags.given_mmap = EINA_TRUE; 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)); SLKI(ie->lock); SLKI(ie->lock_cancel); if (lo) { ie->load_opts = *lo; } if (ie->file || ie->f) { *error = cache->func.constructor(ie); if (*error != EVAS_LOAD_ERROR_NONE) { _evas_cache_image_entry_delete(cache, ie); return NULL; } } if (cache->func.debug) cache->func.debug("build", ie); if (ie->cache_key) _evas_cache_image_activ_add(ie); else _evas_cache_image_dirty_add(ie); return ie; } static void _evas_cache_image_entry_surface_alloc__locked(Evas_Cache_Image *cache, Image_Entry *ie, unsigned int wmin, unsigned int hmin) { if ((ie->allocated.w == wmin) && (ie->allocated.h == hmin)) return; if ((cache->func.surface_alloc(ie, wmin, hmin)) || (ie->load_failed)) { wmin = 0; hmin = 0; } ie->w = wmin; ie->h = hmin; } static void _evas_cache_image_entry_surface_alloc(Evas_Cache_Image *cache, Image_Entry *ie, int w, int h) { int wmin = w > 0 ? w : 1; int hmin = h > 0 ? h : 1; SLKL(engine_lock); _evas_cache_image_entry_surface_alloc__locked(cache, ie, wmin, hmin); SLKU(engine_lock); } static Eina_Bool evas_cache_image_cancelled(void *data) { Image_Entry *current = data; Eina_Bool ret; evas_cache_image_ref(current); ret = evas_preload_thread_cancelled_is(current->preload); evas_cache_image_drop(current); return ret; } static void _evas_cache_image_async_heavy(void *data) { Evas_Cache_Image *cache; Image_Entry *current; int error; int pchannel; eina_thread_name_set(eina_thread_self(), "Evas-preload"); current = data; if (!current->cache) return; SLKL(current->lock); pchannel = current->channel; current->channel++; cache = current->cache; if ((!current->flags.loaded) && current->info.loader->threadable) { evas_module_task_register(evas_cache_image_cancelled, current); error = cache->func.load(current); evas_module_task_unregister(); if (cache->func.debug) cache->func.debug("load", current); current->load_error = error; if (error != EVAS_LOAD_ERROR_NONE) { current->flags.loaded = 0; _evas_cache_image_entry_surface_alloc(cache, current, current->w, current->h); } } current->channel = pchannel; // check the unload cancel flag SLKL(current->lock_cancel); if (current->flags.unload_cancel) { current->flags.unload_cancel = EINA_FALSE; cache->func.surface_delete(current); current->flags.loaded = 0; current->flags.preload_done = 0; } SLKU(current->lock_cancel); SLKU(current->lock); } static void _evas_cache_image_preloaded_notify(Image_Entry *ie) { Evas_Cache_Target *tmp; while ((tmp = ie->targets)) { ie->targets = (Evas_Cache_Target *) eina_inlist_remove(EINA_INLIST_GET(ie->targets), EINA_INLIST_GET(ie->targets)); if (!tmp->delete_me && tmp->preloaded_cb) tmp->preloaded_cb(tmp->preloaded_data); if (!tmp->preload_cancel) evas_object_inform_call_image_preloaded((Eo*) tmp->target); free(tmp); } } static void _evas_cache_image_async_end(void *data) { Image_Entry *ie = (Image_Entry *)data; if (!ie->cache) return; evas_cache_image_ref(ie); ie->cache->preload = eina_list_remove(ie->cache->preload, ie); ie->cache->pending = eina_list_remove(ie->cache->pending, ie); ie->preload = NULL; ie->flags.preload_done = ie->flags.loaded; ie->flags.updated_data = 1; ie->flags.preload_pending = 0; ie->flags.loaded = EINA_TRUE; _evas_cache_image_preloaded_notify(ie); evas_cache_image_drop(ie); } static void _evas_cache_image_async_cancel(void *data) { Evas_Cache_Image *cache = NULL; Image_Entry *ie = (Image_Entry *)data; if (!ie->cache) return; evas_cache_image_ref(ie); ie->preload = NULL; ie->cache->pending = eina_list_remove(ie->cache->pending, ie); ie->flags.preload_pending = 0; if ((ie->flags.delete_me) || (ie->flags.dirty)) { SLKL(engine_lock); ie->flags.delete_me = 0; SLKU(engine_lock); evas_cache_image_drop(ie); return; } if (ie->references == 0) { SLKL(engine_lock); _evas_cache_image_lru_add(ie); SLKU(engine_lock); cache = ie->cache; } if (ie->flags.loaded) _evas_cache_image_async_end(ie); //On Cancelling, they need to draw image directly. else { while (ie->targets) { Evas_Cache_Target *tg = ie->targets; ie->targets = (Evas_Cache_Target *) eina_inlist_remove(EINA_INLIST_GET(ie->targets), EINA_INLIST_GET(tg)); //FIXME: What/When they properly get a notification? Call in advance for compatibility. evas_object_inform_call_image_preloaded((Eo*) tg->target); free(tg); } } evas_cache_image_drop(ie); if (cache) evas_cache_image_flush(cache); } // note - preload_add assumes a target is ONLY added ONCE to the image // entry. make sure you only add once, or remove first, then add static int _evas_cache_image_entry_preload_add(Image_Entry *ie, const Eo *target, void (*preloaded_cb)(void *), void *preloaded_data) { Evas_Cache_Target *tg; if (!ie->cache) return 0; evas_cache_image_ref(ie); if (ie->flags.preload_done) { evas_cache_image_drop(ie); return 0; } tg = calloc(1, sizeof(Evas_Cache_Target)); if (!tg) return 0; tg->target = target; tg->preloaded_cb = preloaded_cb; tg->preloaded_data = preloaded_data; ie->targets = (Evas_Cache_Target *) eina_inlist_append(EINA_INLIST_GET(ie->targets), EINA_INLIST_GET(tg)); if (!ie->preload) { ie->cache->preload = eina_list_append(ie->cache->preload, ie); ie->flags.pending = 0; ie->flags.preload_pending = 1; ie->preload = evas_preload_thread_run(_evas_cache_image_async_heavy, _evas_cache_image_async_end, _evas_cache_image_async_cancel, ie); } evas_cache_image_drop(ie); return 1; } /* force: remove preload forcely. If one object cancel preload and draw image direcltly, * all other targets of that preload will be affected this as well. */ static void _evas_cache_image_entry_preload_remove(Image_Entry *ie, const Eo *target, Eina_Bool force) { Evas_Cache_Target *tg; if (!ie->cache) return; // evas_cache_image_ref(ie); if (target) { EINA_INLIST_FOREACH(ie->targets, tg) { if (tg->target == target) { tg->preload_cancel = EINA_TRUE; break; } } } else { 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); } } if ((!ie->targets || force) && (ie->preload && !ie->flags.pending)) { ie->cache->preload = eina_list_remove(ie->cache->preload, ie); ie->cache->pending = eina_list_append(ie->cache->pending, ie); ie->flags.pending = 1; evas_preload_thread_cancel(ie->preload); } // evas_cache_image_drop(ie); } EAPI int evas_cache_image_usage_get(Evas_Cache_Image *cache) { if (!cache) return 0; return cache->usage; } EAPI int evas_cache_image_get(Evas_Cache_Image *cache) { if (!cache) return 0; return cache->limit; } EAPI void evas_cache_image_set(Evas_Cache_Image *cache, unsigned int limit) { if (!cache) return; if (cache->limit == limit) { return; } cache->limit = limit; evas_cache_image_flush(cache); } EAPI Evas_Cache_Image * evas_cache_image_init(const Evas_Cache_Image_Func *cb) { Evas_Cache_Image *cache; if (_evas_cache_mutex_init++ == 0) { SLKI(engine_lock); } cache = calloc(1, sizeof(Evas_Cache_Image)); if (!cache) return NULL; cache->func = *cb; cache->inactiv = eina_hash_string_superfast_new(NULL); cache->activ = eina_hash_string_superfast_new(NULL); cache->mmap_activ = eina_hash_string_superfast_new(NULL); cache->mmap_inactiv = eina_hash_string_superfast_new(NULL); cache->references = 1; 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_cache_image_shutdown(Evas_Cache_Image *cache) { Eina_List *delete_list; Image_Entry *im; cache->references--; if (cache->references != 0) { return; } EINA_LIST_FREE(cache->preload, im) { /* By doing that we are protecting us from destroying image when the cache is no longer available. */ im->flags.delete_me = 1; _evas_cache_image_entry_preload_remove(im, NULL, EINA_TRUE); } evas_async_events_process(); SLKL(engine_lock); EINA_INLIST_FREE(cache->lru, im) _evas_cache_image_entry_delete(cache, im); EINA_INLIST_FREE(cache->lru_nodata, im) _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); eina_hash_foreach(cache->mmap_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); } SLKU(engine_lock); /* Now wait for all pending image to die */ while (cache->pending) { im = eina_list_data_get(cache->pending); evas_preload_thread_cancel(im->preload); evas_async_events_process(); if (!evas_preload_pthread_wait(im->preload, 1.0)) { // We have waited long enough without reaction from that said // thread, remove it from pending list and silently continue // in the hope of an ok shutdown (but something is wrong). cache->pending = eina_list_remove_list(cache->pending, cache->pending); ERR("Could not stop decoding '%s' during shutdown.\n", im->file); } } eina_hash_free(cache->activ); eina_hash_free(cache->inactiv); eina_hash_free(cache->mmap_activ); eina_hash_free(cache->mmap_inactiv); free(cache); if (--_evas_cache_mutex_init == 0) { SLKD(engine_lock); } } static const 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 }, EINA_FALSE }; static size_t _evas_cache_image_loadopts_append(char *hkey, Evas_Image_Load_Opts **plo) { Evas_Image_Load_Opts *lo = *plo; size_t offset = 0; if ((!lo) || (lo && (lo->emile.scale_down_by == 0) && (EINA_DBL_EQ(lo->emile.dpi, 0.0)) && ((lo->emile.w == 0) || (lo->emile.h == 0)) && ((lo->emile.region.w == 0) || (lo->emile.region.h == 0)) && (lo->emile.orientation == 0) )) { *plo = (Evas_Image_Load_Opts*) &prevent; } else { memcpy(hkey, "//@/", 4); offset += 4; offset += eina_convert_xtoa(lo->emile.scale_down_by, hkey + offset); hkey[offset] = '/'; offset += 1; offset += eina_convert_dtoa(lo->emile.dpi, hkey + offset); hkey[offset] = '/'; offset += 1; offset += eina_convert_xtoa(lo->emile.w, hkey + offset); hkey[offset] = 'x'; offset += 1; offset += eina_convert_xtoa(lo->emile.h, hkey + offset); hkey[offset] = '/'; offset += 1; offset += eina_convert_xtoa(lo->emile.region.x, hkey + offset); hkey[offset] = '+'; offset += 1; offset += eina_convert_xtoa(lo->emile.region.y, hkey + offset); hkey[offset] = '.'; offset += 1; offset += eina_convert_xtoa(lo->emile.region.w, hkey + offset); hkey[offset] = 'x'; offset += 1; offset += eina_convert_xtoa(lo->emile.region.h, hkey + offset); if (lo->emile.orientation) { hkey[offset] = '/'; offset += 1; hkey[offset] = 'o'; offset += 1; } } hkey[offset] = '\0'; return offset; } EAPI Image_Entry * evas_cache_image_mmap_request(Evas_Cache_Image *cache, Eina_File *f, const char *key, Evas_Image_Load_Opts *lo, int *error) { const char *hexcode = "0123456789abcdef"; const char *ckey = "(null)"; char *hkey; char *pf; Image_Entry *im; size_t size; size_t file_length; size_t key_length; unsigned int i; // FIXME: In the long term we should certainly merge both mmap and filename path // by just using the mmap path. But for the time being, let's just have two path // as it is unlikely to really have an impact on real world application if (!f) { *error = EVAS_LOAD_ERROR_GENERIC; return NULL; } /* generate hkey from file+key+load opts */ file_length = sizeof (Eina_File*) * 2; key_length = key ? strlen(key) : 6; size = file_length + key_length + 132; hkey = alloca(sizeof (char) * size); pf = (char*) &f; for (size = 0, i = 0; i < sizeof (Eina_File*); i++) { hkey[size++] = hexcode[(pf[i] & 0xF0) >> 4]; hkey[size++] = hexcode[(pf[i] & 0x0F)]; } memcpy(hkey + size, "//://", 5); size += 5; if (key) ckey = key; memcpy(hkey + size, ckey, key_length); size += key_length; size += _evas_cache_image_loadopts_append(hkey + size, &lo); /* find image by key in active mmap hash */ SLKL(engine_lock); im = eina_hash_find(cache->mmap_activ, hkey); if ((im) && (!im->load_failed)) goto on_ok; else if ((im) && (im->load_failed)) { _evas_cache_image_dirty_add(im); im = NULL; } /* find image by key in inactive/lru hash */ im = eina_hash_find(cache->mmap_inactiv, hkey); if ((im) && (!im->load_failed)) { _evas_cache_image_lru_del(im); _evas_cache_image_activ_add(im); goto on_ok; } else if ((im) && (im->load_failed)) { /* as active 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; } im = _evas_cache_image_entry_new(cache, hkey, NULL, f, NULL, key, lo, error); if (!im) { SLKU(engine_lock); return NULL; } on_ok: *error = EVAS_LOAD_ERROR_NONE; //// SLKL(im->lock); im->references++; //// SLKU(im->lock); SLKU(engine_lock); return im; } EAPI void evas_cache_image_ref(Image_Entry *im) { SLKL(engine_lock); //// SLKL(im->lock); im->references++; //// SLKU(im->lock); SLKU(engine_lock); } EAPI void evas_cache_image_drop(Image_Entry *im) { Evas_Cache_Image *cache; int references; if (!im->cache) return; SLKL(engine_lock); //// SLKL(im->lock); im->references--; if (im->references < 0) im->references = 0; references = im->references; //// SLKU(im->lock); SLKU(engine_lock); cache = im->cache; if (references == 0) { if (im->preload) { _evas_cache_image_entry_preload_remove(im, NULL, EINA_TRUE); return; } if ((im->flags.dirty) || (im->load_failed)) { SLKL(engine_lock); _evas_cache_image_entry_delete(cache, im); SLKU(engine_lock); return; } if (cache) { SLKL(engine_lock); _evas_cache_image_lru_add(im); SLKU(engine_lock); evas_cache_image_flush(cache); } } } EAPI void evas_cache_image_data_not_needed(Image_Entry *im) { int references; references = im->references; if (references > 1) return; if ((im->flags.dirty) || (!im->flags.need_data)) return; SLKL(engine_lock); _evas_cache_image_lru_nodata_add(im); SLKU(engine_lock); } EAPI Image_Entry * evas_cache_image_dirty(Image_Entry *im, unsigned int x, unsigned int y, unsigned int w, unsigned int h) { Image_Entry *im_dirty = im; Evas_Cache_Image *cache; if (!im->cache) return NULL; cache = im->cache; if (!(im->flags.dirty)) { if (im->references == 1) im_dirty = im; else { im_dirty = evas_cache_image_copied_data(cache, im->w, im->h, evas_cache_image_pixels(im), im->flags.alpha, im->space); if (!im_dirty) goto on_error; if (cache->func.debug) cache->func.debug("dirty-src", im); cache->func.dirty(im_dirty, im); if (cache->func.debug) cache->func.debug("dirty-out", im_dirty); im_dirty->references = 1; evas_cache_image_drop(im); } SLKL(engine_lock); _evas_cache_image_dirty_add(im_dirty); SLKU(engine_lock); } if (cache->func.debug) cache->func.debug("dirty-region", im_dirty); if (cache->func.dirty_region) cache->func.dirty_region(im_dirty, x, y, w, h); return im_dirty; on_error: evas_cache_image_drop(im); return NULL; } EAPI Image_Entry * evas_cache_image_alone(Image_Entry *im) { Evas_Cache_Image *cache; Image_Entry *im_dirty = im; int references; if (!im->cache) return NULL; cache = im->cache; references = im->references; if (references <= 1) { SLKL(engine_lock); if (!im->flags.dirty) _evas_cache_image_dirty_add(im); SLKU(engine_lock); } else { im_dirty = evas_cache_image_copied_data(cache, im->w, im->h, evas_cache_image_pixels(im), im->flags.alpha, im->space); if (!im_dirty) goto on_error; if (cache->func.debug) cache->func.debug("dirty-src", im); cache->func.dirty(im_dirty, im); if (cache->func.debug) cache->func.debug("dirty-out", im_dirty); im_dirty->references = 1; evas_cache_image_drop(im); } return im_dirty; on_error: evas_cache_image_drop(im); return NULL; } EAPI Image_Entry * evas_cache_image_copied_data(Evas_Cache_Image *cache, unsigned int w, unsigned int h, DATA32 *image_data, int alpha, Evas_Colorspace cspace) { int err; Image_Entry *im; if (!cache) return NULL; if ((cspace == EVAS_COLORSPACE_YCBCR422P601_PL) || (cspace == EVAS_COLORSPACE_YCBCR422P709_PL) || (cspace == EVAS_COLORSPACE_YCBCR422601_PL)) w &= ~0x1; SLKL(engine_lock); im = _evas_cache_image_entry_new(cache, NULL, NULL, NULL, NULL, NULL, NULL, &err); SLKU(engine_lock); if (!im) return NULL; im->space = cspace; im->flags.alpha = alpha; _evas_cache_image_entry_surface_alloc(cache, im, w, h); if (cache->func.copied_data(im, w, h, image_data, alpha, cspace) != 0) { SLKL(engine_lock); _evas_cache_image_entry_delete(cache, im); SLKU(engine_lock); 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_cache_image_data(Evas_Cache_Image *cache, unsigned int w, unsigned int h, DATA32 *image_data, int alpha, Evas_Colorspace cspace) { int err; Image_Entry *im; if (!cache) return NULL; if ((cspace == EVAS_COLORSPACE_YCBCR422P601_PL) || (cspace == EVAS_COLORSPACE_YCBCR422P709_PL) || (cspace == EVAS_COLORSPACE_YCBCR422601_PL)) w &= ~0x1; SLKL(engine_lock); im = _evas_cache_image_entry_new(cache, NULL, NULL, NULL, NULL, NULL, NULL, &err); SLKU(engine_lock); if (!im) return NULL; im->w = w; im->h = h; im->flags.alpha = alpha; if (cache->func.data(im, w, h, image_data, alpha, cspace) != 0) { SLKL(engine_lock); _evas_cache_image_entry_delete(cache, im); SLKU(engine_lock); return NULL; } im->references = 1; im->flags.loaded = EINA_TRUE; if (cache->func.debug) cache->func.debug("data", im); return im; } EAPI void evas_cache_image_surface_alloc(Image_Entry *im, unsigned int w, unsigned int h) { Evas_Cache_Image *cache = im->cache; if (!im->cache) return; if ((im->space == EVAS_COLORSPACE_YCBCR422P601_PL) || (im->space == EVAS_COLORSPACE_YCBCR422P709_PL) || (im->space == EVAS_COLORSPACE_YCBCR422601_PL)) w &= ~0x1; _evas_cache_image_entry_surface_alloc(cache, im, w, h); if (cache->func.debug) cache->func.debug("surface-alloc", im); } EAPI Image_Entry * evas_cache_image_size_set(Image_Entry *im, unsigned int w, unsigned int h) { Evas_Cache_Image *cache; Image_Entry *im2 = NULL; int error; if (!im->cache) return im; evas_cache_image_ref(im); 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)) { evas_cache_image_drop(im); return im; } cache = im->cache; SLKL(engine_lock); im2 = _evas_cache_image_entry_new(cache, NULL, NULL, NULL, NULL, NULL, NULL, &error); SLKU(engine_lock); if (!im2) goto on_error; im2->flags.alpha = im->flags.alpha; im2->space = im->space; im2->load_opts = im->load_opts; _evas_cache_image_entry_surface_alloc(cache, im2, w, h); error = cache->func.size_set(im2, im, w, h); if (error != 0) goto on_error; im2->references = 1; im2->flags.loaded = EINA_TRUE; evas_cache_image_drop(im); if (cache->func.debug) cache->func.debug("size_set", im2); evas_cache_image_drop(im); return im2; on_error: SLKL(engine_lock); if (im2) _evas_cache_image_entry_delete(cache, im2); SLKU(engine_lock); evas_cache_image_drop(im); evas_cache_image_drop(im); return NULL; } EAPI int evas_cache_image_load_data(Image_Entry *im) { Eina_Bool preload = EINA_FALSE; int error = EVAS_LOAD_ERROR_NONE; if (!im->cache) return error; evas_cache_image_ref(im); if ((im->flags.loaded) && (!im->animated.animated)) { evas_cache_image_drop(im); return error; } evas_common_rgba_pending_unloads_remove(im); if (im->preload) { preload = EINA_TRUE; if (!im->flags.pending) { im->cache->preload = eina_list_remove(im->cache->preload, im); im->cache->pending = eina_list_append(im->cache->pending, im); im->flags.pending = 1; evas_preload_thread_cancel(im->preload); } evas_async_events_process(); evas_preload_pthread_wait(im->preload, 0.01); while (im->flags.preload_pending) { evas_async_events_process(); evas_preload_pthread_wait(im->preload, 0.1); } } if ((im->flags.loaded) && (!im->animated.animated)) { evas_cache_image_drop(im); return error; } SLKL(im->lock); im->flags.in_progress = EINA_TRUE; error = im->cache->func.load(im); im->flags.in_progress = EINA_FALSE; SLKU(im->lock); im->flags.loaded = 1; if (im->cache->func.debug) im->cache->func.debug("load", im); if (error != EVAS_LOAD_ERROR_NONE) { _evas_cache_image_entry_surface_alloc(im->cache, im, im->w, im->h); im->flags.loaded = 0; } if (preload) _evas_cache_image_async_end(im); evas_cache_image_drop(im); return error; } EAPI void evas_cache_image_unload_data(Image_Entry *im) { if (!im->cache) return; evas_cache_image_ref(im); if (im->flags.in_progress) { evas_cache_image_drop(im); return; } evas_cache_image_preload_cancel(im, NULL, EINA_TRUE); if (SLKT(im->lock) == EINA_FALSE) /* can't get image lock - busy async load */ { SLKL(im->lock_cancel); im->flags.unload_cancel = EINA_TRUE; SLKU(im->lock_cancel); evas_cache_image_drop(im); return; } SLKL(im->lock_cancel); if ((!im->flags.loaded) || (!im->file && !im->f) || (!im->info.module) || (im->flags.dirty)) { SLKU(im->lock_cancel); SLKU(im->lock); evas_cache_image_drop(im); return; } SLKU(im->lock_cancel); im->cache->func.destructor(im); SLKU(im->lock); evas_cache_image_drop(im); //FIXME: imagedataunload - inform owners } static Eina_Bool _evas_cache_image_unload_cb(EINA_UNUSED const Eina_Hash *hash, EINA_UNUSED const void *key, void *data, EINA_UNUSED void *fdata) { evas_cache_image_unload_data(data); return EINA_TRUE; } EAPI void evas_cache_image_unload_all(Evas_Cache_Image *cache) { Image_Entry *im; if (!cache) return; // _evas_cache_image_unload_cb -> evas_cache_image_unload_data -> evas_cache_image_ref // deadlock ////// SLKL(engine_lock); EINA_INLIST_FOREACH(cache->lru, im) evas_cache_image_unload_data(im); EINA_INLIST_FOREACH(cache->lru_nodata, im) evas_cache_image_unload_data(im); eina_hash_foreach(cache->activ, _evas_cache_image_unload_cb, NULL); eina_hash_foreach(cache->inactiv, _evas_cache_image_unload_cb, NULL); ////// SLKU(engine_lock); } static int async_frozen = 0; EAPI int evas_cache_async_frozen_get(void) { return async_frozen; } EAPI void evas_cache_async_freeze(void) { async_frozen++; } EAPI void evas_cache_async_thaw(void) { async_frozen--; } EAPI Eina_Bool evas_cache_image_is_loaded(Image_Entry *im) { if (im->flags.loaded) return EINA_TRUE; return EINA_FALSE; } EAPI void evas_cache_image_preload_data(Image_Entry *im, const Eo *target, void (*preloaded_cb)(void *), void *preloaded_data) { RGBA_Image *img = (RGBA_Image *)im; if (!im->cache) return; evas_cache_image_ref(im); if (((int)im->w > 0) && ((int)im->h > 0) && (((im->flags.loaded) && (img->image.data)) || (im->flags.textured && !im->flags.updated_data))) { _evas_cache_image_preloaded_notify(im); evas_object_inform_call_image_preloaded((Evas_Object*)target); evas_cache_image_drop(im); return; } im->flags.loaded = 0; if (!_evas_cache_image_entry_preload_add(im, target, preloaded_cb, preloaded_data)) evas_object_inform_call_image_preloaded((Evas_Object*) target); evas_cache_image_drop(im); } EAPI void evas_cache_image_preload_cancel(Image_Entry *im, const Eo *target, Eina_Bool force) { if (!target) return; evas_cache_image_ref(im); _evas_cache_image_entry_preload_remove(im, target, force); evas_cache_image_drop(im); } #ifdef CACHEDUMP static int total = 0; static void _dump_img(Image_Entry *im, const char *type) { if (!im->cache) return; total += im->cache->func.mem_size_get(im); printf("%s: %4i: %4ib, %4ix%4i alloc[%4ix%4i] [%s] [%s]\n", type, im->references, im->cache->func.mem_size_get(im), im->w, im->h, im->allocated.w, im->allocated.h, im->f ? eina_file_filename_get(im->f) : im->file, im->key); } static Eina_Bool _dump_cache_active(EINA_UNUSED const Eina_Hash *hash, EINA_UNUSED const void *key, void *data, void *fdata EINA_UNUSED) { Image_Entry *im = data; _dump_img(im, "ACTIVE"); return EINA_TRUE; } static void _dump_cache(Evas_Cache_Image *cache) { Image_Entry *im; if (!cache) return; printf("--CACHE DUMP----------------------------------------------------\n"); printf("cache: %ikb / %ikb\n", cache->usage / 1024, cache->limit / 1024); printf("................................................................\n"); total = 0; SLKL(engine_lock); EINA_INLIST_FOREACH(cache->lru_nodata, im) _dump_img(im, "NODATA"); EINA_INLIST_FOREACH(cache->lru, im) _dump_img(im, "DATA "); printf("tot: %i\n" "usg: %i\n", total, cache->usage); eina_hash_foreach(cache->activ, _dump_cache_active, NULL); SLKU(engine_lock); } #endif EAPI int evas_cache_image_flush(Evas_Cache_Image *cache) { if (!cache) return 0; #ifdef CACHEDUMP _dump_cache(cache); #endif if (cache->limit == (unsigned int)-1) return -1; SLKL(engine_lock); while ((cache->lru) && (cache->limit < (unsigned int)cache->usage)) { Image_Entry *im; im = (Image_Entry *)cache->lru->last; if (!im) im = (Image_Entry *)cache->lru; _evas_cache_image_entry_delete(cache, im); } while ((cache->lru_nodata) && (cache->limit < (unsigned int)cache->usage)) { Image_Entry *im; im = (Image_Entry *)cache->lru_nodata->last; if (!im) im = (Image_Entry *)cache->lru_nodata; _evas_cache_image_lru_nodata_del(im); cache->func.surface_delete(im); im->flags.loaded = 0; } SLKU(engine_lock); return cache->usage; } EAPI Image_Entry * evas_cache_image_empty(Evas_Cache_Image *cache) { int err; Image_Entry *im; if (!cache) return NULL; SLKL(engine_lock); im = _evas_cache_image_entry_new(cache, NULL, NULL, NULL, NULL, NULL, NULL, &err); SLKU(engine_lock); if (!im) return NULL; im->references = 1; return im; } EAPI void evas_cache_image_colorspace(Image_Entry *im, Evas_Colorspace cspace) { if (!im->cache) return; evas_cache_image_ref(im); if (im->space == cspace) goto done; im->space = cspace; if (!im->cache) goto done; im->cache->func.color_space(im, cspace); done: evas_cache_image_drop(im); } EAPI void * evas_cache_private_from_image_entry_get(Image_Entry *im) { void *data; if (!im->cache) return NULL; evas_cache_image_ref(im); data = (void *)im->cache->data; evas_cache_image_drop(im); return data; } EAPI void * evas_cache_private_get(Evas_Cache_Image *cache) { if (!cache) return NULL; return cache->data; } EAPI void evas_cache_private_set(Evas_Cache_Image *cache, const void *data) { if (!cache) return; cache->data = (void *)data; } EAPI DATA32 * evas_cache_image_pixels(Image_Entry *im) { if (!im->cache) return NULL; return im->cache->func.surface_pixels(im); }