#ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #ifdef HAVE_EVIL # include #endif #include "evas_common.h" #include "evas_private.h" #ifdef EVAS_CSERVE // FIXME: cache server and threaded preload clash badly atm - disable //#undef BUILD_ASYNC_PRELOAD #endif #ifdef BUILD_ASYNC_PRELOAD typedef struct _Evas_Cache_Preload Evas_Cache_Preload; struct _Evas_Cache_Preload { EINA_INLIST; Image_Entry *ie; }; static LK(engine_lock) = PTHREAD_MUTEX_INITIALIZER; static LK(wakeup) = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t cond_wakeup = PTHREAD_COND_INITIALIZER; static void _evas_cache_image_entry_preload_remove(Image_Entry *ie, const void *target); #endif #define FREESTRC(Var) \ if (Var) \ { \ eina_stringshare_del(Var); \ Var = NULL; \ } //#define CACHEDUMP 1 #ifdef CACHEDUMP static void _dump_img(Image_Entry *im, const char *type) { printf("%s: %4i: %4ikb, %4ix%4i alloc[%4ix%4i] [%s] [%s]\n", type, im->references, (im->allocated.w * im->allocated.h * 4) / 1024, im->w, im->h, im->allocated.w, im->allocated.h, im->file, im->key); } static Eina_Bool _dump_cache_active(__UNUSED__ const Eina_Hash *hash, __UNUSED__ const void *key, void *data, void *fdata __UNUSED__) { Image_Entry *im = data; _dump_img(im, "ACTIVE"); return EINA_TRUE; } static void _dump_cache(Evas_Cache_Image *cache) { Image_Entry *im; printf("--CACHE DUMP----------------------------------------------------\n"); printf("%cache: %ikb / %ikb\n", cache->usage / 1024, cache->limit / 1024); printf("................................................................\n"); EINA_INLIST_FOREACH(cache->lru_nodata, im) _dump_img(im, "NODATA"); EINA_INLIST_FOREACH(cache->lru, im) _dump_img(im, "DATA "); eina_hash_foreach(cache->activ, _dump_cache_active, NULL); } #endif static void _evas_cache_image_entry_delete(Evas_Cache_Image *cache, Image_Entry *ie); static void _evas_cache_image_make_dirty(Evas_Cache_Image *cache, Image_Entry *im) { im->flags.cached = 1; im->flags.dirty = 1; im->flags.activ = 0; im->flags.lru_nodata = 0; #ifdef EVAS_FRAME_QUEUING LKL(cache->lock); #endif cache->dirty = eina_inlist_prepend(cache->dirty, EINA_INLIST_GET(im)); #ifdef EVAS_FRAME_QUEUING LKU(cache->lock); #endif if (im->cache_key) { eina_stringshare_del(im->cache_key); im->cache_key = NULL; } } static void _evas_cache_image_make_activ(Evas_Cache_Image *cache, Image_Entry *im, const char *key) { /* FIXME: Handle case when image is being processed anyway and don't do a double decode. */ im->cache_key = key; if (key) { im->flags.cached = 1; im->flags.activ = 1; im->flags.lru_nodata = 0; im->flags.dirty = 0; #ifdef EVAS_FRAME_QUEUING LKL(cache->lock); #endif eina_hash_direct_add(cache->activ, key, im); #ifdef EVAS_FRAME_QUEUING LKU(cache->lock); #endif } else { _evas_cache_image_make_dirty(cache, im); } } static void _evas_cache_image_make_inactiv(Evas_Cache_Image *cache, Image_Entry *im, const char *key) { if (im->cache_key) { im->flags.activ = 0; im->flags.dirty = 0; im->flags.cached = 1; #ifdef EVAS_FRAME_QUEUING LKL(cache->lock); #endif eina_hash_direct_add(cache->inactiv, key, im); cache->lru = eina_inlist_prepend(cache->lru, EINA_INLIST_GET(im)); cache->usage += cache->func.mem_size_get(im); #ifdef EVAS_FRAME_QUEUING LKU(cache->lock); #endif } else { _evas_cache_image_entry_delete(cache, im); } } static void _evas_cache_image_remove_lru_nodata(Evas_Cache_Image *cache, Image_Entry *im) { if (im->flags.lru_nodata) { im->flags.lru_nodata = 0; #ifdef EVAS_FRAME_QUEUING LKL(cache->lock); #endif cache->lru_nodata = eina_inlist_remove(cache->lru_nodata, EINA_INLIST_GET(im)); cache->usage -= cache->func.mem_size_get(im); #ifdef EVAS_FRAME_QUEUING LKU(cache->lock); #endif } } static void _evas_cache_image_activ_lru_nodata(Evas_Cache_Image *cache, Image_Entry *im) { im->flags.need_data = 0; im->flags.lru_nodata = 1; #ifdef EVAS_FRAME_QUEUING LKL(cache->lock); #endif cache->lru_nodata = eina_inlist_prepend(cache->lru_nodata, EINA_INLIST_GET(im)); cache->usage += cache->func.mem_size_get(im); #ifdef EVAS_FRAME_QUEUING LKU(cache->lock); #endif } static void _evas_cache_image_remove_activ(Evas_Cache_Image *cache, Image_Entry *ie) { if (ie->flags.cached) { if (ie->flags.activ) { #ifdef EVAS_FRAME_QUEUING LKL(cache->lock); #endif eina_hash_del(cache->activ, ie->cache_key, ie); #ifdef EVAS_FRAME_QUEUING LKU(cache->lock); #endif _evas_cache_image_remove_lru_nodata(cache, ie); } else { #ifdef EVAS_FRAME_QUEUING LKL(cache->lock); #endif if (ie->flags.dirty) { cache->dirty = eina_inlist_remove(cache->dirty, EINA_INLIST_GET(ie)); } else { eina_hash_del(cache->inactiv, ie->cache_key, ie); cache->lru = eina_inlist_remove(cache->lru, EINA_INLIST_GET(ie)); cache->usage -= cache->func.mem_size_get(ie); } #ifdef EVAS_FRAME_QUEUING LKU(cache->lock); #endif } ie->flags.cached = 0; ie->flags.dirty = 0; ie->flags.activ = 0; } } static void _evas_cache_image_entry_delete(Evas_Cache_Image *cache, Image_Entry *ie) { if (!ie) return; if (cache->func.debug) cache->func.debug("deleting", ie); #ifdef BUILD_ASYNC_PRELOAD if (ie->flags.delete_me == 1) return ; if (ie->preload) { ie->flags.delete_me = 1; _evas_cache_image_entry_preload_remove(ie, NULL); return ; } #endif _evas_cache_image_remove_activ(cache, ie); cache->func.destructor(ie); if (ie->cache_key) { eina_stringshare_del(ie->cache_key); ie->cache_key = NULL; } FREESTRC(ie->file); FREESTRC(ie->key); ie->cache = NULL; cache->func.surface_delete(ie); #ifdef BUILD_ASYNC_PRELOAD LKD(ie->lock); LKD(ie->lock_cancel); #endif #ifdef EVAS_FRAME_QUEUING LKD(ie->lock_references); #endif cache->func.dealloc(ie); } 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 #ifdef __USE_MISC if (tstamp->mtime_nsec != (unsigned long int)st->st_mtime.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 #ifdef __USE_MISC tstamp->mtime_nsec = (unsigned long int)st->st_mtime.tv_nsec; #else tstamp->mtime_nsec = (unsigned long int)st->st_mtimensec; #endif #endif } static Image_Entry * _evas_cache_image_entry_new(Evas_Cache_Image *cache, const char *hkey, Image_Timestamp *tstamp, const char *file, const char *key, RGBA_Image_Loadopts *lo, int *error) { Image_Entry *ie; const char *cache_key; ie = cache->func.alloc(); if (!ie) { *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; return NULL; } cache_key = hkey ? eina_stringshare_add(hkey) : NULL; ie->flags.loaded = 0; ie->flags.need_data = 1; _evas_cache_image_make_activ(cache, ie, cache_key); ie->space = EVAS_COLORSPACE_ARGB8888; ie->w = -1; ie->h = -1; ie->allocated.w = 0; ie->allocated.h = 0; #ifdef EVAS_FRAME_QUEUING LKI(ie->lock_references); #endif ie->references = 0; ie->cache = cache; ie->file = file ? eina_stringshare_add(file) : NULL; ie->key = key ? eina_stringshare_add(key) : NULL; if (tstamp) ie->tstamp = *tstamp; else memset(&ie->tstamp, 0, sizeof(Image_Timestamp)); ie->load_opts.scale_down_by = 0; ie->load_opts.dpi = 0; ie->load_opts.w = 0; ie->load_opts.h = 0; ie->load_opts.region.x = 0; ie->load_opts.region.y = 0; ie->load_opts.region.w = 0; ie->load_opts.region.h = 0; ie->scale = 1; #ifdef BUILD_ASYNC_PRELOAD LKI(ie->lock); LKI(ie->lock_cancel); ie->targets = NULL; ie->preload = NULL; ie->flags.delete_me = 0; #endif if (lo) ie->load_opts = *lo; if (file) { *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); 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)) { wmin = 0; hmin = 0; } ie->w = wmin; ie->h = hmin; ie->allocated.w = wmin; ie->allocated.h = hmin; } static void _evas_cache_image_entry_surface_alloc(Evas_Cache_Image *cache, Image_Entry *ie, int w, int h) { int wmin; int hmin; wmin = w > 0 ? w : 1; hmin = h > 0 ? h : 1; #ifdef BUILD_ASYNC_PRELOAD LKL(engine_lock); #endif _evas_cache_image_entry_surface_alloc__locked(cache, ie, wmin, hmin); #ifdef BUILD_ASYNC_PRELOAD LKU(engine_lock); #endif } #ifdef BUILD_ASYNC_PRELOAD static void _evas_cache_image_async_heavy(void *data) { Evas_Cache_Image *cache; Image_Entry *current; int error; int pchannel; current = data; LKL(current->lock); pchannel = current->channel; current->channel++; cache = current->cache; if (!current->flags.loaded && ((Evas_Image_Load_Func*) current->info.module)->threadable) { error = cache->func.load(current); if (cache->func.debug) cache->func.debug("load", current); if (error != EVAS_LOAD_ERROR_NONE) { current->flags.loaded = 0; _evas_cache_image_entry_surface_alloc(cache, current, current->w, current->h); } else current->flags.loaded = 1; } current->channel = pchannel; // check the unload cancel flag LKL(current->lock_cancel); if (current->unload_cancel) { current->unload_cancel = EINA_FALSE; cache->func.surface_delete(current); current->flags.loaded = 0; current->flags.preload_done = 0; } LKU(current->lock_cancel); LKU(current->lock); } static void _evas_cache_image_async_end(void *data) { Image_Entry *ie = (Image_Entry *) data; Evas_Cache_Target *tmp; 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; while (ie->targets) { tmp = ie->targets; evas_object_inform_call_image_preloaded((Evas_Object*) tmp->target); ie->targets = (Evas_Cache_Target*) eina_inlist_remove(EINA_INLIST_GET(ie->targets), EINA_INLIST_GET(ie->targets)); free(tmp); } } static void _evas_cache_image_async_cancel(void *data) { Evas_Cache_Image *cache = NULL; Image_Entry *ie = (Image_Entry *) data; ie->preload = NULL; ie->cache->pending = eina_list_remove(ie->cache->pending, ie); if (ie->flags.delete_me || ie->flags.dirty) { ie->flags.delete_me = 0; _evas_cache_image_entry_delete(ie->cache, ie); return ; } if (ie->flags.loaded) { _evas_cache_image_async_end(ie); } #ifdef EVAS_FRAME_QUEUING LKL(ie->lock_references); #endif if (ie->references == 0) { _evas_cache_image_remove_activ(ie->cache, ie); _evas_cache_image_make_inactiv(ie->cache, ie, ie->cache_key); cache = ie->cache; } #ifdef EVAS_FRAME_QUEUING LKU(ie->lock_references); #endif if (cache) evas_cache_image_flush(cache); } static int _evas_cache_image_entry_preload_add(Image_Entry *ie, const void *target) { Evas_Cache_Target *tg; if (ie->flags.preload_done) return 0; tg = malloc(sizeof (Evas_Cache_Target)); if (!tg) return 0; tg->target = target; 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->preload = evas_preload_thread_run(_evas_cache_image_async_heavy, _evas_cache_image_async_end, _evas_cache_image_async_cancel, ie); } return 1; } static void _evas_cache_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) { // FIXME: No callback when we cancel only for one 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); } } if (!ie->targets && 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); } } #endif EAPI int evas_cache_image_usage_get(Evas_Cache_Image *cache) { assert(cache != NULL); return cache->usage; } EAPI int evas_cache_image_get(Evas_Cache_Image *cache) { assert(cache != NULL); return cache->limit; } EAPI void evas_cache_image_set(Evas_Cache_Image *cache, unsigned int limit) { assert(cache != NULL); #ifdef EVAS_FRAME_QUEUING LKL(cache->lock); #endif if (cache->limit == limit) { #ifdef EVAS_FRAME_QUEUING LKU(cache->lock); #endif return; } cache->limit = limit; evas_cache_image_flush(cache); #ifdef EVAS_FRAME_QUEUING LKU(cache->lock); #endif } EAPI Evas_Cache_Image * evas_cache_image_init(const Evas_Cache_Image_Func *cb) { Evas_Cache_Image *new; new = malloc(sizeof (Evas_Cache_Image)); if (!new) return NULL; new->func = *cb; new->limit = 0; new->usage = 0; new->dirty = NULL; new->lru = NULL; new->lru_nodata = NULL; new->inactiv = eina_hash_string_superfast_new(NULL); new->activ = eina_hash_string_superfast_new(NULL); new->references = 1; new->preload = NULL; new->pending = NULL; #ifdef EVAS_FRAME_QUEUING LKI(new->lock); #endif return new; } static Eina_Bool _evas_cache_image_free_cb(__UNUSED__ const Eina_Hash *hash, __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; assert(cache != NULL); #ifdef EVAS_FRAME_QUEUING LKL(cache->lock); #endif cache->references--; if (cache->references > 0) { #ifdef EVAS_FRAME_QUEUING LKU(cache->lock); #endif return ; } #ifdef BUILD_ASYNC_PRELOAD EINA_LIST_FREE(cache->preload, im) { /* By doing that we are protecting us from destroying image when the cache is no longuer available. */ im->flags.delete_me = 1; _evas_cache_image_entry_preload_remove(im, NULL); } evas_async_events_process(); #endif while (cache->lru) { im = (Image_Entry *) cache->lru; _evas_cache_image_entry_delete(cache, im); } while (cache->lru_nodata) { im = (Image_Entry *) cache->lru_nodata; _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); } #ifdef BUILD_ASYNC_PRELOAD /* Now wait for all pending image to die */ while (cache->pending) { evas_async_events_process(); LKL(wakeup); if (cache->pending) pthread_cond_wait(&cond_wakeup, &wakeup); LKU(wakeup); } #endif eina_hash_free(cache->activ); eina_hash_free(cache->inactiv); #ifdef EVAS_FRAME_QUEUING LKU(cache->lock); LKD(cache->lock); #endif free(cache); } EAPI Image_Entry * evas_cache_image_request(Evas_Cache_Image *cache, const char *file, const char *key, RGBA_Image_Loadopts *lo, int *error) { const char *ckey = "(null)"; char *hkey; Image_Entry *im; Evas_Image_Load_Opts prevent = { 0, 0, 0, 0, { 0, 0, 0, 0 } }; size_t size; int stat_done = 0; size_t file_length; size_t key_length; struct stat st; Image_Timestamp tstamp; assert(cache != NULL); if ((!file) || ((!file) && (!key))) { *error = EVAS_LOAD_ERROR_GENERIC; return NULL; } file_length = strlen(file); key_length = key ? strlen(key) : 6; size = file_length + key_length + 128; hkey = alloca(sizeof (char) * size); memcpy(hkey, file, file_length); size = file_length; memcpy(hkey + size, "//://", 5); size += 5; if (key) ckey = key; memcpy(hkey + size, ckey, key_length); size += key_length; 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 = &prevent; // if (key) // format = "%s//://%s"; // else // format = "%s//://%p"; } else { 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] = '\0'; #ifdef EVAS_FRAME_QUEUING LKL(cache->lock); #endif im = eina_hash_find(cache->activ, hkey); #ifdef EVAS_FRAME_QUEUING LKU(cache->lock); #endif if (im) { int ok = 1; stat_done = 1; if (stat(file, &st) < 0) goto on_stat_error; { if (!_timestamp_compare(&(im->tstamp), &st)) ok = 0; } if (ok) goto on_ok; _evas_cache_image_remove_activ(cache, im); _evas_cache_image_make_dirty(cache, im); } #ifdef EVAS_FRAME_QUEUING LKL(cache->lock); #endif im = eina_hash_find(cache->inactiv, hkey); #ifdef EVAS_FRAME_QUEUING LKU(cache->lock); #endif if (im) { int ok = 1; if (!stat_done) { stat_done = 1; if (stat(file, &st) < 0) goto on_stat_error; if (!_timestamp_compare(&(im->tstamp), &st)) ok = 0; } else if (!_timestamp_compare(&(im->tstamp), &st)) ok = 0; if (ok) { _evas_cache_image_remove_activ(cache, im); _evas_cache_image_make_activ(cache, im, im->cache_key); goto on_ok; } _evas_cache_image_entry_delete(cache, im); } if (!stat_done) { if (stat(file, &st) < 0) goto on_stat_error; } _timestamp_build(&tstamp, &st); im = _evas_cache_image_entry_new(cache, hkey, &tstamp, file, key, lo, error); if (!im) return NULL; if (cache->func.debug) cache->func.debug("request", im); on_ok: *error = EVAS_LOAD_ERROR_NONE; #ifdef EVAS_FRAME_QUEUING LKL(im->lock_references); #endif im->references++; if (im->references > 1 && im->flags.lru_nodata) _evas_cache_image_remove_lru_nodata(cache, im); #ifdef EVAS_FRAME_QUEUING LKU(im->lock_references); #endif 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 void evas_cache_image_drop(Image_Entry *im) { Evas_Cache_Image *cache; int references; assert(im); assert(im->cache); #ifdef EVAS_FRAME_QUEUING LKL(im->lock_references); #endif im->references--; if (im->references < 0) im->references = 0; references = im->references; #ifdef EVAS_FRAME_QUEUING LKU(im->lock_references); #endif cache = im->cache; if (references == 0) { #ifdef EVAS_FRAME_QUEUING LKL(im->ref_fq_add); LKL(im->ref_fq_del); if (im->ref_fq[0] != im->ref_fq[1]) { LKU(im->ref_fq_add); LKU(im->ref_fq_del); return; } LKU(im->ref_fq_add); LKU(im->ref_fq_del); #endif #ifdef BUILD_ASYNC_PRELOAD if (im->preload) { _evas_cache_image_entry_preload_remove(im, NULL); return ; } #endif if (im->flags.dirty) { _evas_cache_image_entry_delete(cache, im); return; } _evas_cache_image_remove_activ(cache, im); _evas_cache_image_make_inactiv(cache, im, im->cache_key); evas_cache_image_flush(cache); } } EAPI void evas_cache_image_data_not_needed(Image_Entry *im) { Evas_Cache_Image *cache; int references; assert(im); assert(im->cache); cache = im->cache; #ifdef EVAS_FRAME_QUEUING LKL(im->lock_references); #endif references = im->references; #ifdef EVAS_FRAME_QUEUING LKU(im->lock_references); #endif if (references > 1) return ; if (im->flags.dirty || !im->flags.need_data) return ; _evas_cache_image_activ_lru_nodata(cache, im); } 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; int references; assert(im); assert(im->cache); cache = im->cache; if (!(im->flags.dirty)) { #ifdef EVAS_FRAME_QUEUING LKL(im->lock_references); #endif references = im->references; #ifdef EVAS_FRAME_QUEUING LKU(im->lock_references); #endif #ifndef EVAS_CSERVE // if ref 1 also copy if using shared cache as its read-only if (references == 1) im_dirty = im; else #endif { int error; 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); error = cache->func.dirty(im_dirty, im); if (cache->func.debug) cache->func.debug("dirty-out", im_dirty); #ifdef EVAS_FRAME_QUEUING LKL(im_dirty->lock_references); #endif im_dirty->references = 1; #ifdef EVAS_FRAME_QUEUING LKU(im_dirty->lock_references); #endif evas_cache_image_drop(im); } _evas_cache_image_remove_activ(cache, im_dirty); _evas_cache_image_make_dirty(cache, im_dirty); } 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: if (im_dirty) _evas_cache_image_entry_delete(cache, im_dirty); 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; assert(im); assert(im->cache); cache = im->cache; #ifdef EVAS_FRAME_QUEUING LKL(im->lock_references); #endif references = im->references; #ifdef EVAS_FRAME_QUEUING LKU(im->lock_references); #endif if (references <= 1) { if (!(im->flags.dirty)) { _evas_cache_image_remove_activ(cache, im); _evas_cache_image_make_dirty(cache, im); } } else { int error; 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); error = cache->func.dirty(im_dirty, im); if (cache->func.debug) cache->func.debug("dirty-out", im_dirty); #ifdef EVAS_FRAME_QUEUING LKL(im_dirty->lock_references); #endif im_dirty->references = 1; #ifdef EVAS_FRAME_QUEUING LKU(im_dirty->lock_references); #endif evas_cache_image_drop(im); } return im_dirty; on_error: if (im_dirty) _evas_cache_image_entry_delete(cache, im_dirty); 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, int cspace) { Image_Entry *im; assert(cache); if ((cspace == EVAS_COLORSPACE_YCBCR422P601_PL) || (cspace == EVAS_COLORSPACE_YCBCR422P709_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_cache_image_entry_surface_alloc(cache, 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; } #ifdef EVAS_FRAME_QUEUING LKL(im->lock_references); #endif im->references = 1; #ifdef EVAS_FRAME_QUEUING LKU(im->lock_references); #endif 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, int cspace) { Image_Entry *im; assert(cache); if ((cspace == EVAS_COLORSPACE_YCBCR422P601_PL) || (cspace == EVAS_COLORSPACE_YCBCR422P709_PL)) w &= ~0x1; im = _evas_cache_image_entry_new(cache, NULL, NULL, NULL, NULL, NULL, NULL); im->w = w; im->h = h; im->flags.alpha = alpha; if (cache->func.data(im, w, h, image_data, alpha, cspace) != 0) { _evas_cache_image_entry_delete(cache, im); return NULL; } #ifdef EVAS_FRAME_QUEUING LKL(im->lock_references); #endif im->references = 1; #ifdef EVAS_FRAME_QUEUING LKU(im->lock_references); #endif 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; assert(im); assert(im->cache); cache = im->cache; if ((im->space == EVAS_COLORSPACE_YCBCR422P601_PL) || (im->space == EVAS_COLORSPACE_YCBCR422P709_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 *new; int error; assert(im); assert(im->cache); #ifdef EVAS_FRAME_QUEUING LKL(im->lock_references); #endif assert(im->references > 0); #ifdef EVAS_FRAME_QUEUING LKU(im->lock_references); #endif if ((im->space == EVAS_COLORSPACE_YCBCR422P601_PL) || (im->space == EVAS_COLORSPACE_YCBCR422P709_PL)) w &= ~0x1; if ((im->w == w) && (im->h == h)) return im; cache = im->cache; new = _evas_cache_image_entry_new(cache, NULL, NULL, NULL, NULL, NULL, &error); if (!new) goto on_error; new->flags.alpha = im->flags.alpha; new->space = im->space; new->load_opts = im->load_opts; _evas_cache_image_entry_surface_alloc(cache, new, w, h); error = cache->func.size_set(new, im, w, h); if (error != 0) goto on_error; #ifdef EVAS_FRAME_QUEUING LKL(new->lock_references); #endif new->references = 1; #ifdef EVAS_FRAME_QUEUING LKU(new->lock_references); #endif evas_cache_image_drop(im); if (cache->func.debug) cache->func.debug("size_set", new); return new; on_error: if (new) _evas_cache_image_entry_delete(cache, new); evas_cache_image_drop(im); return NULL; } EAPI void evas_cache_image_load_data(Image_Entry *im) { #ifdef BUILD_ASYNC_PRELOAD Eina_Bool preload = EINA_FALSE; #endif Evas_Cache_Image *cache; int error; assert(im); assert(im->cache); cache = im->cache; if (im->flags.loaded) return; #ifdef BUILD_ASYNC_PRELOAD 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(); LKL(wakeup); while (im->preload) { pthread_cond_wait(&cond_wakeup, &wakeup); LKU(wakeup); evas_async_events_process(); LKL(wakeup); } LKU(wakeup); } if (im->flags.loaded) return ; LKL(im->lock); #endif im->flags.in_progress = EINA_TRUE; error = cache->func.load(im); im->flags.in_progress = EINA_FALSE; #ifdef BUILD_ASYNC_PRELOAD LKU(im->lock); #endif im->flags.loaded = 1; if (cache->func.debug) cache->func.debug("load", im); if (error != EVAS_LOAD_ERROR_NONE) { _evas_cache_image_entry_surface_alloc(cache, im, im->w, im->h); im->flags.loaded = 0; } #ifdef BUILD_ASYNC_PRELOAD if (preload) _evas_cache_image_async_end(im); #endif } EAPI void evas_cache_image_unload_data(Image_Entry *im) { Evas_Cache_Image *cache; assert(im); assert(im->cache); cache = im->cache; if (im->flags.in_progress) return; evas_cache_image_preload_cancel(im, NULL); #ifdef BUILD_ASYNC_PRELOAD LKL(im->lock_cancel); if (LKT(im->lock) != 0) /* can't get image lock - busy async load */ { im->unload_cancel = EINA_TRUE; LKU(im->lock_cancel); return; } LKU(im->lock_cancel); // LKL(im->lock); #endif if ((!im->flags.loaded) || (!im->file) || (!im->info.module) || (im->flags.dirty)) { #ifdef BUILD_ASYNC_PRELOAD LKU(im->lock); #endif return; } cache->func.destructor(im); #ifdef BUILD_ASYNC_PRELOAD LKU(im->lock); #endif //FIXME: imagedataunload - inform owners } static Eina_Bool _evas_cache_image_unload_cb(__UNUSED__ const Eina_Hash *hash, __UNUSED__ const void *key, void *data, __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; 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); } EAPI Eina_Bool evas_cache_image_is_loaded(Image_Entry *im) { assert(im); if (im->flags.loaded) return EINA_TRUE; return EINA_FALSE; } EAPI void evas_cache_image_preload_data(Image_Entry *im, const void *target) { #ifdef BUILD_ASYNC_PRELOAD RGBA_Image *img = (RGBA_Image *)im; assert(im); assert(im->cache); if ((im->flags.loaded) && (img->image.data)) { evas_object_inform_call_image_preloaded((Evas_Object *)target); return; } im->flags.loaded = 0; if (!_evas_cache_image_entry_preload_add(im, target)) { evas_object_inform_call_image_preloaded((Evas_Object *)target); } #else evas_cache_image_load_data(im); evas_object_inform_call_image_preloaded((Evas_Object *)target); #endif } EAPI void evas_cache_image_preload_cancel(Image_Entry *im, const void *target) { #ifdef BUILD_ASYNC_PRELOAD assert(im); assert(im->cache); if (!target) return; _evas_cache_image_entry_preload_remove(im, target); #else (void)im; #endif } EAPI int evas_cache_image_flush(Evas_Cache_Image *cache) { assert(cache); assert(cache->usage >= 0); #ifdef CACHEDUMP _dump_cache(cache); #endif if (cache->limit == (unsigned int)-1) return -1; while ((cache->lru) && (cache->limit < (unsigned int)cache->usage)) { Image_Entry *im; im = (Image_Entry *)cache->lru->last; _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; _evas_cache_image_remove_lru_nodata(cache, im); cache->func.surface_delete(im); im->flags.loaded = 0; } return cache->usage; } EAPI Image_Entry * evas_cache_image_empty(Evas_Cache_Image *cache) { Image_Entry *im; im = _evas_cache_image_entry_new(cache, NULL, NULL, NULL, NULL, NULL, NULL); if (!im) return NULL; #ifdef EVAS_FRAME_QUEUING LKL(im->lock_references); #endif im->references = 1; #ifdef EVAS_FRAME_QUEUING LKU(im->lock_references); #endif return im; } EAPI void evas_cache_image_colorspace(Image_Entry *im, int cspace) { Evas_Cache_Image *cache; assert(im); assert(im->cache); cache = im->cache; if (im->space == cspace) return; im->space = cspace; cache->func.color_space(im, cspace); } EAPI void * evas_cache_private_from_image_entry_get(Image_Entry *im) { Evas_Cache_Image *cache; assert(im); assert(im->cache); cache = im->cache; return (void*) cache->data; } EAPI void * evas_cache_private_get(Evas_Cache_Image *cache) { assert(cache); return cache->data; } EAPI void evas_cache_private_set(Evas_Cache_Image *cache, const void *data) { assert(cache); cache->data = (void *)data; } EAPI DATA32 * evas_cache_image_pixels(Image_Entry *im) { Evas_Cache_Image *cache; assert(im); assert(im->cache); cache = im->cache; return cache->func.surface_pixels(im); } EAPI void evas_cache_image_wakeup(void) { #ifdef BUILD_ASYNC_PRELOAD pthread_cond_broadcast(&cond_wakeup); #endif }