move around - flatter.
[profile/ivi/evas.git] / src / lib / cache / evas_cache_engine_image.c
1 /*
2  * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
3  */
4
5 #include <assert.h>
6
7 #include "evas_common.h"
8 #include "evas_private.h"
9
10 static void
11 _evas_cache_engine_image_make_dirty(Evas_Cache_Engine_Image *cache,
12                                     Engine_Image_Entry *eim)
13 {
14    eim->flags.cached = 1;
15    eim->flags.dirty = 1;
16    eim->flags.loaded = 1;
17    eim->flags.activ = 0;
18    cache->dirty = evas_object_list_prepend(cache->dirty, eim);
19 }
20
21 static void
22 _evas_cache_engine_image_make_active(Evas_Cache_Engine_Image *cache,
23                                      Engine_Image_Entry *eim,
24                                      const char *key)
25 {
26    eim->flags.cached = 1;
27    eim->flags.activ = 1;
28    eim->flags.dirty = 0;
29    cache->activ = evas_hash_add(cache->activ, key, eim);
30 }
31
32 static void
33 _evas_cache_engine_image_make_inactive(Evas_Cache_Engine_Image *cache,
34                                        Engine_Image_Entry *eim,
35                                        const char *key)
36 {
37    eim->flags.cached = 1;
38    eim->flags.dirty = 0;
39    eim->flags.activ = 0;
40    cache->inactiv = evas_hash_add(cache->inactiv, key, eim);
41    cache->lru = evas_object_list_prepend(cache->lru, eim);
42    cache->usage += cache->func.mem_size_get(eim);
43 }
44
45 static void
46 _evas_cache_engine_image_remove_activ(Evas_Cache_Engine_Image *cache,
47                                       Engine_Image_Entry *eim)
48 {
49    if (eim->flags.cached)
50      {
51         if (eim->flags.dirty)
52           {
53              cache->dirty = evas_object_list_remove(cache->dirty, eim);
54           }
55         else
56           if (eim->flags.activ)
57             {
58                cache->activ = evas_hash_del(cache->activ, eim->cache_key, eim);
59             }
60           else
61             {
62                cache->usage -= cache->func.mem_size_get(eim);
63                cache->inactiv = evas_hash_del(cache->inactiv, eim->cache_key, eim);
64                cache->lru = evas_object_list_remove(cache->lru, eim);
65             }
66         eim->flags.cached = 0;
67         eim->flags.dirty = 0;
68         eim->flags.activ = 0;
69      }
70 }
71
72 static Engine_Image_Entry *
73 _evas_cache_engine_image_alloc(Evas_Cache_Engine_Image *cache,
74                                Image_Entry *ie,
75                                const char *hkey)
76 {
77    Engine_Image_Entry   *eim;
78
79    assert(cache);
80
81    if (cache->func.alloc)
82      eim = cache->func.alloc();
83    else
84      eim = malloc(sizeof (Engine_Image_Entry));
85
86    if (!eim) goto on_error;
87    memset(eim, 0, sizeof (Engine_Image_Entry));
88
89    eim->cache = cache;
90    if (ie)
91      {
92         eim->w = ie->w;
93         eim->h = ie->h;
94         eim->src = ie;
95         eim->flags.need_parent = 1;
96      }
97    else
98      {
99         eim->w = -1;
100         eim->h = -1;
101         eim->flags.need_parent = 0;
102         eim->src = NULL;
103      }
104
105    eim->flags.cached = 0;
106    eim->references = 0;
107    eim->cache_key = hkey;
108
109    if (hkey)
110      _evas_cache_engine_image_make_active(cache, eim, hkey);
111    else
112      _evas_cache_engine_image_make_dirty(cache, eim);
113
114    return eim;
115
116  on_error:
117    if (eim)
118      evas_cache_engine_image_drop(eim);
119    evas_stringshare_del(hkey);
120    evas_cache_image_drop(ie);
121    return NULL;
122 }
123
124 static void
125 _evas_cache_engine_image_dealloc(Evas_Cache_Engine_Image *cache, Engine_Image_Entry *eim)
126 {
127    Image_Entry   *im;
128
129    if (cache->func.debug) cache->func.debug("delete", eim);
130
131    _evas_cache_engine_image_remove_activ(cache, eim);
132
133    im = eim->src;
134    cache->func.destructor(eim);
135    if (im) evas_cache_image_drop(im);
136
137    if (cache->func.dealloc)
138      {
139         cache->func.dealloc(eim);
140      }
141    else
142      {
143         memset(eim, 0, sizeof (Engine_Image_Entry));
144         free(eim);
145      }
146 }
147
148 EAPI int
149 evas_cache_engine_image_usage_get(Evas_Cache_Engine_Image *cache)
150 {
151    assert(cache != NULL);
152
153    return cache->usage;
154 }
155
156 EAPI int
157 evas_cache_engine_image_get(Evas_Cache_Engine_Image *cache)
158 {
159    assert(cache != NULL);
160
161    return cache->limit;
162 }
163
164 EAPI void
165 evas_cache_engine_image_set(Evas_Cache_Engine_Image *cache, int limit)
166 {
167    assert(cache != NULL);
168
169    cache->limit = limit;
170 }
171
172 EAPI Evas_Cache_Engine_Image *
173 evas_cache_engine_image_init(const Evas_Cache_Engine_Image_Func *cb, Evas_Cache_Image *parent)
174 {
175    Evas_Cache_Engine_Image     *new;
176
177    new = malloc(sizeof (Evas_Cache_Engine_Image));
178    if (!new)
179      return NULL;
180
181    new->func = *cb;
182
183    new->limit = 0;
184    new->usage = 0;
185
186    new->dirty = NULL;
187    new->lru = NULL;
188    new->activ = NULL;
189    new->inactiv = NULL;
190
191    new->parent = parent;
192    parent->references++;
193
194    new->brother = NULL;
195
196    return new;
197 }
198
199 EAPI Evas_Cache_Engine_Image *
200 evas_cache_engine_image_dup(const Evas_Cache_Engine_Image_Func *cb, Evas_Cache_Engine_Image *brother)
201 {
202    Evas_Cache_Engine_Image     *new;
203
204    new = malloc(sizeof (Evas_Cache_Engine_Image));
205    if (!new)
206      return NULL;
207
208    new->func = brother->func;
209
210 #define ORD(Func) if (cb->Func) new->func.Func = cb->Func;
211
212    ORD(key);
213    ORD(constructor);
214    ORD(destructor);
215    ORD(dirty_region);
216    ORD(dirty);
217    ORD(size_set);
218    ORD(update_data);
219    ORD(load);
220    ORD(mem_size_get);
221    ORD(debug);
222
223 #undef ORD
224
225    new->limit = -1;
226    new->usage = 0;
227    new->references = 1;
228
229    new->dirty = NULL;
230    new->activ = NULL;
231
232    new->parent = brother->parent;
233    new->parent->references++;
234
235    new->brother = brother;
236    brother->references++;
237
238    return new;
239 }
240
241 static Evas_Bool
242 _evas_cache_engine_image_free_cb(const Evas_Hash *hash, const char *key, void *data, void *fdata)
243 {
244    Evas_Cache_Engine_Image     *cache = fdata;
245    Engine_Image_Entry           *eim = data;
246
247    _evas_cache_engine_image_dealloc(cache, eim);
248
249    return 1;
250 }
251
252 EAPI void
253 evas_cache_engine_image_flush(Evas_Cache_Engine_Image *cache)
254 {
255    assert(cache != NULL);
256
257    while ((cache->lru) && (cache->limit < cache->usage))
258      {
259         Engine_Image_Entry      *eim;
260
261         eim = (Engine_Image_Entry *) cache->lru->last;
262         _evas_cache_engine_image_dealloc(cache, eim);
263      }
264 }
265
266 EAPI void
267 evas_cache_engine_image_shutdown(Evas_Cache_Engine_Image *cache)
268 {
269    Engine_Image_Entry   *eim;
270
271    assert(cache != NULL);
272
273    if (cache->func.debug) cache->func.debug("shutdown-engine", NULL);
274
275    evas_hash_foreach(cache->inactiv, _evas_cache_engine_image_free_cb, cache);
276    evas_hash_free(cache->inactiv);
277
278    /* This is mad, I am about to destroy image still alive, but we need to prevent leak. */
279    while (cache->dirty)
280      {
281         eim = (Engine_Image_Entry *) cache->dirty;
282         _evas_cache_engine_image_dealloc(cache, eim);
283      }
284
285    evas_hash_foreach(cache->activ, _evas_cache_engine_image_free_cb, cache);
286    evas_hash_free(cache->activ);
287
288    evas_cache_image_shutdown(cache->parent);
289    if (cache->brother)
290      evas_cache_engine_image_shutdown(cache->brother);
291    free(cache);
292 }
293
294 EAPI Engine_Image_Entry *
295 evas_cache_engine_image_request(Evas_Cache_Engine_Image *cache,
296                                 const char *file, const char *key,
297                                 RGBA_Image_Loadopts *lo, void *data, int *error)
298 {
299    Engine_Image_Entry   *eim;
300    Image_Entry          *im;
301    const char          *ekey;
302
303    assert(cache != NULL);
304
305    *error = -1;
306
307    ekey = NULL;
308    eim = NULL;
309
310    im = evas_cache_image_request(cache->parent, file, key, lo, error);
311    if (!im)
312      goto on_error;
313
314    if (cache->func.key)
315      ekey = cache->func.key(im, file, key, lo, data);
316    else
317      ekey = evas_stringshare_add(im->cache_key);
318    if (!ekey)
319      goto on_error;
320
321    eim = evas_hash_find(cache->activ, ekey);
322    if (eim)
323      {
324         evas_cache_image_drop(im);
325         goto on_ok;
326      }
327
328    eim = evas_hash_find(cache->inactiv, ekey);
329    if (eim)
330      {
331         _evas_cache_engine_image_remove_activ(cache, eim);
332         _evas_cache_engine_image_make_active(cache, eim, ekey);
333         evas_cache_image_drop(im);
334         goto on_ok;
335      }
336
337    eim = _evas_cache_engine_image_alloc(cache, im, ekey);
338    if (!eim) return NULL;
339
340    *error = cache->func.constructor(eim, data);
341    if (*error != 0) goto on_error;
342    if (cache->func.debug)
343      cache->func.debug("constructor-engine", eim);
344
345   on_ok:
346    eim->references++;
347    return eim;
348
349   on_error:
350    if (!eim)
351      {
352         if (im) evas_cache_image_drop(im);
353         if (ekey) evas_stringshare_del(ekey);
354      }
355    else
356      {
357         _evas_cache_engine_image_dealloc(cache, eim);
358      }
359
360    return NULL;
361 }
362
363 EAPI void
364 evas_cache_engine_image_drop(Engine_Image_Entry *eim)
365 {
366    Evas_Cache_Engine_Image     *cache;
367
368    assert(eim);
369    assert(eim->cache);
370
371    eim->references--;
372    cache = eim->cache;
373
374    if (eim->flags.dirty)
375      {
376         _evas_cache_engine_image_dealloc(cache, eim);
377         return ;
378      }
379
380    if (eim->references == 0)
381      {
382         _evas_cache_engine_image_remove_activ(cache, eim);
383         _evas_cache_engine_image_make_inactive(cache, eim, eim->cache_key);
384         evas_cache_engine_image_flush(cache);
385         return ;
386      }
387 }
388
389 EAPI Engine_Image_Entry *
390 evas_cache_engine_image_dirty(Engine_Image_Entry *eim, int x, int y, int w, int h)
391 {
392    Engine_Image_Entry           *eim_dirty = eim;
393    Image_Entry                  *im_dirty = NULL;
394    Image_Entry                  *im;
395    Evas_Cache_Engine_Image      *cache;
396    unsigned char                 alloc_eim;
397
398    assert(eim);
399    assert(eim->cache);
400
401    cache = eim->cache;
402    if (!(eim->flags.dirty))
403      {
404         alloc_eim = 0;
405
406         if (eim->flags.need_parent == 1)
407           {
408              im = eim->src;
409              im_dirty = evas_cache_image_dirty(im, x, y, w, h);
410
411              /* If im == im_dirty, this meens that we have only one reference to the eim. */
412              if (im != im_dirty)
413                {
414                   if (eim->references == 1)
415                     {
416                        _evas_cache_engine_image_remove_activ(cache, eim);
417                        _evas_cache_engine_image_make_dirty(cache, eim);
418
419                        eim->src = im_dirty;
420                     }
421                   else
422                     alloc_eim = 1;
423                }
424           }
425         else
426           if (eim->references > 1)
427             {
428                alloc_eim = 1;
429             }
430           else
431             {
432                _evas_cache_engine_image_remove_activ(cache, eim_dirty);
433                _evas_cache_engine_image_make_dirty(cache, eim_dirty);
434             }
435
436         if (alloc_eim == 1)
437           {
438              int           error;
439
440              eim_dirty = _evas_cache_engine_image_alloc(cache, im_dirty, NULL);
441              if (!eim_dirty) goto on_error;
442
443              eim_dirty->w = eim->w;
444              eim_dirty->h = eim->h;
445              eim_dirty->references = 1;
446
447              error = cache->func.dirty(eim_dirty, eim);
448              if (cache->func.debug)
449                cache->func.debug("dirty-engine", eim_dirty);
450
451              if (error != 0) goto on_error;
452
453              evas_cache_engine_image_drop(eim);
454           }
455      }
456
457    if (cache->func.dirty_region)
458      cache->func.dirty_region(eim_dirty, x, y, w, h);
459    if (cache->func.debug)
460      cache->func.debug("dirty-region-engine", eim_dirty);
461
462    return eim_dirty;
463
464   on_error:
465    if (eim) evas_cache_engine_image_drop(eim);
466    if (eim_dirty && eim_dirty != eim)
467      evas_cache_engine_image_drop(eim_dirty);
468    else
469      if (im_dirty) evas_cache_image_drop(im_dirty);
470
471    return NULL;
472 }
473
474 EAPI Engine_Image_Entry *
475 evas_cache_engine_image_alone(Engine_Image_Entry *eim, void *data)
476 {
477    Evas_Cache_Engine_Image      *cache;
478    Image_Entry                  *im;
479
480
481    assert(eim);
482    assert(eim->cache);
483
484    cache = eim->cache;
485    im = evas_cache_image_alone(eim->src);
486    if (im != eim->src)
487      {
488         eim = _evas_cache_engine_image_alloc(cache, im, NULL);
489         if (!eim) goto on_error;
490
491         eim->references = 1;
492
493         if (cache->func.constructor(eim, data)) goto on_error;
494      }
495    /* FIXME */
496    return eim;
497
498  on_error:
499    evas_cache_image_drop(im);
500    return NULL;
501 }
502
503 static Engine_Image_Entry *
504 _evas_cache_engine_image_push_dirty(Evas_Cache_Engine_Image *cache, Image_Entry *im, void *engine_data)
505 {
506    Engine_Image_Entry    *eim;
507    int                  error;
508
509    eim = _evas_cache_engine_image_alloc(cache, im, NULL);
510    if (!eim) goto on_error;
511    eim->references = 1;
512
513    error = cache->func.update_data(eim, engine_data);
514    if (cache->func.debug)
515      cache->func.debug("dirty-update_data-engine", eim);
516    if (error != 0) goto on_error;
517
518    return eim;
519
520   on_error:
521    if (eim)
522      evas_cache_engine_image_drop(eim);
523    return NULL;
524 }
525
526 EAPI Engine_Image_Entry *
527 evas_cache_engine_image_copied_data(Evas_Cache_Engine_Image *cache, int w, int h, DATA32 *image_data, int alpha, int cspace, void *engine_data)
528 {
529    Image_Entry           *im;
530
531    assert(cache);
532
533    im = evas_cache_image_copied_data(cache->parent, w, h, image_data, alpha, cspace);
534
535    return _evas_cache_engine_image_push_dirty(cache, im, engine_data);
536 }
537
538 EAPI Engine_Image_Entry *
539 evas_cache_engine_image_data(Evas_Cache_Engine_Image *cache, int w, int h, DATA32 *image_data, int alpha, int cspace, void *engine_data)
540 {
541    Image_Entry           *im;
542
543    assert(cache);
544
545    im = evas_cache_image_data(cache->parent, w, h, image_data, alpha, cspace);
546
547    return _evas_cache_engine_image_push_dirty(cache, im, engine_data);
548 }
549
550 EAPI Engine_Image_Entry *
551 evas_cache_engine_image_size_set(Engine_Image_Entry *eim, int w, int h)
552 {
553    Evas_Cache_Engine_Image      *cache;
554    Engine_Image_Entry           *new;
555    Image_Entry                  *im;
556    const char                   *hkey;
557    int                           error;
558
559    assert(eim);
560    assert(eim->cache);
561    assert(eim->references > 0);
562
563    im = NULL;
564    cache = eim->cache;
565
566    if (eim->flags.need_parent == 1)
567      {
568         assert(eim->src);
569
570         if (eim->src->w == w
571             && eim->src->h == h)
572           return eim;
573
574         im = evas_cache_image_size_set(eim->src, w, h);
575         /* FIXME: Good idea to call update_data ? */
576         if (im == eim->src) return eim;
577         eim->src = NULL;
578      }
579
580    hkey = (eim->references > 1 ) ? evas_stringshare_add(eim->cache_key) : NULL;
581
582    new = _evas_cache_engine_image_alloc(cache, im, hkey);
583    if (!new) goto on_error;
584
585    new->w = w;
586    new->h = h;
587    new->references = 1;
588
589    error = cache->func.size_set(new, eim);
590    if (error) goto on_error;
591
592    evas_cache_engine_image_drop(eim);
593    return new;
594
595   on_error:
596    if (new)
597      evas_cache_engine_image_drop(new);
598    else
599      if (im)
600        evas_cache_image_drop(im);
601    evas_cache_engine_image_drop(eim);
602
603    return NULL;
604 }
605
606 EAPI void
607 evas_cache_engine_image_load_data(Engine_Image_Entry *eim)
608 {
609    Evas_Cache_Engine_Image     *cache;
610    int                          size = 0;
611
612    assert(eim);
613    assert(eim->src);
614    assert(eim->cache);
615
616    if (eim->flags.loaded) return ;
617
618    if (eim->src)
619      evas_cache_image_load_data(eim->src);
620
621    cache = eim->cache;
622    if (cache->func.debug)
623      cache->func.debug("load-engine", eim);
624
625    if (eim->flags.dirty)
626      size = cache->func.mem_size_get(eim);
627    cache = eim->cache;
628    cache->func.load(eim, eim->src);
629    if (eim->flags.dirty)
630      cache->usage += cache->func.mem_size_get(eim) - size;
631
632    eim->flags.loaded = 1;
633 }
634
635 EAPI Engine_Image_Entry *
636 evas_cache_engine_image_engine(Evas_Cache_Engine_Image *cache, void *engine_data)
637 {
638    Engine_Image_Entry   *eim;
639    Image_Entry          *ie;
640    int                   error;
641
642    ie = evas_cache_image_empty(cache->parent);
643    if (!ie) return NULL;
644
645    eim = _evas_cache_engine_image_alloc(cache, ie, NULL);
646    if (!eim) goto on_error;
647    eim->references = 1;
648
649    error = cache->func.update_data(eim, engine_data);
650    if (cache->func.debug)
651      cache->func.debug("update_data-engine", eim);
652
653    if (error != 0) goto on_error;
654
655    return eim;
656
657   on_error:
658    if (!eim)
659      {
660         if (ie)
661           evas_cache_image_drop(ie);
662      }
663    else
664      {
665         evas_cache_engine_image_drop(eim);
666      }
667    return NULL;
668 }
669
670 EAPI void
671 evas_cache_engine_image_colorspace(Engine_Image_Entry *eim, int cspace, void *engine_data)
672 {
673    Evas_Cache_Engine_Image     *cache = eim->cache;
674
675    assert(cache);
676
677    cache->func.destructor(eim);
678    evas_cache_image_colorspace(eim->src, cspace);
679    cache->func.constructor(eim, engine_data);
680    if (cache->func.debug)
681      cache->func.debug("cosntructor-colorspace-engine", eim);
682 }
683
684 EAPI void
685 evas_cache_engine_parent_not_needed(Engine_Image_Entry *eim)
686 {
687    assert(eim);
688    assert(eim->cache);
689
690    eim->flags.need_parent = 0;
691    evas_cache_image_data_not_needed(eim->src);
692 }