evas: enable reusing filter buffer.
[platform/upstream/efl.git] / src / lib / evas / canvas / evas_filter_mixin.c
1 #define EFL_CANVAS_FILTER_INTERNAL_PROTECTED
2
3 #include <Evas.h>
4
5 #include "evas_filter.h"
6
7 #define MY_CLASS EFL_CANVAS_FILTER_INTERNAL_MIXIN
8
9 #define FCOW_BEGIN(_pd) ({ Evas_Object_Filter_Data *_fcow = eina_cow_write(evas_object_filter_cow, (const Eina_Cow_Data**)&(_pd->data)); _state_check(_fcow); _fcow; })
10 #define FCOW_END(_fcow, _pd) eina_cow_done(evas_object_filter_cow, (const Eina_Cow_Data**)&(_pd->data), _fcow, EINA_TRUE)
11 #define FCOW_WRITE(pd, name, value) do { \
12    if (pd->data->name != (value)) { \
13      fcow = FCOW_BEGIN(pd); \
14      fcow->name = (value); \
15      FCOW_END(fcow, pd); \
16    }} while (0)
17
18 typedef struct _Evas_Filter_Data Evas_Filter_Data;
19 typedef struct _Evas_Filter_Post_Render_Data Evas_Filter_Post_Render_Data;
20
21 struct _Evas_Object_Filter_Data
22 {
23    Evas_Object_Protected_Data *obj;
24    Eina_Stringshare    *name;
25    Eina_Stringshare    *code;
26    Evas_Filter_Program *chain;
27    Evas_Filter_Context *context;
28    Eina_Hash           *sources; // Evas_Filter_Proxy_Binding
29    Eina_Inlist         *data; // Evas_Filter_Data_Binding
30    Eina_Rectangle       prev_obscured, obscured;
31    Evas_Filter_Padding  prev_padding, padding;
32    void                *output;
33    struct {
34       struct {
35          Eina_Stringshare *name;
36          double            value;
37       } cur;
38       struct {
39          Eina_Stringshare *name;
40          double            value;
41       } next;
42       double               pos;
43    } state;
44    int                  obscured_changes;
45    Eina_Bool            changed : 1;
46    Eina_Bool            invalid : 1; // Code parse failed
47    Eina_Bool            async : 1;
48    Eina_Bool            reuse : 1;
49 };
50
51 struct _Evas_Filter_Data
52 {
53    const Evas_Object_Filter_Data *data;
54 };
55
56 struct _Evas_Filter_Post_Render_Data
57 {
58    Evas_Filter_Data *pd;
59    Evas_Filter_Context *ctx;
60    Eina_Bool success;
61 };
62
63 // FIXME: This should be enabled (with proper heuristics)
64 #define FILTER_CONTEXT_REUSE EINA_TRUE
65
66 static const Evas_Object_Filter_Data evas_filter_data_cow_default = {
67    .reuse = FILTER_CONTEXT_REUSE
68 };
69 Eina_Cow *evas_object_filter_cow = NULL;
70
71 void
72 evas_filter_mixin_init(void)
73 {
74    evas_object_filter_cow = eina_cow_add
75          ("Evas Filter Data", sizeof(Evas_Object_Filter_Data), 8,
76           &evas_filter_data_cow_default, EINA_TRUE);
77 }
78
79 void
80 evas_filter_mixin_shutdown(void)
81 {
82    eina_cow_del(evas_object_filter_cow);
83    evas_object_filter_cow = NULL;
84 }
85
86 static inline void
87 _state_check(Evas_Object_Filter_Data *fcow)
88 {
89    if (!fcow->state.cur.name)
90      fcow->state.cur.name = eina_stringshare_add("default");
91    if (!fcow->state.next.name)
92      fcow->state.next.name = eina_stringshare_add("default");
93 }
94
95 static void
96 _filter_end_sync(Evas_Filter_Context *ctx, Evas_Object_Protected_Data *obj,
97                  Evas_Filter_Data *pd, Eina_Bool success)
98 {
99    void *previous = pd->data->output;
100    Eina_Bool destroy = !pd->data->reuse;
101    Evas_Object_Filter_Data *fcow;
102    Eo *eo_obj = obj->object;
103    void *output = NULL;
104
105    if (!success)
106      {
107         ERR("Filter failed at runtime!");
108         evas_filter_invalid_set(eo_obj, EINA_TRUE);
109         evas_filter_dirty(eo_obj);
110         destroy = EINA_TRUE;
111      }
112    else
113      {
114         output = evas_filter_buffer_backing_get(ctx, EVAS_FILTER_BUFFER_OUTPUT_ID, EINA_FALSE);
115         FCOW_WRITE(pd, output, output);
116      }
117
118    if (previous)
119      ENFN->image_free(ENC, previous);
120
121    if (destroy && (ctx == pd->data->context))
122      {
123         evas_filter_context_unref(ctx); // local ref
124         FCOW_WRITE(pd, context, NULL);
125      }
126
127    evas_filter_context_unref(ctx); // run ref
128    efl_unref(eo_obj);
129 }
130
131 static void
132 _filter_async_post_render_cb(void *data)
133 {
134    Evas_Filter_Post_Render_Data *task = data;
135    Evas_Filter_Data *pd = task->pd;
136
137 #ifdef FILTERS_DEBUG
138    EINA_SAFETY_ON_FALSE_RETURN(eina_main_loop_is());
139 #endif
140
141    _filter_end_sync(task->ctx, pd->data->obj, pd, task->success);
142    free(task);
143 }
144
145 static void
146 _filter_cb(Evas_Filter_Context *ctx, void *data, Eina_Bool success)
147 {
148    Evas_Filter_Post_Render_Data *post_data;
149    Evas_Object_Protected_Data *obj;
150    Evas_Filter_Data *pd = data;
151
152    obj = pd->data->obj;
153    EVAS_OBJECT_DATA_VALID_CHECK(obj);
154
155    if (!pd->data->async)
156      {
157         _filter_end_sync(ctx, pd->data->obj, pd, success);
158         return;
159      }
160
161 #ifdef FILTERS_DEBUG
162    EINA_SAFETY_ON_FALSE_RETURN(!eina_main_loop_is());
163 #endif
164
165    post_data = calloc(1, sizeof(*post_data));
166    post_data->success = success;
167    post_data->ctx = ctx;
168    post_data->pd = pd;
169    evas_post_render_job_add(obj->layer->evas, _filter_async_post_render_cb, post_data);
170 }
171
172 void
173 _evas_filter_source_hash_free_cb(void *data)
174 {
175    Evas_Filter_Proxy_Binding *pb = data;
176    Evas_Object_Protected_Data *proxy, *source;
177    Evas_Filter_Data *pd;
178
179    proxy = efl_data_scope_get(pb->eo_proxy, EFL_CANVAS_OBJECT_CLASS);
180    source = efl_data_scope_get(pb->eo_source, EFL_CANVAS_OBJECT_CLASS);
181
182    if (source)
183      {
184         EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, source->proxy,
185                              Evas_Object_Proxy_Data, source_write)
186           source_write->proxies = eina_list_remove(source_write->proxies, pb->eo_proxy);
187         EINA_COW_WRITE_END(evas_object_proxy_cow, source->proxy, source_write)
188      }
189
190    pd = efl_data_scope_get(pb->eo_proxy, MY_CLASS);
191
192    if (pd && proxy)
193      {
194         if (!eina_hash_population(pd->data->sources))
195           {
196              EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, proxy->proxy,
197                                   Evas_Object_Proxy_Data, proxy_write)
198                proxy_write->is_proxy = EINA_FALSE;
199              EINA_COW_WRITE_END(evas_object_proxy_cow, source->proxy, proxy_write)
200           }
201      }
202
203    eina_stringshare_del(pb->name);
204    free(pb);
205 }
206
207 static inline Eina_Bool
208 _evas_filter_state_set_internal(Evas_Filter_Program *pgm, Evas_Filter_Data *pd)
209 {
210    Efl_Canvas_Filter_State state = EFL_CANVAS_FILTER_STATE_DEFAULT;
211
212    evas_filter_state_prepare(pd->data->obj->object, &state, NULL);
213    state.cur.name = pd->data->state.cur.name;
214    state.cur.value = pd->data->state.cur.value;
215    state.next.name = pd->data->state.next.name;
216    state.next.value = pd->data->state.next.value;
217    state.pos = pd->data->state.pos;
218
219    return evas_filter_program_state_set(pgm, &state);
220 }
221
222 static inline Eina_Bool
223 _evas_filter_obscured_region_changed(Evas_Filter_Data *pd)
224 {
225    Eina_Rectangle inter;
226
227    inter = pd->data->prev_obscured;
228    if (eina_rectangle_is_empty(&pd->data->obscured) &&
229        eina_rectangle_is_empty(&inter))
230      return EINA_FALSE;
231    if (!eina_rectangle_intersection(&inter, &pd->data->obscured))
232      return EINA_TRUE;
233    if ((inter.w != pd->data->prev_obscured.w) ||
234        (inter.h != pd->data->prev_obscured.h))
235      return EINA_TRUE;
236
237    return EINA_FALSE;
238 }
239
240 Eina_Bool
241 evas_filter_object_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
242                           void *engine, void *output, void *context, void *surface,
243                           int x, int y, Eina_Bool do_async, Eina_Bool alpha)
244 {
245    Evas_Filter_Data *pd = efl_data_scope_get(eo_obj, MY_CLASS);
246    int X, Y, W, H;
247    Evas_Filter_Context *filter;
248    void *drawctx;
249    Eina_Bool ok;
250    void *previous = pd->data->output;
251    Evas_Object_Filter_Data *fcow;
252    Eina_Bool use_map = EINA_FALSE;
253    Evas_Filter_Padding pad;
254
255    if (pd->data->invalid || (!pd->data->chain && !pd->data->code))
256      return EINA_FALSE;
257
258    // TIZEN_ONLY(20200619): make filter faster
259    // TODO: apply for all type of object and filter
260    int instr_type;
261    if (ENFN->context_flush && /* quick hack to check gl engine */
262        obj->is_image_object && evas_filter_program_count(pd->data->chain) == 1)
263      {
264         instr_type = evas_filter_program_nth_instruction_type_get(pd->data->chain, 0);
265         if (instr_type == EVAS_FILTER_MODE_GRAYSCALE ||
266             instr_type == EVAS_FILTER_MODE_INVERSE_COLOR)
267           {
268              ((RGBA_Draw_Context *)context)->filter_mode = instr_type;
269              return EINA_FALSE;
270           }
271      }
272    //
273
274    W = obj->cur->geometry.w;
275    H = obj->cur->geometry.h;
276    X = obj->cur->geometry.x;
277    Y = obj->cur->geometry.y;
278
279    // Prepare color multiplier
280    ENFN->context_color_set(engine, context,
281                            obj->cur->cache.clip.r,
282                            obj->cur->cache.clip.g,
283                            obj->cur->cache.clip.b,
284                            obj->cur->cache.clip.a);
285    if (obj->cur->clipper)
286      ENFN->context_multiplier_set(engine, context,
287                                   obj->cur->clipper->cur->cache.clip.r,
288                                   obj->cur->clipper->cur->cache.clip.g,
289                                   obj->cur->clipper->cur->cache.clip.b,
290                                   obj->cur->clipper->cur->cache.clip.a);
291    else
292       ENFN->context_multiplier_unset(engine, context);
293
294    if (obj->map->cur.usemap && obj->map->cur.map && (obj->map->cur.map->count >= 4))
295      {
296         int iw, ih;
297
298         use_map = EINA_TRUE;
299         ENFN->image_size_get(engine, previous, &iw, &ih);
300         evas_object_map_update(eo_obj, x, y, iw, ih, iw, ih);
301      }
302
303    if (!pd->data->chain)
304      {
305         Evas_Filter_Program *pgm;
306         Eina_Bool invalid;
307
308         pgm = evas_filter_program_new(pd->data->name, alpha);
309         evas_filter_program_source_set_all(pgm, pd->data->sources);
310         evas_filter_program_data_set_all(pgm, pd->data->data);
311         _evas_filter_state_set_internal(pgm, pd);
312         invalid = !evas_filter_program_parse(pgm, pd->data->code);
313         if (invalid)
314           {
315              ERR("Filter program parsing failed");
316              evas_filter_program_del(pgm);
317              pgm = NULL;
318           }
319         fcow = FCOW_BEGIN(pd);
320         if (!invalid) evas_filter_program_padding_get(pgm, NULL, &fcow->padding);
321         fcow->chain = pgm;
322         fcow->invalid = invalid;
323         FCOW_END(fcow, pd);
324         if (invalid) return EINA_FALSE;
325      }
326    else if (previous && !pd->data->changed)
327      {
328         Eina_Bool redraw = EINA_TRUE;
329
330         if (_evas_filter_state_set_internal(pd->data->chain, pd))
331           DBG("Filter redraw by state change!");
332         else if (obj->changed)
333           DBG("Filter redraw by object content change!");
334         else if (obj->snapshot_needs_redraw)
335           DBG("Filter redraw by snapshot change!");
336         else if (_evas_filter_obscured_region_changed(pd))
337           DBG("Filter redraw by obscure regions change!");
338         else redraw = EINA_FALSE;
339
340         // Scan proxies to find if any changed
341         if (!redraw && pd->data->sources)
342           {
343              Evas_Filter_Proxy_Binding *pb;
344              Evas_Object_Protected_Data *source;
345              Eina_Iterator *iter;
346
347              iter = eina_hash_iterator_data_new(pd->data->sources);
348              EINA_ITERATOR_FOREACH(iter, pb)
349                {
350                   source = efl_data_scope_get(pb->eo_source, EFL_CANVAS_OBJECT_CLASS);
351                   if (source->changed)
352                     {
353                        redraw = EINA_TRUE;
354                        break;
355                     }
356                }
357              eina_iterator_free(iter);
358           }
359
360         if (!redraw)
361           {
362              // Render this image only
363              if (use_map)
364                {
365                   ENFN->image_map_draw(engine, output, context, surface, previous,
366                                        obj->map->spans, EINA_TRUE, 0, do_async);
367                }
368              else
369                {
370                   ENFN->image_draw(engine, output, context,
371                                    surface, previous,
372                                    0, 0, W, H,         // src
373                                    X + x, Y + y, W, H, // dst
374                                    EINA_FALSE,         // smooth
375                                    do_async);
376                }
377              return EINA_TRUE;
378           }
379      }
380    else
381      {
382         _evas_filter_state_set_internal(pd->data->chain, pd);
383      }
384
385    filter = pd->data->context;
386    if (filter)
387      {
388         int prev_w, prev_h;
389         Eina_Bool was_async;
390
391         was_async = evas_filter_context_async_get(filter);
392         evas_filter_context_size_get(filter, &prev_w, &prev_h);
393         if ((!pd->data->reuse) || (was_async != do_async) ||
394             (prev_w != W) || (prev_h != H))
395           {
396              evas_filter_context_unref(filter);
397              FCOW_WRITE(pd, context, NULL);
398              filter = NULL;
399           }
400      }
401
402    if (filter)
403      {
404         ok = evas_filter_context_program_use(engine, output, filter, pd->data->chain, EINA_TRUE, X, Y);
405         if (!ok)
406           {
407              evas_filter_context_unref(filter);
408              FCOW_WRITE(pd, context, NULL);
409              filter = NULL;
410           }
411      }
412
413    if (!filter)
414      {
415         filter = evas_filter_context_new(obj->layer->evas, do_async, 0);
416
417         // Run script
418         ok = evas_filter_context_program_use(engine, output, filter, pd->data->chain, EINA_FALSE, X, Y);
419         if (!filter || !ok)
420           {
421              ERR("Parsing failed?");
422              evas_filter_context_unref(filter);
423              FCOW_WRITE(pd, invalid, EINA_TRUE);
424              FCOW_WRITE(pd, context, NULL);
425              return EINA_FALSE;
426           }
427      }
428
429    // Proxies
430    evas_filter_context_proxy_render_all(filter, eo_obj, output, EINA_FALSE);
431
432    // Draw Context
433    drawctx = ENFN->context_new(engine);
434    ENFN->context_color_set(engine, drawctx,
435                            obj->cur->cache.clip.r,
436                            obj->cur->cache.clip.g,
437                            obj->cur->cache.clip.b,
438                            obj->cur->cache.clip.a);
439
440    // Set obscured region
441    evas_filter_context_obscured_region_set(filter, pd->data->obscured);
442
443    // Allocate all buffers now
444    evas_filter_context_buffers_allocate_all(filter);
445    evas_filter_target_set(filter, context, surface, X + x, Y + y,
446                           use_map ? obj->map->spans : NULL);
447
448    // Request rendering from the object itself (child class)
449    evas_filter_program_padding_get(pd->data->chain, &pad, NULL);
450    ok = evas_filter_input_render(eo_obj, filter, engine, output, drawctx, NULL,
451                                  pad.l, pad.r, pad.t, pad.b, 0, 0, do_async);
452    if (!ok) ERR("Filter input render failed.");
453
454    ENFN->context_free(engine, drawctx);
455
456    // Add post-run callback and run filter
457    evas_filter_context_post_run_callback_set(filter, _filter_cb, pd);
458
459    fcow = FCOW_BEGIN(pd);
460    fcow->context = filter;
461    fcow->changed = EINA_FALSE;
462    fcow->async = do_async;
463    fcow->prev_obscured = fcow->obscured;
464    fcow->prev_padding = fcow->padding;
465    fcow->padding = pad;
466    fcow->invalid = EINA_FALSE;
467    FCOW_END(fcow, pd);
468
469    // Run the filter now (maybe async)
470    efl_ref(eo_obj);
471    ok = evas_filter_context_run(engine, output, filter);
472    if (!ok) ERR("Filter program failed to run!");
473
474    return ok;
475 }
476
477 EOLIAN static void
478 _efl_canvas_filter_internal_efl_gfx_filter_filter_program_set(Eo *eo_obj, Evas_Filter_Data *pd,
479                                                               const char *code, const char *name)
480 {
481    Evas_Object_Protected_Data *obj;
482    Evas_Filter_Program *pgm = NULL;
483    Evas_Object_Filter_Data *fcow;
484    Eina_Bool invalid = pd->data->invalid;
485    Eina_Bool alpha;
486
487    obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
488    if (eina_streq(pd->data->code, code) && eina_streq(pd->data->name, name))
489      return;
490
491    evas_object_async_block(obj);
492    fcow = FCOW_BEGIN(pd);
493    {
494       fcow->obj = obj;
495
496       evas_filter_context_unref(fcow->context);
497       fcow->context = NULL;
498
499       // Parse filter program
500       evas_filter_program_del(fcow->chain);
501       eina_stringshare_replace(&fcow->name, name);
502       if (code)
503         {
504            alpha = evas_filter_input_alpha(eo_obj);
505            pgm = evas_filter_program_new(fcow->name, alpha);
506            evas_filter_program_source_set_all(pgm, fcow->sources);
507            evas_filter_program_data_set_all(pgm, fcow->data);
508            _evas_filter_state_set_internal(pgm, pd);
509            invalid = !evas_filter_program_parse(pgm, code);
510            if (invalid)
511              {
512                 ERR("Parsing failed!");
513                 evas_filter_program_del(pgm);
514                 pgm = NULL;
515              }
516            else
517              {
518                 evas_filter_program_padding_get(pgm, NULL, &fcow->padding);
519              }
520         }
521       fcow->chain = pgm;
522       fcow->changed = EINA_TRUE;
523       fcow->invalid = invalid;
524       eina_stringshare_replace(&fcow->code, code);
525    }
526    FCOW_END(fcow, pd);
527
528    evas_filter_dirty(eo_obj);
529 }
530
531 EOLIAN static void
532 _efl_canvas_filter_internal_efl_gfx_filter_filter_program_get(const Eo *eo_obj EINA_UNUSED, Evas_Filter_Data *pd, const char **code, const char **name)
533 {
534    if (code) *code = pd->data->code;
535    if (name) *name = pd->data->name;
536 }
537
538 EOLIAN static void
539 _efl_canvas_filter_internal_efl_gfx_filter_filter_source_set(Eo *eo_obj, Evas_Filter_Data *pd,
540                                                              const char *name, Efl_Gfx_Entity *eo_source)
541 {
542    Evas_Object_Protected_Data *obj;
543    Evas_Filter_Proxy_Binding *pb, *pb_old = NULL;
544    Evas_Object_Protected_Data *source = NULL;
545    Evas_Object_Filter_Data *fcow = NULL;
546    Eina_Bool invalid = pd->data->invalid;
547
548    obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
549    if (eo_source)
550      source = efl_data_scope_get(eo_source, EFL_CANVAS_OBJECT_CLASS);
551
552    evas_object_async_block(obj);
553    if (!name)
554      {
555         if (!eo_source || !pd->data->sources) return;
556         if (eina_hash_del_by_data(pd->data->sources, eo_source))
557           goto update;
558         return;
559      }
560
561    if (!source && !pd->data->sources)
562      return;
563
564    if (pd->data->sources)
565      {
566         pb_old = eina_hash_find(pd->data->sources, name);
567         if (pb_old && (pb_old->eo_source == eo_source)) return;
568      }
569
570    fcow = FCOW_BEGIN(pd);
571    if (!fcow->sources)
572      fcow->sources = eina_hash_string_small_new(_evas_filter_source_hash_free_cb);
573    else if (pb_old)
574      eina_hash_del(fcow->sources, name, pb_old);
575
576    if (!source)
577      {
578         pb_old = eina_hash_find(fcow->sources, name);
579         if (!pb_old)
580           {
581              FCOW_END(fcow, pd);
582              return;
583           }
584         eina_hash_del_by_key(fcow->sources, name);
585         goto update;
586      }
587
588    pb = calloc(1, sizeof(*pb));
589    pb->eo_proxy = eo_obj;
590    pb->eo_source = eo_source;
591    pb->name = eina_stringshare_add(name);
592
593    if (!eina_list_data_find(source->proxy->proxies, eo_obj))
594      {
595         EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, source->proxy, Evas_Object_Proxy_Data, source_write)
596           source_write->proxies = eina_list_append(source_write->proxies, eo_obj);
597         EINA_COW_WRITE_END(evas_object_proxy_cow, source->proxy, source_write)
598      }
599
600    if (!obj->proxy->is_proxy)
601      {
602         EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, obj->proxy, Evas_Object_Proxy_Data, proxy_write)
603           proxy_write->is_proxy = EINA_TRUE;
604         EINA_COW_WRITE_END(evas_object_proxy_cow, obj->proxy, proxy_write)
605      }
606
607    eina_hash_add(fcow->sources, pb->name, pb);
608    evas_filter_program_source_set_all(fcow->chain, fcow->sources);
609    evas_filter_program_data_set_all(fcow->chain, fcow->data);
610    invalid = !evas_filter_program_parse(fcow->chain, fcow->code);
611    if (!invalid) evas_filter_program_padding_get(fcow->chain, NULL, &fcow->padding);
612
613    // Update object
614 update:
615    if (fcow)
616      {
617         fcow->changed = EINA_TRUE;
618         fcow->invalid = invalid;
619         FCOW_END(fcow, pd);
620      }
621
622    evas_filter_dirty(eo_obj);
623 }
624
625 EOLIAN static Efl_Gfx_Entity *
626 _efl_canvas_filter_internal_efl_gfx_filter_filter_source_get(const Eo *obj EINA_UNUSED, Evas_Filter_Data *pd,
627                                                              const char * name)
628 {
629    Evas_Filter_Proxy_Binding *pb = eina_hash_find(pd->data->sources, name);
630    if (!pb) return NULL;
631    return pb->eo_source;
632 }
633
634 EOLIAN static void
635 _efl_canvas_filter_internal_efl_gfx_filter_filter_state_set(Eo *eo_obj, Evas_Filter_Data *pd,
636                                                             const char *cur_state, double cur_val,
637                                                             const char *next_state, double next_val,
638                                                             double pos)
639 {
640    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
641
642    evas_object_async_block(obj);
643    if ((cur_state != pd->data->state.cur.name) ||
644        (!EINA_DBL_EQ(cur_val, pd->data->state.cur.value)) ||
645        (next_state != pd->data->state.next.name) ||
646        (!EINA_DBL_EQ(next_val, pd->data->state.next.value)) ||
647        (!EINA_DBL_EQ(pos, pd->data->state.pos)))
648      {
649         Evas_Object_Filter_Data *fcow = FCOW_BEGIN(pd);
650         fcow->changed = 1;
651         eina_stringshare_replace(&fcow->state.cur.name, cur_state);
652         fcow->state.cur.value = cur_val;
653         eina_stringshare_replace(&fcow->state.next.name, next_state);
654         fcow->state.next.value = next_val;
655         fcow->state.pos = pos;
656         FCOW_END(fcow, pd);
657
658         if (pd->data->chain)
659           {
660              _evas_filter_state_set_internal(pd->data->chain, pd);
661           }
662
663         evas_filter_dirty(eo_obj);
664      }
665 }
666
667 EOLIAN static void
668 _efl_canvas_filter_internal_efl_gfx_filter_filter_state_get(const Eo *obj EINA_UNUSED, Evas_Filter_Data *pd,
669                                                             const char **cur_state, double *cur_val,
670                                                             const char **next_state, double *next_val,
671                                                             double *pos)
672 {
673    if (cur_state) *cur_state = pd->data->state.cur.name;
674    if (cur_val) *cur_val = pd->data->state.cur.value;
675    if (next_state) *next_state = pd->data->state.next.name;
676    if (next_val) *next_val = pd->data->state.next.value;
677    if (pos) *pos = pd->data->state.pos;
678 }
679
680 EOLIAN static void
681 _efl_canvas_filter_internal_efl_gfx_filter_filter_padding_get(const Eo *eo_obj EINA_UNUSED, Evas_Filter_Data *pd,
682                                                               int *l, int *r, int *t, int *b)
683 {
684    Evas_Filter_Padding pad = { 0, 0, 0, 0 };
685
686    if (pd->data->chain)
687      evas_filter_program_padding_get(pd->data->chain, &pad, NULL);
688
689    if (l) *l = pad.l;
690    if (r) *r = pad.r;
691    if (t) *t = pad.t;
692    if (b) *b = pad.b;
693 }
694
695 EOLIAN static void
696 _efl_canvas_filter_internal_filter_changed_set(Eo *eo_obj EINA_UNUSED, Evas_Filter_Data *pd, Eina_Bool val)
697 {
698    if ((&evas_filter_data_cow_default != pd->data) && (pd->data->changed != val))
699      {
700         Evas_Object_Filter_Data *fcow = FCOW_BEGIN(pd);
701         fcow->changed = val;
702         FCOW_END(fcow, pd);
703      }
704 }
705
706 EOLIAN static void
707 _efl_canvas_filter_internal_filter_invalid_set(Eo *eo_obj EINA_UNUSED, Evas_Filter_Data *pd, Eina_Bool val)
708 {
709    if (pd->data->invalid != val)
710      {
711         Evas_Object_Filter_Data *fcow = FCOW_BEGIN(pd);
712         fcow->invalid = val;
713         FCOW_END(fcow, pd);
714      }
715 }
716
717 EOLIAN static Efl_Object *
718 _efl_canvas_filter_internal_efl_object_constructor(Eo *eo_obj, Evas_Filter_Data *pd)
719 {
720    Eo *obj = NULL;
721
722    obj = efl_constructor(efl_super(eo_obj, MY_CLASS));
723    pd->data = eina_cow_alloc(evas_object_filter_cow);
724
725    return obj;
726 }
727
728 EOLIAN static void
729 _efl_canvas_filter_internal_efl_object_destructor(Eo *eo_obj, Evas_Filter_Data *pd)
730 {
731    Evas_Object_Protected_Data *obj;
732    Evas_Object_Filter_Data *fcow;
733    Evas_Filter_Data_Binding *db;
734    Evas_Public_Data *e;
735    Eina_Inlist *il;
736
737    if (!pd->data || (&evas_filter_data_cow_default == pd->data))
738      goto finish;
739
740    obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
741    e = obj->layer->evas;
742
743    if (pd->data->context)
744      {
745         evas_filter_context_unref(pd->data->context);
746         FCOW_WRITE(pd, context, NULL);
747      }
748
749    if (pd->data->output)
750      {
751         if (!pd->data->async)
752           ENFN->image_free(ENC, pd->data->output);
753         else
754           evas_unref_queue_image_put(e, pd->data->output);
755      }
756    eina_hash_free(pd->data->sources);
757    EINA_INLIST_FOREACH_SAFE(pd->data->data, il, db)
758      {
759         eina_stringshare_del(db->name);
760         eina_stringshare_del(db->value);
761         free(db);
762      }
763    evas_filter_program_del(pd->data->chain);
764    eina_stringshare_del(pd->data->code);
765    eina_stringshare_del(pd->data->state.cur.name);
766    eina_stringshare_del(pd->data->state.next.name);
767
768 finish:
769    eina_cow_free(evas_object_filter_cow, (const Eina_Cow_Data **) &pd->data);
770
771    efl_destructor(efl_super(eo_obj, MY_CLASS));
772 }
773
774 EOLIAN static void
775 _efl_canvas_filter_internal_efl_gfx_filter_filter_data_set(Eo *eo_obj, Evas_Filter_Data *pd,
776                                                            const char *name, const char *value,
777                                                            Eina_Bool execute)
778 {
779    Evas_Filter_Data_Binding *db, *found = NULL;
780    Evas_Object_Filter_Data *fcow;
781    Eina_Bool invalid = pd->data->invalid;
782
783    EINA_SAFETY_ON_NULL_RETURN(pd->data);
784    EINA_SAFETY_ON_NULL_RETURN(name);
785
786    EINA_INLIST_FOREACH(pd->data->data, db)
787      {
788         if (!strcmp(name, db->name))
789           {
790              if (db->execute == execute)
791                {
792                   if ((value == db->value) || (value && db->value && !strcmp(value, db->value)))
793                     return;
794                }
795              found = db;
796              break;
797           }
798      }
799
800    fcow = FCOW_BEGIN(pd);
801    {
802       if (found)
803         {
804            // Note: we are keeping references to NULL values here.
805            eina_stringshare_replace(&found->value, value);
806            found->execute = execute;
807         }
808       else if (value)
809         {
810            db = calloc(1, sizeof(Evas_Filter_Data_Binding));
811            db->name = eina_stringshare_add(name);
812            db->value = eina_stringshare_add(value);
813            db->execute = execute;
814            fcow->data = eina_inlist_append(fcow->data, EINA_INLIST_GET(db));
815         }
816       if (fcow->chain)
817         {
818            evas_filter_program_data_set_all(fcow->chain, fcow->data);
819            invalid = !evas_filter_program_parse(fcow->chain, fcow->code);
820            if (!invalid) evas_filter_program_padding_get(fcow->chain, NULL, &fcow->padding);
821         }
822       fcow->invalid = invalid;
823       fcow->changed = 1;
824    }
825    FCOW_END(fcow, pd);
826
827    evas_filter_dirty(eo_obj);
828 }
829
830 EOLIAN static void
831 _efl_canvas_filter_internal_efl_gfx_filter_filter_data_get(const Eo *obj EINA_UNUSED, Evas_Filter_Data *pd,
832                                                            const char *name, const char **value,
833                                                            Eina_Bool *execute)
834 {
835    Evas_Filter_Data_Binding *db;
836
837    if (!value && !execute) return;
838    EINA_SAFETY_ON_NULL_RETURN(pd->data);
839
840    EINA_INLIST_FOREACH(pd->data->data, db)
841      {
842         if (!strcmp(name, db->name))
843           {
844              if (value) *value = db->value;
845              if (execute) *execute = db->execute;
846              return;
847           }
848      }
849
850    if (value) *value = NULL;
851    if (execute) *execute = EINA_FALSE;
852 }
853
854 EOLIAN static void *
855 _efl_canvas_filter_internal_filter_output_buffer_get(const Eo *obj EINA_UNUSED, Evas_Filter_Data *pd)
856 {
857    return pd->data->output;
858 }
859
860 Eina_Bool
861 _evas_filter_obscured_regions_set(Evas_Object_Protected_Data *obj, const Eina_Tiler *tiler)
862 {
863    Evas_Object_Filter_Data *fcow;
864    Eina_Rectangle prev, rect = {};
865    Eina_Rectangle *r;
866    Evas_Filter_Data *pd;
867    Eina_Iterator *it;
868    Eina_Bool was_empty = EINA_FALSE, redraw = EINA_FALSE;
869    int obscured_changes = 0;
870    int area = 0;
871
872    // TODO: Can we handle more than one opaque region?
873
874    pd = efl_data_scope_get(obj->object, MY_CLASS);
875    if (!pd->data) return EINA_FALSE;
876
877    // Find largest opaque rect
878    it = eina_tiler_iterator_new(tiler);
879    EINA_ITERATOR_FOREACH(it, r)
880      {
881         int wh = r->w * r->h;
882         if (wh > area)
883           {
884              area = wh;
885              rect = *r;
886           }
887      }
888    eina_iterator_free(it);
889
890    prev = pd->data->prev_obscured;
891    if (!pd->data->changed && (!prev.w || !prev.h))
892      {
893         was_empty = EINA_TRUE;
894         obscured_changes = 0;
895      }
896    else if (memcmp(&rect, &prev, sizeof(rect)))
897      {
898         fcow = FCOW_BEGIN(pd);
899         fcow->obscured = rect;
900         obscured_changes = fcow->obscured_changes + 1;
901         if (obscured_changes > 2)
902           {
903              // Reset obscure as it changes too much
904              memset(&fcow->obscured, 0, sizeof(fcow->obscured));
905              obscured_changes = 0;
906              redraw = EINA_TRUE;
907           }
908         FCOW_END(fcow, pd);
909      }
910
911    FCOW_WRITE(pd, obscured_changes, obscured_changes);
912    if (redraw) return EINA_TRUE;
913
914    // Snapshot objects need to be redrawn if the padding has increased
915    if ((pd->data->prev_padding.l < pd->data->padding.l) ||
916        (pd->data->prev_padding.r < pd->data->padding.r) ||
917        (pd->data->prev_padding.t < pd->data->padding.t) ||
918        (pd->data->prev_padding.b < pd->data->padding.b))
919      return EINA_TRUE;
920
921    // Snapshot objects need to be redrawn if the obscured region has shrank
922    if (!was_empty && !_evas_eina_rectangle_inside(&pd->data->obscured, &prev))
923      return EINA_TRUE;
924
925    return EINA_FALSE;
926 }
927
928 void
929 _evas_filter_radius_get(Evas_Object_Protected_Data *obj, int *l, int *r, int *t, int *b)
930 {
931    Evas_Filter_Padding pad = {};
932    Evas_Filter_Data *pd;
933
934    pd = efl_data_scope_get(obj->object, MY_CLASS);
935    if (!pd->data || !pd->data->chain) goto end;
936
937    evas_filter_program_padding_get(pd->data->chain, NULL, &pad);
938
939 end:
940    if (l) *l = pad.l;
941    if (r) *r = pad.r;
942    if (t) *t = pad.t;
943    if (b) *b = pad.b;
944 }
945
946 #include "canvas/efl_canvas_filter_internal.eo.c"