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