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_FALSE
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
evas_filter_mixin_init(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
evas_filter_mixin_shutdown(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
_state_check(Evas_Object_Filter_Data * fcow)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
_filter_end_sync(Evas_Filter_Context * ctx,Evas_Object_Protected_Data * obj,Evas_Filter_Data * pd,Eina_Bool success)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
_filter_async_post_render_cb(void * data)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
_filter_cb(Evas_Filter_Context * ctx,void * data,Eina_Bool success)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
_evas_filter_source_hash_free_cb(void * data)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
_evas_filter_state_set_internal(Evas_Filter_Program * pgm,Evas_Filter_Data * pd)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
_evas_filter_obscured_region_changed(Evas_Filter_Data * pd)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
evas_filter_object_render(Eo * eo_obj,Evas_Object_Protected_Data * obj,void * engine,void * output,void * context,void * surface,int x,int y,Eina_Bool do_async,Eina_Bool alpha)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    W = obj->cur->geometry.w;
259    H = obj->cur->geometry.h;
260    X = obj->cur->geometry.x;
261    Y = obj->cur->geometry.y;
262 
263    // Prepare color multiplier
264    ENFN->context_color_set(engine, context,
265                            obj->cur->cache.clip.r,
266                            obj->cur->cache.clip.g,
267                            obj->cur->cache.clip.b,
268                            obj->cur->cache.clip.a);
269    if (obj->cur->clipper)
270      ENFN->context_multiplier_set(engine, context,
271                                   obj->cur->clipper->cur->cache.clip.r,
272                                   obj->cur->clipper->cur->cache.clip.g,
273                                   obj->cur->clipper->cur->cache.clip.b,
274                                   obj->cur->clipper->cur->cache.clip.a);
275    else
276       ENFN->context_multiplier_unset(engine, context);
277 
278    if (obj->map->cur.usemap && obj->map->cur.map && (obj->map->cur.map->count >= 4))
279      {
280         int iw, ih;
281 
282         use_map = EINA_TRUE;
283         ENFN->image_size_get(engine, previous, &iw, &ih);
284         evas_object_map_update(eo_obj, x, y, iw, ih, iw, ih);
285      }
286 
287    if (!pd->data->chain)
288      {
289         Evas_Filter_Program *pgm;
290         Eina_Bool invalid;
291 
292         pgm = evas_filter_program_new(pd->data->name, alpha);
293         evas_filter_program_source_set_all(pgm, pd->data->sources);
294         evas_filter_program_data_set_all(pgm, pd->data->data);
295         _evas_filter_state_set_internal(pgm, pd);
296         invalid = !evas_filter_program_parse(pgm, pd->data->code);
297         if (invalid)
298           {
299              ERR("Filter program parsing failed");
300              evas_filter_program_del(pgm);
301              pgm = NULL;
302           }
303         fcow = FCOW_BEGIN(pd);
304         if (!invalid) evas_filter_program_padding_get(pgm, NULL, &fcow->padding);
305         fcow->chain = pgm;
306         fcow->invalid = invalid;
307         FCOW_END(fcow, pd);
308         if (invalid) return EINA_FALSE;
309      }
310    else if (previous && !pd->data->changed)
311      {
312         Eina_Bool redraw = EINA_TRUE;
313 
314         if (_evas_filter_state_set_internal(pd->data->chain, pd))
315           DBG("Filter redraw by state change!");
316         else if (obj->changed)
317           DBG("Filter redraw by object content change!");
318         else if (obj->snapshot_needs_redraw)
319           DBG("Filter redraw by snapshot change!");
320         else if (_evas_filter_obscured_region_changed(pd))
321           DBG("Filter redraw by obscure regions change!");
322         else redraw = EINA_FALSE;
323 
324         // Scan proxies to find if any changed
325         if (!redraw && pd->data->sources)
326           {
327              Evas_Filter_Proxy_Binding *pb;
328              Evas_Object_Protected_Data *source;
329              Eina_Iterator *iter;
330 
331              iter = eina_hash_iterator_data_new(pd->data->sources);
332              EINA_ITERATOR_FOREACH(iter, pb)
333                {
334                   source = efl_data_scope_get(pb->eo_source, EFL_CANVAS_OBJECT_CLASS);
335                   if (source->changed)
336                     {
337                        redraw = EINA_TRUE;
338                        break;
339                     }
340                }
341              eina_iterator_free(iter);
342           }
343 
344         if (!redraw)
345           {
346              // Render this image only
347              if (use_map)
348                {
349                   ENFN->image_map_draw(engine, output, context, surface, previous,
350                                        obj->map->spans, EINA_TRUE, 0, do_async);
351                }
352              else
353                {
354                   ENFN->image_draw(engine, output, context,
355                                    surface, previous,
356                                    0, 0, W, H,         // src
357                                    X + x, Y + y, W, H, // dst
358                                    EINA_FALSE,         // smooth
359                                    do_async);
360                }
361              return EINA_TRUE;
362           }
363      }
364    else
365      {
366         _evas_filter_state_set_internal(pd->data->chain, pd);
367      }
368 
369    filter = pd->data->context;
370    if (filter)
371      {
372         int prev_w, prev_h;
373         Eina_Bool was_async;
374 
375         was_async = evas_filter_context_async_get(filter);
376         evas_filter_context_size_get(filter, &prev_w, &prev_h);
377         if ((!pd->data->reuse) || (was_async != do_async) ||
378             (prev_w != W) || (prev_h != H))
379           {
380              evas_filter_context_unref(filter);
381              FCOW_WRITE(pd, context, NULL);
382              filter = NULL;
383           }
384      }
385 
386    if (filter)
387      {
388         ok = evas_filter_context_program_use(engine, output, filter, pd->data->chain, EINA_TRUE, X, Y);
389         if (!ok)
390           {
391              evas_filter_context_unref(filter);
392              FCOW_WRITE(pd, context, NULL);
393              filter = NULL;
394           }
395      }
396 
397    if (!filter)
398      {
399         filter = evas_filter_context_new(obj->layer->evas, do_async, 0);
400 
401         // Run script
402         ok = evas_filter_context_program_use(engine, output, filter, pd->data->chain, EINA_FALSE, X, Y);
403         if (!filter || !ok)
404           {
405              ERR("Parsing failed?");
406              evas_filter_context_unref(filter);
407              FCOW_WRITE(pd, invalid, EINA_TRUE);
408              FCOW_WRITE(pd, context, NULL);
409              return EINA_FALSE;
410           }
411      }
412 
413    // Proxies
414    evas_filter_context_proxy_render_all(filter, eo_obj, output, EINA_FALSE);
415 
416    // Draw Context
417    drawctx = ENFN->context_new(engine);
418    ENFN->context_color_set(engine, drawctx,
419                            obj->cur->cache.clip.r,
420                            obj->cur->cache.clip.g,
421                            obj->cur->cache.clip.b,
422                            obj->cur->cache.clip.a);
423 
424    // Set obscured region
425    evas_filter_context_obscured_region_set(filter, pd->data->obscured);
426 
427    // Allocate all buffers now
428    evas_filter_context_buffers_allocate_all(filter);
429    evas_filter_target_set(filter, context, surface, X + x, Y + y,
430                           use_map ? obj->map->spans : NULL);
431 
432    // Request rendering from the object itself (child class)
433    evas_filter_program_padding_get(pd->data->chain, &pad, NULL);
434    ok = evas_filter_input_render(eo_obj, filter, engine, output, drawctx, NULL,
435                                  pad.l, pad.r, pad.t, pad.b, 0, 0, do_async);
436    if (!ok) ERR("Filter input render failed.");
437 
438    ENFN->context_free(engine, drawctx);
439 
440    // Add post-run callback and run filter
441    evas_filter_context_post_run_callback_set(filter, _filter_cb, pd);
442 
443    fcow = FCOW_BEGIN(pd);
444    fcow->context = filter;
445    fcow->changed = EINA_FALSE;
446    fcow->async = do_async;
447    fcow->prev_obscured = fcow->obscured;
448    fcow->prev_padding = fcow->padding;
449    fcow->padding = pad;
450    fcow->invalid = EINA_FALSE;
451    FCOW_END(fcow, pd);
452 
453    // Run the filter now (maybe async)
454    efl_ref(eo_obj);
455    ok = evas_filter_context_run(engine, output, filter);
456    if (!ok) ERR("Filter program failed to run!");
457 
458    return ok;
459 }
460 
461 EOLIAN static void
_efl_canvas_filter_internal_efl_gfx_filter_filter_program_set(Eo * eo_obj,Evas_Filter_Data * pd,const char * code,const char * name)462 _efl_canvas_filter_internal_efl_gfx_filter_filter_program_set(Eo *eo_obj, Evas_Filter_Data *pd,
463                                                               const char *code, const char *name)
464 {
465    Evas_Object_Protected_Data *obj;
466    Evas_Filter_Program *pgm = NULL;
467    Evas_Object_Filter_Data *fcow;
468    Eina_Bool invalid = pd->data->invalid;
469    Eina_Bool alpha;
470 
471    obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
472    if (eina_streq(pd->data->code, code) && eina_streq(pd->data->name, name))
473      return;
474 
475    evas_object_async_block(obj);
476    fcow = FCOW_BEGIN(pd);
477    {
478       fcow->obj = obj;
479 
480       evas_filter_context_unref(fcow->context);
481       fcow->context = NULL;
482 
483       // Parse filter program
484       evas_filter_program_del(fcow->chain);
485       eina_stringshare_replace(&fcow->name, name);
486       if (code)
487         {
488            alpha = evas_filter_input_alpha(eo_obj);
489            pgm = evas_filter_program_new(fcow->name, alpha);
490            evas_filter_program_source_set_all(pgm, fcow->sources);
491            evas_filter_program_data_set_all(pgm, fcow->data);
492            _evas_filter_state_set_internal(pgm, pd);
493            invalid = !evas_filter_program_parse(pgm, code);
494            if (invalid)
495              {
496                 ERR("Parsing failed!");
497                 evas_filter_program_del(pgm);
498                 pgm = NULL;
499              }
500            else
501              {
502                 evas_filter_program_padding_get(pgm, NULL, &fcow->padding);
503              }
504         }
505       fcow->chain = pgm;
506       fcow->changed = EINA_TRUE;
507       fcow->invalid = invalid;
508       eina_stringshare_replace(&fcow->code, code);
509    }
510    FCOW_END(fcow, pd);
511 
512    evas_filter_dirty(eo_obj);
513 }
514 
515 EOLIAN static void
_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)516 _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)
517 {
518    if (code) *code = pd->data->code;
519    if (name) *name = pd->data->name;
520 }
521 
522 EOLIAN static void
_efl_canvas_filter_internal_efl_gfx_filter_filter_source_set(Eo * eo_obj,Evas_Filter_Data * pd,const char * name,Efl_Gfx_Entity * eo_source)523 _efl_canvas_filter_internal_efl_gfx_filter_filter_source_set(Eo *eo_obj, Evas_Filter_Data *pd,
524                                                              const char *name, Efl_Gfx_Entity *eo_source)
525 {
526    Evas_Object_Protected_Data *obj;
527    Evas_Filter_Proxy_Binding *pb, *pb_old = NULL;
528    Evas_Object_Protected_Data *source = NULL;
529    Evas_Object_Filter_Data *fcow = NULL;
530    Eina_Bool invalid = pd->data->invalid;
531 
532    obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
533    if (eo_source)
534      source = efl_data_scope_get(eo_source, EFL_CANVAS_OBJECT_CLASS);
535 
536    evas_object_async_block(obj);
537    if (!name)
538      {
539         if (!eo_source || !pd->data->sources) return;
540         if (eina_hash_del_by_data(pd->data->sources, eo_source))
541           goto update;
542         return;
543      }
544 
545    if (!source && !pd->data->sources)
546      return;
547 
548    if (pd->data->sources)
549      {
550         pb_old = eina_hash_find(pd->data->sources, name);
551         if (pb_old && (pb_old->eo_source == eo_source)) return;
552      }
553 
554    fcow = FCOW_BEGIN(pd);
555    if (!fcow->sources)
556      fcow->sources = eina_hash_string_small_new(_evas_filter_source_hash_free_cb);
557    else if (pb_old)
558      eina_hash_del(fcow->sources, name, pb_old);
559 
560    if (!source)
561      {
562         pb_old = eina_hash_find(fcow->sources, name);
563         if (!pb_old)
564           {
565              FCOW_END(fcow, pd);
566              return;
567           }
568         eina_hash_del_by_key(fcow->sources, name);
569         goto update;
570      }
571 
572    pb = calloc(1, sizeof(*pb));
573    pb->eo_proxy = eo_obj;
574    pb->eo_source = eo_source;
575    pb->name = eina_stringshare_add(name);
576 
577    if (!eina_list_data_find(source->proxy->proxies, eo_obj))
578      {
579         EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, source->proxy, Evas_Object_Proxy_Data, source_write)
580           source_write->proxies = eina_list_append(source_write->proxies, eo_obj);
581         EINA_COW_WRITE_END(evas_object_proxy_cow, source->proxy, source_write)
582      }
583 
584    if (!obj->proxy->is_proxy)
585      {
586         EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, obj->proxy, Evas_Object_Proxy_Data, proxy_write)
587           proxy_write->is_proxy = EINA_TRUE;
588         EINA_COW_WRITE_END(evas_object_proxy_cow, obj->proxy, proxy_write)
589      }
590 
591    eina_hash_add(fcow->sources, pb->name, pb);
592    evas_filter_program_source_set_all(fcow->chain, fcow->sources);
593    evas_filter_program_data_set_all(fcow->chain, fcow->data);
594    invalid = !evas_filter_program_parse(fcow->chain, fcow->code);
595    if (!invalid) evas_filter_program_padding_get(fcow->chain, NULL, &fcow->padding);
596 
597    // Update object
598 update:
599    if (fcow)
600      {
601         fcow->changed = EINA_TRUE;
602         fcow->invalid = invalid;
603         FCOW_END(fcow, pd);
604      }
605 
606    evas_filter_dirty(eo_obj);
607 }
608 
609 EOLIAN static Efl_Gfx_Entity *
_efl_canvas_filter_internal_efl_gfx_filter_filter_source_get(const Eo * obj EINA_UNUSED,Evas_Filter_Data * pd,const char * name)610 _efl_canvas_filter_internal_efl_gfx_filter_filter_source_get(const Eo *obj EINA_UNUSED, Evas_Filter_Data *pd,
611                                                              const char * name)
612 {
613    Evas_Filter_Proxy_Binding *pb = eina_hash_find(pd->data->sources, name);
614    if (!pb) return NULL;
615    return pb->eo_source;
616 }
617 
618 EOLIAN static void
_efl_canvas_filter_internal_efl_gfx_filter_filter_state_set(Eo * eo_obj,Evas_Filter_Data * pd,const char * cur_state,double cur_val,const char * next_state,double next_val,double pos)619 _efl_canvas_filter_internal_efl_gfx_filter_filter_state_set(Eo *eo_obj, Evas_Filter_Data *pd,
620                                                             const char *cur_state, double cur_val,
621                                                             const char *next_state, double next_val,
622                                                             double pos)
623 {
624    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
625 
626    evas_object_async_block(obj);
627    if ((cur_state != pd->data->state.cur.name) ||
628        (!EINA_DBL_EQ(cur_val, pd->data->state.cur.value)) ||
629        (next_state != pd->data->state.next.name) ||
630        (!EINA_DBL_EQ(next_val, pd->data->state.next.value)) ||
631        (!EINA_DBL_EQ(pos, pd->data->state.pos)))
632      {
633         Evas_Object_Filter_Data *fcow = FCOW_BEGIN(pd);
634         fcow->changed = 1;
635         eina_stringshare_replace(&fcow->state.cur.name, cur_state);
636         fcow->state.cur.value = cur_val;
637         eina_stringshare_replace(&fcow->state.next.name, next_state);
638         fcow->state.next.value = next_val;
639         fcow->state.pos = pos;
640         FCOW_END(fcow, pd);
641 
642         if (pd->data->chain)
643           {
644              _evas_filter_state_set_internal(pd->data->chain, pd);
645           }
646 
647         evas_filter_dirty(eo_obj);
648      }
649 }
650 
651 EOLIAN static void
_efl_canvas_filter_internal_efl_gfx_filter_filter_state_get(const Eo * obj EINA_UNUSED,Evas_Filter_Data * pd,const char ** cur_state,double * cur_val,const char ** next_state,double * next_val,double * pos)652 _efl_canvas_filter_internal_efl_gfx_filter_filter_state_get(const Eo *obj EINA_UNUSED, Evas_Filter_Data *pd,
653                                                             const char **cur_state, double *cur_val,
654                                                             const char **next_state, double *next_val,
655                                                             double *pos)
656 {
657    if (cur_state) *cur_state = pd->data->state.cur.name;
658    if (cur_val) *cur_val = pd->data->state.cur.value;
659    if (next_state) *next_state = pd->data->state.next.name;
660    if (next_val) *next_val = pd->data->state.next.value;
661    if (pos) *pos = pd->data->state.pos;
662 }
663 
664 EOLIAN static void
_efl_canvas_filter_internal_efl_gfx_filter_filter_padding_get(const Eo * eo_obj EINA_UNUSED,Evas_Filter_Data * pd,int * l,int * r,int * t,int * b)665 _efl_canvas_filter_internal_efl_gfx_filter_filter_padding_get(const Eo *eo_obj EINA_UNUSED, Evas_Filter_Data *pd,
666                                                               int *l, int *r, int *t, int *b)
667 {
668    Evas_Filter_Padding pad = { 0, 0, 0, 0 };
669 
670    if (pd->data->chain)
671      evas_filter_program_padding_get(pd->data->chain, &pad, NULL);
672 
673    if (l) *l = pad.l;
674    if (r) *r = pad.r;
675    if (t) *t = pad.t;
676    if (b) *b = pad.b;
677 }
678 
679 EOLIAN static void
_efl_canvas_filter_internal_filter_changed_set(Eo * eo_obj EINA_UNUSED,Evas_Filter_Data * pd,Eina_Bool val)680 _efl_canvas_filter_internal_filter_changed_set(Eo *eo_obj EINA_UNUSED, Evas_Filter_Data *pd, Eina_Bool val)
681 {
682    if ((&evas_filter_data_cow_default != pd->data) && (pd->data->changed != val))
683      {
684         Evas_Object_Filter_Data *fcow = FCOW_BEGIN(pd);
685         fcow->changed = val;
686         FCOW_END(fcow, pd);
687      }
688 }
689 
690 EOLIAN static void
_efl_canvas_filter_internal_filter_invalid_set(Eo * eo_obj EINA_UNUSED,Evas_Filter_Data * pd,Eina_Bool val)691 _efl_canvas_filter_internal_filter_invalid_set(Eo *eo_obj EINA_UNUSED, Evas_Filter_Data *pd, Eina_Bool val)
692 {
693    if (pd->data->invalid != val)
694      {
695         Evas_Object_Filter_Data *fcow = FCOW_BEGIN(pd);
696         fcow->invalid = val;
697         FCOW_END(fcow, pd);
698      }
699 }
700 
701 EOLIAN static Efl_Object *
_efl_canvas_filter_internal_efl_object_constructor(Eo * eo_obj,Evas_Filter_Data * pd)702 _efl_canvas_filter_internal_efl_object_constructor(Eo *eo_obj, Evas_Filter_Data *pd)
703 {
704    Eo *obj = NULL;
705 
706    obj = efl_constructor(efl_super(eo_obj, MY_CLASS));
707    pd->data = eina_cow_alloc(evas_object_filter_cow);
708 
709    return obj;
710 }
711 
712 EOLIAN static void
_efl_canvas_filter_internal_efl_object_destructor(Eo * eo_obj,Evas_Filter_Data * pd)713 _efl_canvas_filter_internal_efl_object_destructor(Eo *eo_obj, Evas_Filter_Data *pd)
714 {
715    Evas_Object_Protected_Data *obj;
716    Evas_Object_Filter_Data *fcow;
717    Evas_Filter_Data_Binding *db;
718    Evas_Public_Data *e;
719    Eina_Inlist *il;
720 
721    if (!pd->data || (&evas_filter_data_cow_default == pd->data))
722      goto finish;
723 
724    obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
725    e = obj->layer->evas;
726 
727    if (pd->data->context)
728      {
729         evas_filter_context_unref(pd->data->context);
730         FCOW_WRITE(pd, context, NULL);
731      }
732 
733    if (pd->data->output)
734      {
735         if (!pd->data->async)
736           ENFN->image_free(ENC, pd->data->output);
737         else
738           evas_unref_queue_image_put(e, pd->data->output);
739      }
740    eina_hash_free(pd->data->sources);
741    EINA_INLIST_FOREACH_SAFE(pd->data->data, il, db)
742      {
743         eina_stringshare_del(db->name);
744         eina_stringshare_del(db->value);
745         free(db);
746      }
747    evas_filter_program_del(pd->data->chain);
748    eina_stringshare_del(pd->data->code);
749    eina_stringshare_del(pd->data->state.cur.name);
750    eina_stringshare_del(pd->data->state.next.name);
751 
752 finish:
753    eina_cow_free(evas_object_filter_cow, (const Eina_Cow_Data **) &pd->data);
754 
755    efl_destructor(efl_super(eo_obj, MY_CLASS));
756 }
757 
758 EOLIAN static void
_efl_canvas_filter_internal_efl_gfx_filter_filter_data_set(Eo * eo_obj,Evas_Filter_Data * pd,const char * name,const char * value,Eina_Bool execute)759 _efl_canvas_filter_internal_efl_gfx_filter_filter_data_set(Eo *eo_obj, Evas_Filter_Data *pd,
760                                                            const char *name, const char *value,
761                                                            Eina_Bool execute)
762 {
763    Evas_Filter_Data_Binding *db, *found = NULL;
764    Evas_Object_Filter_Data *fcow;
765    Eina_Bool invalid = pd->data->invalid;
766 
767    EINA_SAFETY_ON_NULL_RETURN(pd->data);
768    EINA_SAFETY_ON_NULL_RETURN(name);
769 
770    EINA_INLIST_FOREACH(pd->data->data, db)
771      {
772         if (!strcmp(name, db->name))
773           {
774              if (db->execute == execute)
775                {
776                   if ((value == db->value) || (value && db->value && !strcmp(value, db->value)))
777                     return;
778                }
779              found = db;
780              break;
781           }
782      }
783 
784    fcow = FCOW_BEGIN(pd);
785    {
786       if (found)
787         {
788            // Note: we are keeping references to NULL values here.
789            eina_stringshare_replace(&found->value, value);
790            found->execute = execute;
791         }
792       else if (value)
793         {
794            db = calloc(1, sizeof(Evas_Filter_Data_Binding));
795            db->name = eina_stringshare_add(name);
796            db->value = eina_stringshare_add(value);
797            db->execute = execute;
798            fcow->data = eina_inlist_append(fcow->data, EINA_INLIST_GET(db));
799         }
800       if (fcow->chain)
801         {
802            evas_filter_program_data_set_all(fcow->chain, fcow->data);
803            invalid = !evas_filter_program_parse(fcow->chain, fcow->code);
804            if (!invalid) evas_filter_program_padding_get(fcow->chain, NULL, &fcow->padding);
805         }
806       fcow->invalid = invalid;
807       fcow->changed = 1;
808    }
809    FCOW_END(fcow, pd);
810 
811    evas_filter_dirty(eo_obj);
812 }
813 
814 EOLIAN static void
_efl_canvas_filter_internal_efl_gfx_filter_filter_data_get(const Eo * obj EINA_UNUSED,Evas_Filter_Data * pd,const char * name,const char ** value,Eina_Bool * execute)815 _efl_canvas_filter_internal_efl_gfx_filter_filter_data_get(const Eo *obj EINA_UNUSED, Evas_Filter_Data *pd,
816                                                            const char *name, const char **value,
817                                                            Eina_Bool *execute)
818 {
819    Evas_Filter_Data_Binding *db;
820 
821    if (!value && !execute) return;
822    EINA_SAFETY_ON_NULL_RETURN(pd->data);
823 
824    EINA_INLIST_FOREACH(pd->data->data, db)
825      {
826         if (!strcmp(name, db->name))
827           {
828              if (value) *value = db->value;
829              if (execute) *execute = db->execute;
830              return;
831           }
832      }
833 
834    if (value) *value = NULL;
835    if (execute) *execute = EINA_FALSE;
836 }
837 
838 EOLIAN static void *
_efl_canvas_filter_internal_filter_output_buffer_get(const Eo * obj EINA_UNUSED,Evas_Filter_Data * pd)839 _efl_canvas_filter_internal_filter_output_buffer_get(const Eo *obj EINA_UNUSED, Evas_Filter_Data *pd)
840 {
841    return pd->data->output;
842 }
843 
844 Eina_Bool
_evas_filter_obscured_regions_set(Evas_Object_Protected_Data * obj,const Eina_Tiler * tiler)845 _evas_filter_obscured_regions_set(Evas_Object_Protected_Data *obj, const Eina_Tiler *tiler)
846 {
847    Evas_Object_Filter_Data *fcow;
848    Eina_Rectangle prev, rect = {};
849    Eina_Rectangle *r;
850    Evas_Filter_Data *pd;
851    Eina_Iterator *it;
852    Eina_Bool was_empty = EINA_FALSE, redraw = EINA_FALSE;
853    int obscured_changes = 0;
854    int area = 0;
855 
856    // TODO: Can we handle more than one opaque region?
857 
858    pd = efl_data_scope_get(obj->object, MY_CLASS);
859    if (!pd->data) return EINA_FALSE;
860 
861    // Find largest opaque rect
862    it = eina_tiler_iterator_new(tiler);
863    EINA_ITERATOR_FOREACH(it, r)
864      {
865         int wh = r->w * r->h;
866         if (wh > area)
867           {
868              area = wh;
869              rect = *r;
870           }
871      }
872    eina_iterator_free(it);
873 
874    prev = pd->data->prev_obscured;
875    if (!pd->data->changed && (!prev.w || !prev.h))
876      {
877         was_empty = EINA_TRUE;
878         obscured_changes = 0;
879      }
880    else if (memcmp(&rect, &prev, sizeof(rect)))
881      {
882         fcow = FCOW_BEGIN(pd);
883         fcow->obscured = rect;
884         obscured_changes = fcow->obscured_changes + 1;
885         if (obscured_changes > 2)
886           {
887              // Reset obscure as it changes too much
888              memset(&fcow->obscured, 0, sizeof(fcow->obscured));
889              obscured_changes = 0;
890              redraw = EINA_TRUE;
891           }
892         FCOW_END(fcow, pd);
893      }
894 
895    FCOW_WRITE(pd, obscured_changes, obscured_changes);
896    if (redraw) return EINA_TRUE;
897 
898    // Snapshot objects need to be redrawn if the padding has increased
899    if ((pd->data->prev_padding.l < pd->data->padding.l) ||
900        (pd->data->prev_padding.r < pd->data->padding.r) ||
901        (pd->data->prev_padding.t < pd->data->padding.t) ||
902        (pd->data->prev_padding.b < pd->data->padding.b))
903      return EINA_TRUE;
904 
905    // Snapshot objects need to be redrawn if the obscured region has shrank
906    if (!was_empty && !_evas_eina_rectangle_inside(&pd->data->obscured, &prev))
907      return EINA_TRUE;
908 
909    return EINA_FALSE;
910 }
911 
912 void
_evas_filter_radius_get(Evas_Object_Protected_Data * obj,int * l,int * r,int * t,int * b)913 _evas_filter_radius_get(Evas_Object_Protected_Data *obj, int *l, int *r, int *t, int *b)
914 {
915    Evas_Filter_Padding pad = {};
916    Evas_Filter_Data *pd;
917 
918    pd = efl_data_scope_get(obj->object, MY_CLASS);
919    if (!pd->data || !pd->data->chain) goto end;
920 
921    evas_filter_program_padding_get(pd->data->chain, NULL, &pad);
922 
923 end:
924    if (l) *l = pad.l;
925    if (r) *r = pad.r;
926    if (t) *t = pad.t;
927    if (b) *b = pad.b;
928 }
929 
930 #include "canvas/efl_canvas_filter_internal.eo.c"
931