1 /*
2  * \@file evas_filter.c
3  *
4  * Infrastructure for simple filters applied to RGBA and Alpha buffers.
5  * Originally used by font effects.
6  *
7  * Filters include:
8  * - Blur (Gaussian, Box, Motion) and Shadows
9  * - Bump maps (light effects)
10  * - Displacement maps
11  * - Color curves
12  * - Blending and masking
13  *
14  * The reference documentation can be found in evas_filter_parser.c
15  */
16 
17 #include "evas_filter.h"
18 
19 #include "evas_filter_private.h"
20 #include <Ector.h>
21 #include <software/Ector_Software.h>
22 #include "evas_ector_buffer.eo.h"
23 
24 #define _assert(a) if (!(a)) CRI("Failed on %s", #a);
25 
26 static void _buffer_free(Evas_Filter_Buffer *fb);
27 static void _command_del(Evas_Filter_Context *ctx, Evas_Filter_Command *cmd);
28 static Evas_Filter_Buffer *_buffer_alloc_new(Evas_Filter_Context *ctx, int w, int h, Eina_Bool alpha_only, Eina_Bool render, Eina_Bool draw);
29 static void _filter_buffer_unlock_all(Evas_Filter_Context *ctx);
30 
31 #define DRAW_COLOR_SET(r, g, b, a) do { cmd->draw.R = r; cmd->draw.G = g; cmd->draw.B = b; cmd->draw.A = a; } while (0)
32 #define DRAW_CLIP_SET(_x, _y, _w, _h) do { cmd->draw.clip.x = _x; cmd->draw.clip.y = _y; cmd->draw.clip.w = _w; cmd->draw.clip.h = _h; } while (0)
33 #define DRAW_FILL_SET(fmode) do { cmd->draw.fillmode = fmode; } while (0)
34 
35 /* Main functions */
36 
37 #define _free(ptr) free(ptr)
38 //eina_freeq_ptr_main_add(ptr, NULL, sizeof(*ptr))
39 
40 
41 /* FIXME: This code is come from a log by
42    CRI<14853>:eo lib/eo/eo.c:1894 efl_unref() Calling efl_unref instead of efl_del or efl_parent_set(NULL).
43    Temporary fallback in place triggered." When u get correct method, please fix this. */
44 static void
_buffer_del(Eo * buffer)45 _buffer_del(Eo *buffer)
46 {
47    if (!buffer) return;
48 
49    if (efl_parent_get(buffer))
50      efl_del(buffer);
51    else
52      efl_unref(buffer);
53 }
54 
55 Evas_Filter_Context *
evas_filter_context_new(Evas_Public_Data * evas,Eina_Bool async,void * user_data)56 evas_filter_context_new(Evas_Public_Data *evas, Eina_Bool async, void *user_data)
57 {
58    Evas_Filter_Context *ctx;
59 
60    EINA_SAFETY_ON_NULL_RETURN_VAL(evas, NULL);
61    EINA_SAFETY_ON_NULL_RETURN_VAL(evas->engine.func->gfx_filter_supports, NULL);
62    EINA_SAFETY_ON_NULL_RETURN_VAL(evas->engine.func->gfx_filter_process, NULL);
63 
64    ctx = calloc(1, sizeof(Evas_Filter_Context));
65    if (!ctx) return NULL;
66 
67    ctx->evas = evas;
68    ctx->async = async;
69    ctx->user_data = user_data;
70    ctx->buffer_scaled_get = &evas_filter_buffer_scaled_get;
71    ctx->gl = (ENFN->gl_surface_read_pixels != NULL);
72    ctx->refcount = 1;
73 
74    return ctx;
75 }
76 
77 void *
evas_filter_context_data_get(Evas_Filter_Context * ctx)78 evas_filter_context_data_get(Evas_Filter_Context *ctx)
79 {
80    EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, NULL);
81 
82    return ctx->user_data;
83 }
84 
85 Eina_Bool
evas_filter_context_async_get(Evas_Filter_Context * ctx)86 evas_filter_context_async_get(Evas_Filter_Context *ctx)
87 {
88    return ctx->async;
89 }
90 
91 void
evas_filter_context_size_get(Evas_Filter_Context * ctx,int * w,int * h)92 evas_filter_context_size_get(Evas_Filter_Context *ctx, int *w, int *h)
93 {
94    if (w) *w = ctx->w;
95    if (h) *h = ctx->h;
96 }
97 
98 /* Private function to reset the filter context. Used from parser.c */
99 void
evas_filter_context_clear(Evas_Filter_Context * ctx,Eina_Bool keep_buffers)100 evas_filter_context_clear(Evas_Filter_Context *ctx, Eina_Bool keep_buffers)
101 {
102    Evas_Filter_Command *cmd;
103    Evas_Filter_Buffer *fb;
104 
105    if (!ctx) return;
106 
107    if (ctx->target.surface) ENFN->image_free(ENC, ctx->target.surface);
108    if (ctx->target.mask) ENFN->image_free(ENC, ctx->target.mask);
109    ctx->target.surface = NULL;
110    ctx->target.mask = NULL;
111 
112    if (!keep_buffers)
113      {
114         ctx->last_buffer_id = 0;
115         EINA_LIST_FREE(ctx->buffers, fb)
116           _buffer_free(fb);
117      }
118 
119    ctx->last_command_id = 0;
120    EINA_INLIST_FREE(ctx->commands, cmd)
121      _command_del(ctx, cmd);
122 
123    // Note: don't reset post_run, as it it set by the client
124 }
125 
126 static void
_filter_buffer_backing_free(Evas_Filter_Buffer * fb)127 _filter_buffer_backing_free(Evas_Filter_Buffer *fb)
128 {
129    if (!fb || !fb->buffer) return;
130    _buffer_del((Eo *)fb->buffer);
131    fb->buffer = NULL;
132 }
133 
134 /** @hidden private render proxy objects */
135 void
evas_filter_context_proxy_render_all(Evas_Filter_Context * ctx,Eo * eo_obj,void * output,Eina_Bool do_async)136 evas_filter_context_proxy_render_all(Evas_Filter_Context *ctx, Eo *eo_obj, void *output,
137                                      Eina_Bool do_async)
138 {
139    Evas_Object_Protected_Data *source;
140    Evas_Object_Protected_Data *obj;
141    void *proxy_surface;
142    Evas_Filter_Buffer *fb;
143    Eina_List *li;
144 
145    if (!ctx->has_proxies) return;
146    obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
147 
148    EINA_LIST_FOREACH(ctx->buffers, li, fb)
149      if (fb->source)
150        {
151           // TODO: Lock current object as proxyrendering (see image obj)
152           source = efl_data_scope_get(fb->source, EFL_CANVAS_OBJECT_CLASS);
153           _assert(fb->w == source->cur->geometry.w);
154           _assert(fb->h == source->cur->geometry.h);
155           proxy_surface = source->proxy->surface;
156           if (source->proxy->surface && !source->proxy->redraw)
157             {
158                XDBG("Source already rendered: '%s' of type '%s'",
159                    fb->source_name, efl_class_name_get(efl_class_get(fb->source)));
160             }
161           else
162             {
163                XDBG("Source needs to be rendered: '%s' of type '%s' (%s)",
164                    fb->source_name, efl_class_name_get(efl_class_get(fb->source)),
165                    source->proxy->redraw ? "redraw" : "no surface");
166                evas_render_proxy_subrender(ctx->evas->evas, output, fb->source, eo_obj, obj, EINA_FALSE, do_async);
167             }
168           if (fb->buffer)
169             {
170                void *old_surface;
171 
172                old_surface = evas_ector_buffer_drawable_image_get(fb->buffer);
173                if (old_surface)
174                  {
175                     evas_ector_buffer_engine_image_release(fb->buffer, old_surface);
176                     if (old_surface && (old_surface != proxy_surface))
177                       _filter_buffer_backing_free(fb);
178                  }
179             }
180           XDBG("Source #%d '%s' has dimensions %dx%d", fb->id, fb->source_name, fb->w, fb->h);
181           if (!fb->buffer) fb->buffer = ENFN->ector_buffer_wrap(ENC, obj->layer->evas->evas, source->proxy->surface);
182           fb->alpha_only = EINA_FALSE;
183        }
184 }
185 
186 void
_evas_filter_context_program_reuse(void * engine,void * output,Evas_Filter_Context * ctx)187 _evas_filter_context_program_reuse(void *engine, void *output, Evas_Filter_Context *ctx)
188 {
189    Evas_Filter_Buffer *fb;
190    Eina_List *li;
191 
192    _filter_buffer_unlock_all(ctx);
193 
194    EINA_LIST_FOREACH(ctx->buffers, li, fb)
195      {
196         void *dc, *surface;
197 
198         fb->used = EINA_FALSE;
199         fb->locked = EINA_FALSE;
200 
201         if (!fb->is_render) continue;
202         if (fb->source) continue;
203 
204         surface = evas_ector_buffer_render_image_get(fb->buffer);
205         if (!surface) continue;
206 
207         dc = ENFN->context_new(engine);
208         ENFN->context_color_set(engine, dc, 0, 0, 0, 0);
209         ENFN->context_render_op_set(engine, dc, EVAS_RENDER_COPY);
210         ENFN->rectangle_draw(engine, output, dc, surface, 0, 0, fb->w, fb->h, ctx->async);
211         ENFN->context_free(engine, dc);
212         fb->dirty = EINA_FALSE;
213 
214         evas_ector_buffer_engine_image_release(fb->buffer, surface);
215      }
216 }
217 
218 static void
_context_destroy(void * data)219 _context_destroy(void *data)
220 {
221    Evas_Filter_Context *ctx = data;
222 
223    EINA_SAFETY_ON_FALSE_RETURN(ctx->refcount == 0);
224    evas_filter_context_clear(ctx, EINA_FALSE);
225    _free(ctx);
226 }
227 
228 int
evas_filter_context_ref(Evas_Filter_Context * ctx)229 evas_filter_context_ref(Evas_Filter_Context *ctx)
230 {
231    EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, -1);
232 
233 #ifdef FILTERS_DEBUG
234    EINA_SAFETY_ON_FALSE_RETURN_VAL(eina_main_loop_is(), -1);
235    EINA_SAFETY_ON_FALSE_RETURN_VAL(ctx->refcount > 0, -1);
236 #endif
237 
238    return (++ctx->refcount);
239 }
240 
241 void
evas_filter_context_unref(Evas_Filter_Context * ctx)242 evas_filter_context_unref(Evas_Filter_Context *ctx)
243 {
244    if (!ctx) return;
245 
246 #ifdef FILTERS_DEBUG
247    EINA_SAFETY_ON_FALSE_RETURN(eina_main_loop_is());
248    EINA_SAFETY_ON_FALSE_RETURN(ctx->refcount > 0);
249 #endif
250 
251    if ((--ctx->refcount) != 0) return;
252 
253    if (!ctx->running)
254      _context_destroy(ctx);
255    // else: post_run_cb will be called
256 }
257 
258 void
evas_filter_context_post_run_callback_set(Evas_Filter_Context * ctx,Evas_Filter_Cb cb,void * data)259 evas_filter_context_post_run_callback_set(Evas_Filter_Context *ctx,
260                                           Evas_Filter_Cb cb, void *data)
261 {
262    EINA_SAFETY_ON_NULL_RETURN(ctx);
263    ctx->post_run.cb = cb;
264    ctx->post_run.data = data;
265 }
266 
267 static Evas_Filter_Buffer *
_buffer_empty_new(Evas_Filter_Context * ctx,int w,int h,Eina_Bool alpha_only,Eina_Bool transient)268 _buffer_empty_new(Evas_Filter_Context *ctx, int w, int h, Eina_Bool alpha_only,
269                   Eina_Bool transient)
270 {
271    Evas_Filter_Buffer *fb;
272 
273    fb = calloc(1, sizeof(Evas_Filter_Buffer));
274    if (!fb) return NULL;
275 
276    fb->id = ++(ctx->last_buffer_id);
277    fb->ctx = ctx;
278    fb->alpha_only = alpha_only;
279    fb->transient = transient;
280    fb->w = w;
281    fb->h = h;
282 
283    ctx->buffers = eina_list_append(ctx->buffers, fb);
284    return fb;
285 }
286 
287 static Ector_Buffer *
_ector_buffer_create(Evas_Filter_Buffer const * fb,Eina_Bool render,Eina_Bool draw)288 _ector_buffer_create(Evas_Filter_Buffer const *fb, Eina_Bool render, Eina_Bool draw)
289 {
290    Efl_Gfx_Colorspace cspace = EFL_GFX_COLORSPACE_ARGB8888;
291    Ector_Buffer_Flag flags;
292 
293    // FIXME: Once all filters are GL buffers need not be CPU accessible
294    flags = ECTOR_BUFFER_FLAG_CPU_READABLE | ECTOR_BUFFER_FLAG_CPU_WRITABLE;
295    if (render) flags |= ECTOR_BUFFER_FLAG_RENDERABLE;
296    if (draw) flags |= ECTOR_BUFFER_FLAG_DRAWABLE;
297    if (fb->alpha_only) cspace = EFL_GFX_COLORSPACE_GRY8;
298 
299    return fb->ENFN->ector_buffer_new(FB_ENC, fb->ctx->evas->evas,
300                                      fb->w, fb->h, cspace, flags);
301 }
302 
303 Eina_Bool
evas_filter_context_buffers_allocate_all(Evas_Filter_Context * ctx)304 evas_filter_context_buffers_allocate_all(Evas_Filter_Context *ctx)
305 {
306    Evas_Filter_Command *cmd;
307    Evas_Filter_Buffer *fb;
308    Eina_List *li, *li2;
309    unsigned w, h;
310 
311    EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, EINA_FALSE);
312    w = ctx->w;
313    h = ctx->h;
314 
315    XDBG("Allocating all buffers based on output size %ux%u", w, h);
316 
317    EINA_LIST_FOREACH(ctx->buffers, li, fb)
318      fb->cleanup = EINA_TRUE;
319 
320    EINA_INLIST_FOREACH(ctx->commands, cmd)
321      {
322         Evas_Filter_Fill_Mode fillmode = cmd->draw.fillmode;
323         Evas_Filter_Buffer *in, *out;
324 
325         in = cmd->input;
326         EINA_SAFETY_ON_NULL_GOTO(in, alloc_fail);
327 
328         in->cleanup = EINA_FALSE;
329         if (!in->w && !in->h)
330           {
331              in->w = w;
332              in->h = h;
333           }
334 
335         if (cmd->mask)
336           cmd->mask->cleanup = EINA_FALSE;
337 
338         // FIXME: No need for stretch buffers with GL!
339         if (fillmode & EVAS_FILTER_FILL_MODE_STRETCH_XY)
340           {
341              unsigned sw = w, sh = h;
342 
343              switch (cmd->mode)
344                {
345                 case EVAS_FILTER_MODE_BLEND:
346                   in = cmd->input;
347                   break;
348                 case EVAS_FILTER_MODE_BUMP:
349                 case EVAS_FILTER_MODE_DISPLACE:
350                 case EVAS_FILTER_MODE_MASK:
351                   in = cmd->mask;
352                   break;
353                 default:
354                   CRI("Invalid fillmode set for command %d", cmd->mode);
355                   return EINA_FALSE;
356                }
357 
358              EINA_SAFETY_ON_NULL_GOTO(in, alloc_fail);
359              if (in->w) sw = in->w;
360              if (in->h) sh = in->h;
361 
362              if ((sw != w) || (sh != h))
363                {
364                   if (fillmode & EVAS_FILTER_FILL_MODE_STRETCH_X)
365                     sw = w;
366                   if (fillmode & EVAS_FILTER_FILL_MODE_STRETCH_Y)
367                     sh = h;
368 
369                   fb = _buffer_alloc_new(ctx, sw, sh, in->alpha_only, 1, 1);
370                   XDBG("Allocated temporary buffer #%d of size %ux%u %s",
371                        fb ? fb->id : -1, sw, sh, in->alpha_only ? "alpha" : "rgba");
372                   if (!fb) goto alloc_fail;
373                   fb->transient = EINA_TRUE;
374                   fb->cleanup = EINA_FALSE;
375                }
376           }
377 
378         if (cmd->draw.need_temp_buffer)
379           {
380              unsigned sw = w, sh = h;
381 
382              in = cmd->input;
383              if (in->w) sw = in->w;
384              if (in->h) sh = in->h;
385 
386              fb = _buffer_alloc_new(ctx, sw, sh, in->alpha_only, 1, 1);
387              XDBG("Allocated temporary buffer #%d of size %ux%u %s",
388                   fb ? fb->id : -1, sw, sh, in->alpha_only ? "alpha" : "rgba");
389              if (!fb) goto alloc_fail;
390              fb->transient = EINA_TRUE;
391              fb->cleanup = EINA_FALSE;
392           }
393 
394         out = cmd->output;
395         out->cleanup = EINA_FALSE;
396         if (!out->w && !out->h)
397           {
398              out->w = w;
399              out->h = h;
400           }
401      }
402 
403    EINA_LIST_FOREACH(ctx->buffers, li, fb)
404      {
405         Eina_Bool render = EINA_FALSE, draw = EINA_FALSE;
406 
407         if (fb->source)
408           {
409              fb->cleanup = EINA_FALSE;
410              continue;
411           }
412         if (fb->buffer || fb->cleanup)
413           continue;
414 
415         if (!fb->w && !fb->h)
416           {
417              ERR("Size of buffer %d should be known at this point. Is this a dangling buffer?", fb->id);
418              continue;
419           }
420 
421         // Skip input buffer, allocate it in input render phase
422         if (fb->id == EVAS_FILTER_BUFFER_INPUT_ID)
423           continue;
424 
425         render = fb->is_render || fb->transient;
426         draw |= (fb->id == EVAS_FILTER_BUFFER_OUTPUT_ID);
427 
428         fb->buffer = _ector_buffer_create(fb, render, draw);
429         XDBG("Allocated buffer #%d of size %ux%u %s: %p",
430              fb->id, fb->w, fb->h, fb->alpha_only ? "alpha" : "rgba", fb->buffer);
431         if (!fb->buffer) goto alloc_fail;
432      }
433 
434    EINA_LIST_FOREACH_SAFE(ctx->buffers, li, li2, fb)
435      {
436         if (fb->cleanup)
437           {
438              XDBG("Cleanup buffer #%d %dx%d %s", fb->id, fb->w, fb->h, fb->alpha_only ? "alpha" : "rgba");
439              ctx->buffers = eina_list_remove_list(ctx->buffers, li);
440              _buffer_free(fb);
441           }
442      }
443 
444    return EINA_TRUE;
445 
446 alloc_fail:
447    ERR("Buffer allocation failed! Context size: %dx%d", w, h);
448    return EINA_FALSE;
449 }
450 
451 int
evas_filter_buffer_empty_new(Evas_Filter_Context * ctx,int w,int h,Eina_Bool alpha_only)452 evas_filter_buffer_empty_new(Evas_Filter_Context *ctx, int w, int h, Eina_Bool alpha_only)
453 {
454    Evas_Filter_Buffer *fb;
455    Eina_List *li;
456 
457    EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, -1);
458 
459    EINA_LIST_FOREACH(ctx->buffers, li, fb)
460      {
461         if ((fb->alpha_only == alpha_only) &&
462             (fb->w == w) && (fb->h == h) && !fb->dirty && !fb->used)
463           {
464              fb->used = EINA_TRUE;
465              return fb->id;
466           }
467      }
468 
469    fb = _buffer_empty_new(ctx, w, h, alpha_only, EINA_FALSE);
470    if (!fb) return -1;
471 
472    fb->used = EINA_TRUE;
473    return fb->id;
474 }
475 
476 int
evas_filter_buffer_proxy_new(Evas_Filter_Context * ctx,Evas_Filter_Proxy_Binding * pb,int * w,int * h)477 evas_filter_buffer_proxy_new(Evas_Filter_Context *ctx, Evas_Filter_Proxy_Binding *pb,
478                              int *w, int *h)
479 {
480    Evas_Object_Protected_Data *source;
481    Evas_Filter_Buffer *fb;
482    Eina_List *li;
483 
484    EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, -1);
485    EINA_SAFETY_ON_NULL_RETURN_VAL(pb, -1);
486 
487    source = efl_data_scope_get(pb->eo_source, EFL_CANVAS_OBJECT_CLASS);
488    if (!source) return -1;
489 
490    // FIXME: This is not true if the source is an evas image
491    *w = source->cur->geometry.w;
492    *h = source->cur->geometry.h;
493 
494    EINA_LIST_FOREACH(ctx->buffers, li, fb)
495      {
496         if (pb->eo_source == fb->source)
497           {
498              if (fb->used) return -1;
499              if (fb->alpha_only) return -1;
500              if (!eina_streq(pb->name, fb->source_name)) return -1;
501              if ((*w != fb->w) || (*h != fb->h)) return -1;
502 
503              fb->used = EINA_TRUE;
504              return fb->id;
505           }
506      }
507 
508    fb = _buffer_empty_new(ctx, *w, *h, EINA_FALSE, EINA_FALSE);
509    if (!fb) return -1;
510 
511    fb->source = efl_ref(pb->eo_source);
512    fb->source_name = eina_stringshare_add(pb->name);
513 
514    fb->used = EINA_TRUE;
515    return fb->id;
516 }
517 
518 static Evas_Filter_Buffer *
_buffer_alloc_new(Evas_Filter_Context * ctx,int w,int h,Eina_Bool alpha_only,Eina_Bool render,Eina_Bool draw)519 _buffer_alloc_new(Evas_Filter_Context *ctx, int w, int h, Eina_Bool alpha_only,
520                   Eina_Bool render, Eina_Bool draw)
521 {
522    Evas_Filter_Buffer *fb;
523 
524    EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, NULL);
525    EINA_SAFETY_ON_FALSE_RETURN_VAL(w > 0 && h > 0, NULL);
526 
527    fb = calloc(1, sizeof(Evas_Filter_Buffer));
528    if (!fb) return NULL;
529 
530    fb->id = ++(ctx->last_buffer_id);
531    fb->ctx = ctx;
532    fb->w = w;
533    fb->h = h;
534    fb->alpha_only = alpha_only;
535    fb->is_render = render;
536    fb->buffer = _ector_buffer_create(fb, render, draw);
537    if (!fb->buffer)
538      {
539         ERR("Failed to create ector buffer!");
540         _free(fb);
541         return NULL;
542      }
543 
544    ctx->buffers = eina_list_append(ctx->buffers, fb);
545    return fb;
546 }
547 
548 static void
_buffer_free(Evas_Filter_Buffer * fb)549 _buffer_free(Evas_Filter_Buffer *fb)
550 {
551    _filter_buffer_backing_free(fb);
552    eina_stringshare_del(fb->source_name);
553    efl_unref(fb->source);
554    _free(fb);
555 }
556 
557 Evas_Filter_Buffer *
_filter_buffer_get(Evas_Filter_Context * ctx,int bufid)558 _filter_buffer_get(Evas_Filter_Context *ctx, int bufid)
559 {
560    Evas_Filter_Buffer *buffer;
561    Eina_List *l;
562 
563    EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, NULL);
564 
565    EINA_LIST_FOREACH(ctx->buffers, l, buffer)
566      if (buffer->id == bufid) return buffer;
567 
568    return NULL;
569 }
570 
571 void *
evas_filter_buffer_backing_get(Evas_Filter_Context * ctx,int bufid,Eina_Bool render)572 evas_filter_buffer_backing_get(Evas_Filter_Context *ctx, int bufid, Eina_Bool render)
573 {
574    Evas_Filter_Buffer *fb;
575 
576    fb = _filter_buffer_get(ctx, bufid);
577    if (!fb) return NULL;
578 
579    if (!fb->buffer)
580      evas_filter_buffer_backing_set(ctx, bufid, NULL);
581 
582    if (render)
583      return evas_ector_buffer_render_image_get(fb->buffer); // ref++
584    else
585      return evas_ector_buffer_drawable_image_get(fb->buffer); // ref++
586 }
587 
588 Eina_Bool
evas_filter_buffer_backing_set(Evas_Filter_Context * ctx,int bufid,void * engine_buffer)589 evas_filter_buffer_backing_set(Evas_Filter_Context *ctx, int bufid,
590                                void *engine_buffer)
591 {
592    Evas_Filter_Buffer *fb;
593    Eina_Bool ret = EINA_FALSE;
594    Eo *buffer = NULL;
595 
596    fb = _filter_buffer_get(ctx, bufid);
597    if (!fb) return EINA_FALSE;
598 
599    if (!engine_buffer)
600      {
601         buffer = _ector_buffer_create(fb, fb->is_render, EINA_FALSE);
602         XDBG("Allocated buffer #%d of size %ux%u %s: %p",
603              fb->id, fb->w, fb->h, fb->alpha_only ? "alpha" : "rgba", fb->buffer);
604         ret = buffer ? EINA_TRUE : EINA_FALSE;
605         goto end;
606      }
607 
608    if (fb->is_render) goto end;
609 
610    buffer = ENFN->ector_buffer_wrap(ENC, ctx->evas->evas, engine_buffer);
611    if (!buffer) return EINA_FALSE;
612 
613    ret = EINA_TRUE;
614 
615 end:
616    if (fb->buffer != buffer) _buffer_del((Eo *)fb->buffer);
617    fb->buffer = buffer;
618    return ret;
619 }
620 
621 static Evas_Filter_Command *
_command_new(Evas_Filter_Context * ctx,Evas_Filter_Mode mode,Evas_Filter_Buffer * input,Evas_Filter_Buffer * mask,Evas_Filter_Buffer * output)622 _command_new(Evas_Filter_Context *ctx, Evas_Filter_Mode mode,
623              Evas_Filter_Buffer *input, Evas_Filter_Buffer *mask,
624              Evas_Filter_Buffer *output)
625 {
626    Evas_Filter_Command *cmd;
627 
628    cmd = calloc(1, sizeof(Evas_Filter_Command));
629    if (!cmd) return NULL;
630 
631    cmd->id = ++(ctx->last_command_id);
632    cmd->ctx = ctx;
633    cmd->mode = mode;
634    cmd->input = input;
635    cmd->mask = mask;
636    cmd->output = output;
637    cmd->draw.R = 255;
638    cmd->draw.G = 255;
639    cmd->draw.B = 255;
640    cmd->draw.A = 255;
641    cmd->draw.rop = EFL_GFX_RENDER_OP_BLEND;
642    if (output)
643      {
644         cmd->draw.output_was_dirty = output->dirty;
645         output->is_render = EINA_TRUE;
646         output->dirty = EINA_TRUE;
647      }
648 
649    ctx->commands = eina_inlist_append(ctx->commands, EINA_INLIST_GET(cmd));
650    return cmd;
651 }
652 
653 static void
_command_del(Evas_Filter_Context * ctx,Evas_Filter_Command * cmd)654 _command_del(Evas_Filter_Context *ctx, Evas_Filter_Command *cmd)
655 {
656    if (!ctx || !cmd) return;
657    ctx->commands = eina_inlist_remove(ctx->commands, EINA_INLIST_GET(cmd));
658    switch (cmd->mode)
659      {
660       case EVAS_FILTER_MODE_CURVE: _free(cmd->curve.data); break;
661       default: break;
662      }
663    _free(cmd);
664 }
665 
666 Evas_Filter_Buffer *
evas_filter_temporary_buffer_get(Evas_Filter_Context * ctx,int w,int h,Eina_Bool alpha_only,Eina_Bool clean)667 evas_filter_temporary_buffer_get(Evas_Filter_Context *ctx, int w, int h,
668                                  Eina_Bool alpha_only, Eina_Bool clean)
669 {
670    Evas_Filter_Buffer *fb = NULL;
671    Eina_List *l;
672 
673    EINA_LIST_FOREACH(ctx->buffers, l, fb)
674      {
675         if (fb->transient && !fb->locked && (fb->alpha_only == alpha_only)
676             && (!clean || !fb->dirty))
677           {
678              if ((!w || (w == fb->w)) && (!h || (h == fb->h)))
679                {
680                   fb->locked = EINA_TRUE;
681                   return fb;
682                }
683           }
684      }
685 
686    if (ctx->running)
687      {
688         ERR("Can not create a new buffer while filter is running!");
689         return NULL;
690      }
691 
692    fb = _buffer_empty_new(ctx, w, h, alpha_only, EINA_TRUE);
693    fb->locked = EINA_TRUE;
694    fb->is_render = EINA_TRUE;
695    XDBG("Created temporary buffer %d %s", fb->id, alpha_only ? "alpha" : "rgba");
696 
697    return fb;
698 }
699 
700 static void
_filter_buffer_unlock_all(Evas_Filter_Context * ctx)701 _filter_buffer_unlock_all(Evas_Filter_Context *ctx)
702 {
703    Evas_Filter_Buffer *buf = NULL;
704    Eina_List *l;
705 
706    EINA_LIST_FOREACH(ctx->buffers, l, buf)
707      buf->locked = EINA_FALSE;
708 }
709 
710 Evas_Filter_Command *
evas_filter_command_fill_add(Evas_Filter_Context * ctx,void * draw_context,int bufid)711 evas_filter_command_fill_add(Evas_Filter_Context *ctx, void *draw_context,
712                              int bufid)
713 {
714    Evas_Filter_Command *cmd;
715    Evas_Filter_Buffer *buf = NULL;
716    int R, G, B, A, cx, cy, cw, ch;
717 
718    EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, NULL);
719    EINA_SAFETY_ON_NULL_RETURN_VAL(draw_context, NULL);
720 
721    buf = _filter_buffer_get(ctx, bufid);
722    if (!buf)
723      {
724         ERR("Buffer %d does not exist.", bufid);
725         return NULL;
726      }
727 
728    cmd = _command_new(ctx, EVAS_FILTER_MODE_FILL, buf, NULL, buf);
729    if (!cmd) return NULL;
730 
731    ENFN->context_color_get(ENC, draw_context, &R, &G, &B, &A);
732    DRAW_COLOR_SET(R, G, B, A);
733 
734    ENFN->context_clip_get(ENC, draw_context, &cx, &cy, &cw, &ch);
735    DRAW_CLIP_SET(cx, cy, cw, ch);
736 
737    XDBG("Add fill %d with color(%d,%d,%d,%d)", buf->id, R, G, B, A);
738 
739    if (!R && !G && !B && !A)
740      buf->dirty = EINA_FALSE;
741 
742    return cmd;
743 }
744 
745 static Evas_Filter_Command *
evas_filter_command_blur_add_gl(Evas_Filter_Context * ctx,Evas_Filter_Buffer * in,Evas_Filter_Buffer * out,Evas_Filter_Blur_Type type,int rx,int ry,int ox,int oy,int count,int R,int G,int B,int A,Eina_Bool alphaonly)746 evas_filter_command_blur_add_gl(Evas_Filter_Context *ctx,
747                                 Evas_Filter_Buffer *in, Evas_Filter_Buffer *out,
748                                 Evas_Filter_Blur_Type type,
749                                 int rx, int ry, int ox, int oy, int count,
750                                 int R, int G, int B, int A, Eina_Bool alphaonly)
751 {
752    Evas_Filter_Command *cmd = NULL;
753    Evas_Filter_Buffer *dx_in, *dx_out, *dy_in, *dy_out, *tmp = NULL;
754    int down_x = 1, down_y = 1;
755    int pad_x = 0, pad_y = 0;
756    double dx, dy;
757 
758    /* GL blur implementation:
759     *
760     * - Create intermediate buffer T1, T2
761     * - Downscale input to buffer T1
762     * - Apply X blur kernel from T1 to T2
763     * - Apply Y blur kernel from T2 back to output
764     *
765     * In order to avoid sampling artifacts when moving or resizing a filtered
766     * snapshot, we make sure that we always sample and scale based on the same
767     * original pixels positions:
768     * - Input pixels must be aligned to down_x,down_y boundaries
769     * - T1/T2 buffer size is up to 1px larger than [input / scale_x,y]
770     */
771 
772    dx = rx;
773    dy = ry;
774    dx_in = in;
775    dy_out = out;
776 
777 #if 1
778    if (type == EVAS_FILTER_BLUR_DEFAULT)
779      {
780         // Apply downscaling for large enough radii only.
781         down_x = 1 << evas_filter_smallest_pow2_larger_than(dx / 2) / 2;
782         down_y = 1 << evas_filter_smallest_pow2_larger_than(dy / 2) / 2;
783 
784         // Downscaling to max 4 times for perfect picture quality (with
785         // the standard scaling fragment shader and SHD_SAM22).
786         if (down_x > 4) down_x = 4;
787         if (down_y > 4) down_y = 4;
788 
789         if (down_x > 1 && down_y > 1)
790           {
791              int ww, hh;
792 
793              pad_x = ctx->x % down_x;
794              pad_y = ctx->y % down_y;
795 
796              ww = ceil((double) ctx->w / down_x) + 1;
797              hh = ceil((double) ctx->h / down_y) + 1;
798 
799              tmp = evas_filter_temporary_buffer_get(ctx, ww, hh, in->alpha_only, EINA_TRUE);
800              if (!tmp) goto fail;
801 
802              dx /= (double) down_x;
803              dy /= (double) down_y;
804 
805              XDBG("Add GL downscale %d (%dx%d) -> %d (%dx%d)", in->id, in->w, in->h, tmp->id, tmp->w, tmp->h);
806              cmd = _command_new(ctx, EVAS_FILTER_MODE_BLEND, in, NULL, tmp);
807              if (!cmd) goto fail;
808              cmd->draw.fillmode = EVAS_FILTER_FILL_MODE_STRETCH_XY;
809              cmd->draw.scale.down = EINA_TRUE;
810              cmd->draw.scale.pad_x = pad_x;
811              cmd->draw.scale.pad_y = pad_y;
812              cmd->draw.scale.factor_x = down_x;
813              cmd->draw.scale.factor_y = down_y;
814              cmd->draw.alphaonly = alphaonly;
815              dx_in = tmp;
816 
817              tmp = evas_filter_temporary_buffer_get(ctx, ww, hh, in->alpha_only, EINA_TRUE);
818              if (!tmp) goto fail;
819              dy_out = tmp;
820           }
821      }
822 #endif
823 
824    if (EINA_DBL_NONZERO(dx) && EINA_DBL_NONZERO(dy))
825      {
826         tmp = evas_filter_temporary_buffer_get(ctx, dx_in->w, dx_in->h, in->alpha_only, 1);
827         if (!tmp) goto fail;
828         dy_in = dx_out = tmp;
829      }
830    else
831      {
832         dx_out = out;
833         dy_in = in;
834      }
835 
836    if (EINA_DBL_NONZERO(dx))
837      {
838         XDBG("Add GL blur %d -> %d (%.2fx%.2f px)", dx_in->id, dx_out->id, dx, 0.0);
839         cmd = _command_new(ctx, EVAS_FILTER_MODE_BLUR, dx_in, NULL, dx_out);
840         if (!cmd) goto fail;
841         cmd->blur.type = type;
842         cmd->blur.dx = dx;
843         cmd->blur.count = count;
844         cmd->draw.alphaonly = alphaonly;
845      }
846 
847    if (EINA_DBL_NONZERO(dy))
848      {
849         XDBG("Add GL blur %d -> %d (%.2fx%.2f px)", dy_in->id, dy_out->id, 0.0, dy);
850         cmd = _command_new(ctx, EVAS_FILTER_MODE_BLUR, dy_in, NULL, dy_out);
851         if (!cmd) goto fail;
852         cmd->blur.type = type;
853         cmd->blur.dy = dy;
854         cmd->blur.count = count;
855         cmd->draw.alphaonly = alphaonly;
856      }
857 
858    EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, NULL);
859    if (cmd->output != out)
860      {
861         XDBG("Add GL upscale %d (%dx%d) -> %d (%dx%d)",
862              cmd->output->id, cmd->output->w, cmd->output->h, out->id, out->w, out->h);
863         cmd = _command_new(ctx, EVAS_FILTER_MODE_BLEND, cmd->output, NULL, out);
864         if (!cmd) goto fail;
865         cmd->draw.fillmode = EVAS_FILTER_FILL_MODE_STRETCH_XY;
866         cmd->draw.scale.down = EINA_FALSE;
867         cmd->draw.scale.pad_x = pad_x;
868         cmd->draw.scale.pad_y = pad_y;
869         cmd->draw.scale.factor_x = down_x;
870         cmd->draw.scale.factor_y = down_y;
871         cmd->draw.alphaonly = alphaonly;
872      }
873 
874    cmd->draw.ox = ox;
875    cmd->draw.oy = oy;
876    DRAW_COLOR_SET(R, G, B, A);
877    cmd->draw.rop = (in == out) ? EFL_GFX_RENDER_OP_COPY : EFL_GFX_RENDER_OP_BLEND;
878 
879    _filter_buffer_unlock_all(ctx);
880    return cmd;
881 
882 fail:
883    ERR("Failed to add blur");
884    _filter_buffer_unlock_all(ctx);
885    return NULL;
886 }
887 
888 static Eina_Bool
_blur_support_gl(Evas_Filter_Context * ctx,Evas_Filter_Buffer * in,Evas_Filter_Buffer * out)889 _blur_support_gl(Evas_Filter_Context *ctx, Evas_Filter_Buffer *in, Evas_Filter_Buffer *out)
890 {
891    Evas_Filter_Command cmd = {};
892 
893    cmd.input = in;
894    cmd.output = out;
895    cmd.mode = EVAS_FILTER_MODE_BLUR;
896    cmd.ctx = ctx;
897    cmd.blur.type = EVAS_FILTER_BLUR_GAUSSIAN;
898    cmd.blur.dx = 5;
899 
900    return cmd.ENFN->gfx_filter_supports(_evas_engine_context(cmd.ctx->evas), &cmd) == EVAS_FILTER_SUPPORT_GL;
901 }
902 
903 Evas_Filter_Command *
evas_filter_command_blur_add(Evas_Filter_Context * ctx,void * drawctx,int inbuf,int outbuf,Evas_Filter_Blur_Type type,int dx,int dy,int ox,int oy,int count,Eina_Bool alphaonly)904 evas_filter_command_blur_add(Evas_Filter_Context *ctx, void *drawctx,
905                              int inbuf, int outbuf, Evas_Filter_Blur_Type type,
906                              int dx, int dy, int ox, int oy, int count,
907                              Eina_Bool alphaonly)
908 {
909    Evas_Filter_Buffer *in = NULL, *out = NULL, *tmp = NULL, *in_dy = NULL;
910    Evas_Filter_Buffer *out_dy = NULL, *out_dx = NULL;
911    Evas_Filter_Buffer *copybuf = NULL, *blendbuf = NULL;
912    Evas_Filter_Command *cmd = NULL;
913    int R, G, B, A, render_op;
914    Eina_Bool override;
915    DATA32 color;
916 
917    EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, NULL);
918    EINA_SAFETY_ON_NULL_RETURN_VAL(drawctx, NULL);
919 
920    if (dx < 0) dx = 0;
921    if (dy < 0) dy = 0;
922    if (!dx && !dy)
923      {
924         XDBG("Changing 0px blur into simple blend");
925         return evas_filter_command_blend_add(ctx, drawctx, inbuf, outbuf, ox, oy, EVAS_FILTER_FILL_MODE_NONE, alphaonly);
926      }
927 
928    in = _filter_buffer_get(ctx, inbuf);
929    EINA_SAFETY_ON_FALSE_GOTO(in, fail);
930 
931    out = _filter_buffer_get(ctx, outbuf);
932    EINA_SAFETY_ON_FALSE_GOTO(out, fail);
933 
934    ENFN->context_color_get(ENC, drawctx, &R, &G, &B, &A);
935    color = ARGB_JOIN(A, R, G, B);
936    if (!color)
937      {
938         DBG("Blur with transparent color. Nothing to do.");
939         return _command_new(ctx, EVAS_FILTER_MODE_SKIP, NULL, NULL, NULL);
940      }
941 
942    if (_blur_support_gl(ctx, in, out))
943      return evas_filter_command_blur_add_gl(ctx, in, out, type, dx, dy, ox, oy,
944                                             count, R, G, B, A, alphaonly);
945 
946    // Note (SW engine):
947    // The basic blur operation overrides the pixels in the target buffer,
948    // only supports one direction (X or Y) and no offset. As a consequence
949    // most cases require intermediate work buffers.
950 
951    if (in == out) out->dirty = EINA_FALSE;
952 
953    render_op = ENFN->context_render_op_get(ENC, drawctx);
954    override = (render_op == EVAS_RENDER_COPY);
955 
956    switch (type)
957      {
958       case EVAS_FILTER_BLUR_GAUSSIAN:
959         count = 1;
960         break;
961 
962       case EVAS_FILTER_BLUR_BOX:
963         count = MIN(MAX(1, count), 6);
964         break;
965 
966       case EVAS_FILTER_BLUR_DEFAULT:
967         {
968            /* In DEFAULT mode we cheat, depending on the size of the kernel:
969             * For 1px to 2px, use true Gaussian blur.
970             * For 3px to 6px, use two Box blurs.
971             * For more than 6px, use three Box blurs.
972             * This will give both nicer and MUCH faster results than Gaussian.
973             *
974             * NOTE: This step should be avoided in GL.
975             */
976 
977            int tmp_out = outbuf;
978            int tmp_in = inbuf;
979            int tmp_ox = ox;
980            int tmp_oy = oy;
981 
982            // For 2D blur: create intermediate buffer
983            if (dx && dy)
984              {
985                 tmp = evas_filter_temporary_buffer_get(ctx, 0, 0, in->alpha_only, 1);
986                 if (!tmp) goto fail;
987                 tmp_in = tmp_out = tmp->id;
988                 tmp_ox = tmp_oy = 0;
989              }
990 
991            // X box blur
992            if (dx)
993              {
994                 if (dx <= 2)
995                   type = EVAS_FILTER_BLUR_GAUSSIAN;
996                 else
997                   type = EVAS_FILTER_BLUR_BOX;
998 
999                 if (dy) ENFN->context_color_set(ENC, drawctx, 255, 255, 255, 255);
1000                 cmd = evas_filter_command_blur_add(ctx, drawctx, inbuf, tmp_out,
1001                                                    type, dx, 0, tmp_ox, tmp_oy, 0,
1002                                                    alphaonly);
1003                 if (!cmd) goto fail;
1004                 cmd->blur.auto_count = EINA_TRUE;
1005                 if (dy) ENFN->context_color_set(ENC, drawctx, R, G, B, A);
1006              }
1007 
1008            // Y box blur
1009            if (dy)
1010              {
1011                 if (dy <= 2)
1012                   type = EVAS_FILTER_BLUR_GAUSSIAN;
1013                 else
1014                   type = EVAS_FILTER_BLUR_BOX;
1015 
1016                 if (dx && (inbuf == outbuf))
1017                   ENFN->context_render_op_set(ENC, drawctx, EVAS_RENDER_COPY);
1018                 cmd = evas_filter_command_blur_add(ctx, drawctx, tmp_in, outbuf,
1019                                                    type, 0, dy, ox, oy, 0,
1020                                                    alphaonly);
1021                 if (dx && (inbuf == outbuf))
1022                   ENFN->context_render_op_set(ENC, drawctx, render_op);
1023                 if (!cmd) goto fail;
1024                 cmd->blur.auto_count = EINA_TRUE;
1025              }
1026 
1027            return cmd;
1028         }
1029 
1030       default:
1031         CRI("Not implemented yet!");
1032         goto fail;
1033      }
1034 
1035    // For 2D blur: create intermediate buffer between X and Y passes
1036    if (dx && dy)
1037      {
1038         // If there's an offset: create intermediate buffer before offset blend
1039         if (ox || oy)
1040           {
1041              copybuf = evas_filter_temporary_buffer_get(ctx, 0, 0, in->alpha_only, 0);
1042              if (!copybuf) goto fail;
1043           }
1044 
1045         // Intermediate buffer between X and Y passes
1046         tmp = evas_filter_temporary_buffer_get(ctx, 0, 0, in->alpha_only, 0);
1047         if (!tmp) goto fail;
1048 
1049         if (in == out)
1050           {
1051              // IN = OUT and 2-D blur. IN -blur-> TMP -blur-> IN.
1052              out_dx = tmp;
1053              in_dy = tmp;
1054              out_dy = copybuf ? copybuf : in;
1055           }
1056         else
1057           {
1058              // IN != OUT and 2-D blur. IN -blur-> TMP -blur-> OUT.
1059              out_dx = tmp;
1060              in_dy = tmp;
1061              out_dy = copybuf ? copybuf : out;
1062           }
1063      }
1064    else if (dx)
1065      {
1066         // X blur only
1067         if (in == out)
1068           {
1069              // IN = OUT and 1-D blur. IN -blur-> TMP -copy-> IN.
1070              tmp = evas_filter_temporary_buffer_get(ctx, 0, 0, in->alpha_only, 0);
1071              if (!tmp) goto fail;
1072              copybuf = tmp;
1073              out_dx = tmp;
1074           }
1075         else if (ox || oy || (color != 0xFFFFFFFF))
1076           {
1077              // IN != OUT and 1-D blur. IN -blur-> TMP -blend-> OUT.
1078              tmp = evas_filter_temporary_buffer_get(ctx, 0, 0, in->alpha_only, 0);
1079              if (!tmp) goto fail;
1080              blendbuf = tmp;
1081              out_dx = tmp;
1082           }
1083         else if (out->dirty)
1084           {
1085              // IN != OUT and 1-D blur. IN -blur-> TMP -blend-> OUT.
1086              tmp = evas_filter_temporary_buffer_get(ctx, 0, 0, in->alpha_only, 0);
1087              if (!tmp) goto fail;
1088              blendbuf = tmp;
1089              out_dx = tmp;
1090           }
1091         else
1092           {
1093              // IN != OUT and 1-D blur. IN -blur-> OUT.
1094              out_dx = out;
1095           }
1096      }
1097    else
1098      {
1099         // Y blur only
1100         if (in == out)
1101           {
1102              // IN = OUT and 1-D blur. IN -blur-> TMP -copy-> IN.
1103              tmp = evas_filter_temporary_buffer_get(ctx, 0, 0, in->alpha_only, 0);
1104              if (!tmp) goto fail;
1105              copybuf = tmp;
1106              in_dy = in;
1107              out_dy = tmp;
1108           }
1109         else if (ox || oy || (color != 0xFFFFFFFF))
1110           {
1111              // IN != OUT and 1-D blur. IN -blur-> TMP -blend-> IN.
1112              tmp = evas_filter_temporary_buffer_get(ctx, 0, 0, in->alpha_only, 0);
1113              if (!tmp) goto fail;
1114              if (override)
1115                copybuf = tmp;
1116              else
1117                blendbuf = tmp;
1118              in_dy = in;
1119              out_dy = tmp;
1120           }
1121         else if (out->dirty && !override)
1122           {
1123              // IN != OUT and 1-D blur. IN -blur-> TMP -blend-> OUT.
1124              tmp = evas_filter_temporary_buffer_get(ctx, 0, 0, in->alpha_only, 0);
1125              if (!tmp) goto fail;
1126              blendbuf = tmp;
1127              in_dy = in;
1128              out_dy = tmp;
1129           }
1130         else
1131           {
1132              // IN != OUT and 1-D blur. IN -blur-> OUT.
1133              in_dy = in;
1134              out_dy = out;
1135           }
1136      }
1137 
1138    if (dx)
1139      {
1140         XDBG("Add horizontal blur %d -> %d (%dpx)", in->id, out_dx->id, dx);
1141         cmd = _command_new(ctx, EVAS_FILTER_MODE_BLUR, in, NULL, out_dx);
1142         if (!cmd) goto fail;
1143         cmd->blur.type = type;
1144         cmd->blur.dx = dx;
1145         cmd->blur.dy = 0;
1146         cmd->blur.count = count;
1147         if (!dy) DRAW_COLOR_SET(R, G, B, A);
1148      }
1149 
1150    if (dy)
1151      {
1152         XDBG("Add vertical blur %d -> %d (%dpx)", in_dy->id, out_dy->id, dy);
1153         cmd = _command_new(ctx, EVAS_FILTER_MODE_BLUR, in_dy, NULL, out_dy);
1154         if (!cmd) goto fail;
1155         cmd->blur.type = type;
1156         cmd->blur.dx = 0;
1157         cmd->blur.dy = dy;
1158         cmd->blur.count = count;
1159         DRAW_COLOR_SET(R, G, B, A);
1160      }
1161 
1162    if (blendbuf)
1163      {
1164         Evas_Filter_Command *blendcmd;
1165 
1166         XDBG("Add extra blend %d -> %d", blendbuf->id, out->id);
1167         blendcmd = evas_filter_command_blend_add(ctx, drawctx,
1168                                                  blendbuf->id, out->id, ox, oy,
1169                                                  EVAS_FILTER_FILL_MODE_NONE,
1170                                                  alphaonly);
1171         if (!blendcmd) goto fail;
1172         ox = oy = 0;
1173      }
1174    else if (copybuf)
1175      {
1176         Evas_Filter_Command *copycmd;
1177 
1178         XDBG("Add extra copy %d -> %d: offset: %d,%d", copybuf->id, out->id, ox, oy);
1179         ENFN->context_color_set(ENC, drawctx, 255, 255, 255, 255);
1180         ENFN->context_render_op_set(ENC, drawctx, EVAS_RENDER_COPY);
1181         copycmd = evas_filter_command_blend_add(ctx, drawctx,
1182                                                 copybuf->id, out->id, ox, oy,
1183                                                 EVAS_FILTER_FILL_MODE_NONE,
1184                                                 alphaonly);
1185         ENFN->context_color_set(ENC, drawctx, R, G, B, A);
1186         ENFN->context_render_op_set(ENC, drawctx, render_op);
1187         if (!copycmd) goto fail;
1188         ox = oy = 0;
1189      }
1190 
1191    out->dirty = EINA_TRUE;
1192    _filter_buffer_unlock_all(ctx);
1193    return cmd;
1194 
1195 fail:
1196    ERR("Failed to add blur");
1197    _filter_buffer_unlock_all(ctx);
1198    return NULL;
1199 }
1200 
1201 Evas_Filter_Command *
evas_filter_command_blend_add(Evas_Filter_Context * ctx,void * drawctx,int inbuf,int outbuf,int ox,int oy,Evas_Filter_Fill_Mode fillmode,Eina_Bool alphaonly)1202 evas_filter_command_blend_add(Evas_Filter_Context *ctx, void *drawctx,
1203                               int inbuf, int outbuf, int ox, int oy,
1204                               Evas_Filter_Fill_Mode fillmode,
1205                               Eina_Bool alphaonly)
1206 {
1207    Evas_Filter_Command *cmd;
1208    Evas_Filter_Buffer *in, *out;
1209    Eina_Bool copy;
1210    int R, G, B, A;
1211 
1212    EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, NULL);
1213 
1214    if (inbuf == outbuf)
1215      {
1216         XDBG("Skipping NOP blend operation %d --> %d", inbuf, outbuf);
1217         return NULL;
1218      }
1219 
1220    in = _filter_buffer_get(ctx, inbuf);
1221    if (!in)
1222      {
1223         ERR("Buffer %d does not exist [input].", inbuf);
1224         return NULL;
1225      }
1226 
1227    out = _filter_buffer_get(ctx, outbuf);
1228    if (!out)
1229      {
1230         ERR("Buffer %d does not exist [output].", outbuf);
1231         return NULL;
1232      }
1233 
1234    cmd = _command_new(ctx, EVAS_FILTER_MODE_BLEND, in, NULL, out);
1235    if (!cmd) return NULL;
1236 
1237    if (ENFN->context_render_op_get(ENC, drawctx) == EVAS_RENDER_COPY)
1238      copy = EINA_TRUE;
1239    else
1240      copy = EINA_FALSE;
1241 
1242    ENFN->context_color_get(ENC, drawctx, &R, &G, &B, &A);
1243    DRAW_COLOR_SET(R, G, B, A);
1244    DRAW_FILL_SET(fillmode);
1245    cmd->draw.ox = ox;
1246    cmd->draw.oy = oy;
1247    cmd->draw.rop = copy ? EFL_GFX_RENDER_OP_COPY : EFL_GFX_RENDER_OP_BLEND;
1248    cmd->draw.alphaonly = alphaonly;
1249    cmd->draw.clip_use =
1250          !!ENFN->context_clip_get(ENC, drawctx,
1251                                   &cmd->draw.clip.x, &cmd->draw.clip.y,
1252                                   &cmd->draw.clip.w, &cmd->draw.clip.h);
1253 
1254    XDBG("Add %s %d -> %d: offset %d,%d, color: %d,%d,%d,%d",
1255         copy ? "copy" : "blend", in->id, out->id, ox, oy, R, G, B, A);
1256    if (cmd->draw.clip_use)
1257      XDBG("Draw clip: %d,%d,%d,%d", cmd->draw.clip.x, cmd->draw.clip.y,
1258          cmd->draw.clip.w, cmd->draw.clip.h);
1259 
1260    out->dirty = EINA_TRUE;
1261    return cmd;
1262 }
1263 
1264 Evas_Filter_Command *
evas_filter_command_grow_add(Evas_Filter_Context * ctx,void * draw_context,int inbuf,int outbuf,int radius,Eina_Bool smooth,Eina_Bool alphaonly)1265 evas_filter_command_grow_add(Evas_Filter_Context *ctx, void *draw_context,
1266                              int inbuf, int outbuf, int radius, Eina_Bool smooth,
1267                              Eina_Bool alphaonly)
1268 {
1269    Evas_Filter_Command *blurcmd = NULL, *threshcmd = NULL, *blendcmd;
1270    Evas_Filter_Buffer *tmp, *in, *out;
1271    int diam = abs(radius) * 2 + 1;
1272    DATA8 curve[256] = {0};
1273    int tmin = 0, growbuf;
1274 
1275    EINA_SAFETY_ON_NULL_GOTO(ctx, fail);
1276 
1277    if (!radius)
1278      {
1279         XDBG("Changing 0px grow into simple blend");
1280         return evas_filter_command_blend_add(ctx, draw_context, inbuf, outbuf, 0, 0,
1281                                              EVAS_FILTER_FILL_MODE_NONE, alphaonly);
1282      }
1283 
1284    in = _filter_buffer_get(ctx, inbuf);
1285    EINA_SAFETY_ON_NULL_GOTO(in, fail);
1286 
1287    out = _filter_buffer_get(ctx, outbuf);
1288    EINA_SAFETY_ON_NULL_GOTO(out, fail);
1289 
1290    if ((inbuf != outbuf) && out->dirty)
1291      {
1292         tmp = evas_filter_temporary_buffer_get(ctx, in->w, in->h, in->alpha_only, 1);
1293         EINA_SAFETY_ON_NULL_GOTO(tmp, fail);
1294         growbuf = tmp->id;
1295      }
1296    else
1297      growbuf = outbuf;
1298 
1299    blurcmd = evas_filter_command_blur_add(ctx, draw_context, inbuf, growbuf,
1300                                           EVAS_FILTER_BLUR_DEFAULT,
1301                                           abs(radius), abs(radius), 0, 0, 0,
1302                                           alphaonly);
1303    EINA_SAFETY_ON_NULL_GOTO(blurcmd, fail);
1304 
1305    if (diam > 255) diam = 255;
1306    if (radius > 0)
1307      tmin = 255 / diam;
1308    else if (radius < 0)
1309      tmin = 256 - (255 / diam);
1310 
1311    if (!smooth)
1312      memset(curve + tmin, 255, 256 - tmin);
1313    else
1314      {
1315         int k, start, end, range;
1316 
1317         // This is pretty experimental.
1318         range = MAX(2, 12 - radius);
1319         start = ((tmin > range) ? (tmin - range) : 0);
1320         end = ((tmin < (256 - range)) ? (tmin + range) : 256);
1321 
1322         for (k = start; k < end; k++)
1323           curve[k] = ((k - start) * 255) / (end - start);
1324         if (end < 256)
1325           memset(curve + end, 255, 256 - end);
1326      }
1327 
1328    /* Use a temp buffer here. Becuase curve_add is using a temp buffer as well
1329       if inbuf and outbuf are same and doing blend_add. Then grow_add will do
1330       blend_add twice. Using a temp buffer will save a calling blend_add */
1331    tmp = evas_filter_temporary_buffer_get(ctx, in->w, in->h, in->alpha_only, 1);
1332    EINA_SAFETY_ON_NULL_GOTO(tmp, fail);
1333 
1334    threshcmd = evas_filter_command_curve_add(ctx, draw_context, growbuf, tmp->id,
1335                                              curve, EVAS_FILTER_CHANNEL_ALPHA);
1336    EINA_SAFETY_ON_NULL_GOTO(threshcmd, fail);
1337 
1338    blendcmd = evas_filter_command_blend_add(ctx, draw_context, tmp->id,
1339                                             outbuf, 0, 0,
1340                                             EVAS_FILTER_FILL_MODE_NONE,
1341                                             alphaonly);
1342    EINA_SAFETY_ON_NULL_GOTO(blendcmd, fail);
1343 
1344    return blurcmd;
1345 
1346 fail:
1347    ERR("Failed to add grow");
1348    if (threshcmd) _command_del(ctx, threshcmd);
1349    if (blurcmd) _command_del(ctx, blurcmd);
1350    return NULL;
1351 }
1352 
1353 Evas_Filter_Command *
evas_filter_command_curve_add(Evas_Filter_Context * ctx,void * draw_context EINA_UNUSED,int inbuf,int outbuf,DATA8 * curve,Evas_Filter_Channel channel)1354 evas_filter_command_curve_add(Evas_Filter_Context *ctx,
1355                               void *draw_context EINA_UNUSED,
1356                               int inbuf, int outbuf, DATA8 *curve,
1357                               Evas_Filter_Channel channel)
1358 {
1359    Evas_Filter_Command *cmd, *blendcmd;
1360    Evas_Filter_Buffer *in, *out, *tmp = NULL, *curve_out;
1361    DATA8 *copy;
1362 
1363    EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, NULL);
1364    EINA_SAFETY_ON_NULL_RETURN_VAL(curve, NULL);
1365 
1366    in = _filter_buffer_get(ctx, inbuf);
1367    out = _filter_buffer_get(ctx, outbuf);
1368    EINA_SAFETY_ON_NULL_RETURN_VAL(in, NULL);
1369    EINA_SAFETY_ON_NULL_RETURN_VAL(out, NULL);
1370 
1371    if (in->alpha_only != out->alpha_only)
1372      WRN("Incompatible formats for color curves, implicit conversion will be "
1373          "slow and may not produce the desired output.");
1374 
1375    if (in == out)
1376      {
1377         tmp = evas_filter_temporary_buffer_get(ctx, in->w, in->h, in->alpha_only, 1);
1378         if (!tmp) return NULL;
1379         curve_out = tmp;
1380      }
1381    else curve_out = out;
1382 
1383    XDBG("Add curve %d -> %d", in->id, curve_out->id);
1384 
1385    copy = malloc(256 * sizeof(DATA8));
1386    if (!copy) return NULL;
1387 
1388    cmd = _command_new(ctx, EVAS_FILTER_MODE_CURVE, in, NULL, curve_out);
1389    if (!cmd)
1390      {
1391         _free(copy);
1392         return NULL;
1393      }
1394 
1395    memcpy(copy, curve, 256 * sizeof(DATA8));
1396    cmd->curve.data = copy;
1397    if (cmd->input->alpha_only)
1398      cmd->curve.channel = EVAS_FILTER_CHANNEL_ALPHA;
1399    else
1400      cmd->curve.channel = channel;
1401 
1402    if (tmp)
1403      {
1404         blendcmd = evas_filter_command_blend_add(ctx, draw_context, curve_out->id,
1405                                                 out->id, 0, 0,
1406                                                 EVAS_FILTER_FILL_MODE_NONE,
1407                                                 out->alpha_only);
1408         if (!blendcmd)
1409           {
1410              _command_del(ctx, cmd);
1411              return NULL;
1412           }
1413      }
1414 
1415    return cmd;
1416 }
1417 
1418 Evas_Filter_Command *
evas_filter_command_displacement_map_add(Evas_Filter_Context * ctx,void * draw_context EINA_UNUSED,int inbuf,int outbuf,int dispbuf,Evas_Filter_Displacement_Flags flags,int intensity,Evas_Filter_Fill_Mode fillmode)1419 evas_filter_command_displacement_map_add(Evas_Filter_Context *ctx,
1420                                          void *draw_context EINA_UNUSED,
1421                                          int inbuf, int outbuf, int dispbuf,
1422                                          Evas_Filter_Displacement_Flags flags,
1423                                          int intensity,
1424                                          Evas_Filter_Fill_Mode fillmode)
1425 {
1426    Evas_Filter_Buffer *in, *out, *map, *tmp = NULL, *disp_out;
1427    Evas_Filter_Command *cmd = NULL;
1428    Eina_Bool alphaonly = EINA_FALSE;
1429 
1430    EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, NULL);
1431    EINA_SAFETY_ON_FALSE_RETURN_VAL(intensity >= 0, NULL);
1432 
1433    in = _filter_buffer_get(ctx, inbuf);
1434    out = _filter_buffer_get(ctx, outbuf);
1435    map = _filter_buffer_get(ctx, dispbuf);
1436    EINA_SAFETY_ON_NULL_RETURN_VAL(in, NULL);
1437    EINA_SAFETY_ON_NULL_RETURN_VAL(out, NULL);
1438    EINA_SAFETY_ON_NULL_RETURN_VAL(map, NULL);
1439 
1440    if (in->alpha_only != out->alpha_only)
1441      DBG("Different color formats, implicit conversion may be slow");
1442 
1443    if (map->alpha_only)
1444      {
1445         WRN("Displacement map is not an RGBA buffer, X and Y axes will be "
1446             "displaced together.");
1447      }
1448 
1449    if (in == out)
1450      {
1451         tmp = evas_filter_temporary_buffer_get(ctx, in->w, in->h, in->alpha_only, 1);
1452         if (!tmp) return NULL;
1453         disp_out = tmp;
1454      }
1455    else disp_out = out;
1456 
1457    cmd = _command_new(ctx, EVAS_FILTER_MODE_DISPLACE, in, map, disp_out);
1458    if (!cmd) goto fail;
1459 
1460    DRAW_FILL_SET(fillmode);
1461    cmd->displacement.flags = flags & EVAS_FILTER_DISPLACE_BITMASK;
1462    cmd->displacement.intensity = intensity;
1463    cmd->draw.rop = _evas_to_gfx_render_op(ENFN->context_render_op_get(ENC, draw_context));
1464 
1465    if (tmp)
1466      {
1467         Evas_Filter_Command *fillcmd;
1468 
1469         fillcmd = evas_filter_command_blend_add(ctx, draw_context, disp_out->id,
1470                                                 out->id, 0, 0,
1471                                                 EVAS_FILTER_FILL_MODE_NONE,
1472                                                 alphaonly);
1473         if (!fillcmd) goto fail;
1474      }
1475 
1476    _filter_buffer_unlock_all(ctx);
1477    return cmd;
1478 
1479 fail:
1480    _filter_buffer_unlock_all(ctx);
1481    _command_del(ctx, cmd);
1482    return NULL;
1483 }
1484 
1485 Evas_Filter_Command *
evas_filter_command_mask_add(Evas_Filter_Context * ctx,void * draw_context,int inbuf,int maskbuf,int outbuf,Evas_Filter_Fill_Mode fillmode)1486 evas_filter_command_mask_add(Evas_Filter_Context *ctx, void *draw_context,
1487                              int inbuf, int maskbuf, int outbuf,
1488                              Evas_Filter_Fill_Mode fillmode)
1489 {
1490    Evas_Filter_Command *cmd;
1491    Evas_Filter_Buffer *in, *out, *mask;
1492    Efl_Gfx_Render_Op render_op;
1493    int R, G, B, A;
1494 
1495    EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, NULL);
1496 
1497    render_op = _evas_to_gfx_render_op(ENFN->context_render_op_get(ENC, draw_context));
1498    ENFN->context_color_get(ENC, draw_context, &R, &G, &B, &A);
1499 
1500    in = _filter_buffer_get(ctx, inbuf);
1501    out = _filter_buffer_get(ctx, outbuf);
1502    mask = _filter_buffer_get(ctx, maskbuf);
1503    EINA_SAFETY_ON_NULL_RETURN_VAL(in, NULL);
1504    EINA_SAFETY_ON_NULL_RETURN_VAL(out, NULL);
1505    EINA_SAFETY_ON_NULL_RETURN_VAL(mask, NULL);
1506 
1507    cmd = _command_new(ctx, EVAS_FILTER_MODE_MASK, in, mask, out);
1508    if (!cmd) return NULL;
1509 
1510    cmd->draw.rop = render_op;
1511    DRAW_COLOR_SET(R, G, B, A);
1512    DRAW_FILL_SET(fillmode);
1513 
1514    return cmd;
1515 }
1516 
1517 Evas_Filter_Command *
evas_filter_command_bump_map_add(Evas_Filter_Context * ctx,void * draw_context EINA_UNUSED,int inbuf,int bumpbuf,int outbuf,float xyangle,float zangle,float elevation,float sf,DATA32 black,DATA32 color,DATA32 white,Evas_Filter_Bump_Flags flags,Evas_Filter_Fill_Mode fillmode)1518 evas_filter_command_bump_map_add(Evas_Filter_Context *ctx,
1519                                  void *draw_context EINA_UNUSED,
1520                                  int inbuf, int bumpbuf, int outbuf,
1521                                  float xyangle, float zangle, float elevation,
1522                                  float sf,
1523                                  DATA32 black, DATA32 color, DATA32 white,
1524                                  Evas_Filter_Bump_Flags flags,
1525                                  Evas_Filter_Fill_Mode fillmode)
1526 {
1527    Evas_Filter_Command *cmd;
1528    Evas_Filter_Buffer *in, *out, *map;
1529 
1530    EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, NULL);
1531 
1532    in = _filter_buffer_get(ctx, inbuf);
1533    out = _filter_buffer_get(ctx, outbuf);
1534    map = _filter_buffer_get(ctx, bumpbuf);
1535    EINA_SAFETY_ON_NULL_RETURN_VAL(in, NULL);
1536    EINA_SAFETY_ON_NULL_RETURN_VAL(out, NULL);
1537    EINA_SAFETY_ON_NULL_RETURN_VAL(map, NULL);
1538 
1539    if (!map->alpha_only)
1540      DBG("Bump map is not an Alpha buffer, implicit conversion may be slow");
1541 
1542    // FIXME: Boo!
1543    if (!in->alpha_only)
1544      WRN("RGBA bump map support is not implemented! This will trigger conversion.");
1545 
1546    // FIXME: Must ensure in != out
1547    EINA_SAFETY_ON_FALSE_RETURN_VAL(in != out, NULL);
1548    EINA_SAFETY_ON_FALSE_RETURN_VAL(map != out, NULL);
1549 
1550    cmd = _command_new(ctx, EVAS_FILTER_MODE_BUMP, in, map, out);
1551    if (!cmd) return NULL;
1552 
1553    DRAW_FILL_SET(fillmode);
1554    cmd->bump.xyangle = xyangle;
1555    cmd->bump.zangle = zangle;
1556    cmd->bump.specular_factor = sf;
1557    cmd->bump.dark = black;
1558    cmd->bump.color = color;
1559    cmd->bump.white = white;
1560    cmd->bump.elevation = elevation;
1561    cmd->bump.compensate = !!(flags & EVAS_FILTER_BUMP_COMPENSATE);
1562 
1563    return cmd;
1564 }
1565 
1566 Evas_Filter_Command *
evas_filter_command_transform_add(Evas_Filter_Context * ctx,void * draw_context EINA_UNUSED,int inbuf,int outbuf,Evas_Filter_Transform_Flags flags,int ox,int oy)1567 evas_filter_command_transform_add(Evas_Filter_Context *ctx,
1568                                   void *draw_context EINA_UNUSED,
1569                                   int inbuf, int outbuf,
1570                                   Evas_Filter_Transform_Flags flags,
1571                                   int ox, int oy)
1572 {
1573    Evas_Filter_Command *cmd;
1574    Evas_Filter_Buffer *in, *out;
1575 
1576    EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, NULL);
1577 
1578    in = _filter_buffer_get(ctx, inbuf);
1579    out = _filter_buffer_get(ctx, outbuf);
1580    EINA_SAFETY_ON_NULL_RETURN_VAL(in, NULL);
1581    EINA_SAFETY_ON_NULL_RETURN_VAL(out, NULL);
1582 
1583    cmd = _command_new(ctx, EVAS_FILTER_MODE_TRANSFORM, in, NULL, out);
1584    if (!cmd) return NULL;
1585 
1586    DRAW_COLOR_SET(255, 255, 255, 255);
1587    cmd->transform.flags = flags;
1588    cmd->draw.ox = ox;
1589    cmd->draw.oy = oy;
1590 
1591    if (in->alpha_only == out->alpha_only)
1592      {
1593         DBG("Incompatible buffer formats, will trigger implicit conversion.");
1594         cmd->draw.rop = EFL_GFX_RENDER_OP_COPY;
1595      }
1596    else
1597      cmd->draw.rop = EFL_GFX_RENDER_OP_BLEND;
1598 
1599    return cmd;
1600 }
1601 
1602 Evas_Filter_Command *
evas_filter_command_grayscale_add(Evas_Filter_Context * ctx,void * draw_context EINA_UNUSED,int inbuf,int outbuf)1603 evas_filter_command_grayscale_add(Evas_Filter_Context *ctx,
1604                                   void *draw_context EINA_UNUSED,
1605                                   int inbuf, int outbuf)
1606 {
1607    Evas_Filter_Command *cmd;
1608    Evas_Filter_Buffer *in, *out;
1609 
1610    EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, NULL);
1611 
1612    in = _filter_buffer_get(ctx, inbuf);
1613    EINA_SAFETY_ON_NULL_RETURN_VAL(in, NULL);
1614 
1615    out = _filter_buffer_get(ctx, outbuf);
1616    EINA_SAFETY_ON_NULL_RETURN_VAL(out, NULL);
1617 
1618    cmd = _command_new(ctx, EVAS_FILTER_MODE_GRAYSCALE, in, NULL, out);
1619    EINA_SAFETY_ON_NULL_RETURN_VAL(out, NULL);
1620 
1621    return cmd;
1622 }
1623 
1624 Evas_Filter_Command *
evas_filter_command_inverse_color_add(Evas_Filter_Context * ctx,void * draw_context EINA_UNUSED,int inbuf,int outbuf)1625 evas_filter_command_inverse_color_add(Evas_Filter_Context *ctx,
1626                                      void *draw_context EINA_UNUSED,
1627                                      int inbuf, int outbuf)
1628 {
1629    Evas_Filter_Command *cmd;
1630    Evas_Filter_Buffer *in, *out;
1631 
1632    EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, NULL);
1633 
1634    in = _filter_buffer_get(ctx, inbuf);
1635    EINA_SAFETY_ON_NULL_RETURN_VAL(in, NULL);
1636 
1637    out = _filter_buffer_get(ctx, outbuf);
1638    EINA_SAFETY_ON_NULL_RETURN_VAL(out, NULL);
1639 
1640    cmd = _command_new(ctx, EVAS_FILTER_MODE_INVERSE_COLOR, in, NULL, out);
1641    EINA_SAFETY_ON_NULL_RETURN_VAL(out, NULL);
1642 
1643    return cmd;
1644 }
1645 
1646 void
evas_filter_context_obscured_region_set(Evas_Filter_Context * ctx,Eina_Rectangle rect)1647 evas_filter_context_obscured_region_set(Evas_Filter_Context *ctx, Eina_Rectangle rect)
1648 {
1649    ctx->obscured.real = rect;
1650 }
1651 
1652 /* Final target */
1653 Eina_Bool
evas_filter_target_set(Evas_Filter_Context * ctx,void * draw_context,void * surface,int x,int y,const RGBA_Map * map)1654 evas_filter_target_set(Evas_Filter_Context *ctx, void *draw_context,
1655                        void *surface, int x, int y, const RGBA_Map *map)
1656 {
1657    void *mask = NULL;
1658 
1659    EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, EINA_FALSE);
1660 
1661    ctx->target.surface = ENFN->image_ref(ENC, surface);
1662    ctx->target.x = x;
1663    ctx->target.y = y;
1664    ctx->target.clip_use = ENFN->context_clip_get
1665          (ENC, draw_context, &ctx->target.cx, &ctx->target.cy,
1666           &ctx->target.cw, &ctx->target.ch);
1667    ctx->target.color_use = ENFN->context_multiplier_get
1668          (ENC, draw_context, &ctx->target.r, &ctx->target.g,
1669           &ctx->target.b, &ctx->target.a);
1670    if (ctx->target.r == 255 && ctx->target.g == 255 &&
1671        ctx->target.b == 255 && ctx->target.a == 255)
1672      ctx->target.color_use = EINA_FALSE;
1673    ctx->target.rop = ENFN->context_render_op_get(ENC, draw_context);
1674 
1675    _free(ctx->target.map);
1676    if (!map) ctx->target.map = NULL;
1677    else
1678      {
1679         size_t len = sizeof(RGBA_Map) + sizeof(RGBA_Map_Point) * (map->count - 1);
1680         ctx->target.map = malloc(len);
1681         memcpy(ctx->target.map, map, len);
1682      }
1683 
1684    ENFN->context_clip_image_get
1685       (ENC, draw_context, &mask, &ctx->target.mask_x, &ctx->target.mask_y);
1686    if (ctx->target.mask)
1687      ctx->evas->engine.func->image_free(_evas_engine_context(ctx->evas), ctx->target.mask);
1688    ctx->target.mask = mask; // FIXME: why no ref???
1689 
1690    return EINA_TRUE;
1691 }
1692 
1693 static Eina_Bool
_filter_target_render(void * engine,void * output,Evas_Filter_Context * ctx)1694 _filter_target_render(void *engine, void *output, Evas_Filter_Context *ctx)
1695 {
1696    Evas_Filter_Buffer *src;
1697    void *drawctx, *image = NULL, *surface;
1698 
1699    EINA_SAFETY_ON_NULL_RETURN_VAL(ctx->target.surface, EINA_FALSE);
1700 
1701    drawctx = ENFN->context_new(engine);
1702    surface = ctx->target.surface;
1703 
1704    src = _filter_buffer_get(ctx, EVAS_FILTER_BUFFER_OUTPUT_ID);
1705    EINA_SAFETY_ON_NULL_RETURN_VAL(src, EINA_FALSE);
1706 
1707    image = evas_ector_buffer_drawable_image_get(src->buffer);
1708    EINA_SAFETY_ON_NULL_GOTO(image, fail);
1709 
1710    // FIXME: Use ector buffer RENDERER here
1711 
1712    if (ctx->target.clip_use)
1713      {
1714         ENFN->context_clip_set(engine, drawctx, ctx->target.cx, ctx->target.cy,
1715                                ctx->target.cw, ctx->target.ch);
1716      }
1717 
1718    if (ctx->target.color_use)
1719      {
1720         ENFN->context_multiplier_set(engine, drawctx,
1721                                      ctx->target.r, ctx->target.g,
1722                                      ctx->target.b, ctx->target.a);
1723      }
1724 
1725    if (ctx->target.mask)
1726      {
1727         ENFN->context_clip_image_set(engine, drawctx, ctx->target.mask,
1728                                      ctx->target.mask_x, ctx->target.mask_y,
1729                                      ctx->evas, EINA_FALSE);
1730      }
1731 
1732    ENFN->context_render_op_set(engine, drawctx, ctx->target.rop);
1733    if (ctx->target.map)
1734      {
1735         ENFN->image_map_draw(engine, output, drawctx, surface, image,
1736                              ctx->target.map, EINA_TRUE, 0, EINA_FALSE);
1737      }
1738    else
1739      {
1740         ENFN->image_draw(engine, output, drawctx, surface, image,
1741                          0, 0, src->w, src->h,
1742                          ctx->target.x, ctx->target.y, src->w, src->h,
1743                          EINA_TRUE, EINA_FALSE);
1744      }
1745 
1746    ENFN->context_free(engine, drawctx);
1747    evas_ector_buffer_engine_image_release(src->buffer, image);
1748 
1749    ENFN->image_free(engine, surface);
1750    ctx->target.surface = NULL;
1751 
1752    return EINA_TRUE;
1753 
1754 fail:
1755    ENFN->image_free(engine, surface);
1756    ctx->target.surface = NULL;
1757 
1758    ERR("Failed to render filter to target canvas!");
1759    return EINA_FALSE;
1760 }
1761 
1762 
1763 /* Font drawing stuff */
1764 Eina_Bool
evas_filter_font_draw(Evas_Filter_Context * ctx,void * engine,void * output,void * draw_context,int bufid,Evas_Font_Set * font,int x,int y,Evas_Text_Props * text_props,Eina_Bool do_async)1765 evas_filter_font_draw(Evas_Filter_Context *ctx,
1766                       void *engine, void *output, void *draw_context, int bufid,
1767                       Evas_Font_Set *font, int x, int y,
1768                       Evas_Text_Props *text_props, Eina_Bool do_async)
1769 {
1770    Eina_Bool async_unref;
1771    Evas_Filter_Buffer *fb;
1772    void *surface = NULL;
1773 
1774    fb = _filter_buffer_get(ctx, bufid);
1775    EINA_SAFETY_ON_NULL_RETURN_VAL(fb, EINA_FALSE);
1776 
1777    surface = evas_filter_buffer_backing_get(ctx, bufid, EINA_TRUE);
1778    EINA_SAFETY_ON_NULL_RETURN_VAL(surface, EINA_FALSE);
1779 
1780    // Copied from evas_font_draw_async_check
1781    async_unref = ENFN->font_draw(engine, output, draw_context, surface,
1782                                  font, x, y, fb->w, fb->h, fb->w, fb->h,
1783                                  text_props, do_async);
1784    if (do_async && async_unref)
1785      {
1786         evas_common_font_glyphs_ref(text_props->glyphs);
1787         evas_unref_queue_glyph_put(ctx->evas, text_props->glyphs);
1788      }
1789 
1790    evas_ector_buffer_engine_image_release(fb->buffer, surface);
1791    return EINA_TRUE;
1792 }
1793 
1794 /* Clip full input rect (0, 0, sw, sh) to target (dx, dy, dw, dh)
1795  * and get source's clipped sx, sy as well as destination x, y, cols and rows */
1796 void
_clip_to_target(int * sx,int * sy,int sw,int sh,int ox,int oy,int dw,int dh,int * dx,int * dy,int * rows,int * cols)1797 _clip_to_target(int *sx /* OUT */, int *sy /* OUT */, int sw, int sh,
1798                 int ox, int oy, int dw, int dh,
1799                 int *dx /* OUT */, int *dy /* OUT */,
1800                 int *rows /* OUT */, int *cols /* OUT */)
1801 {
1802    if (ox > 0)
1803      {
1804         (*sx) = 0;
1805         (*dx) = ox;
1806         (*cols) = sw;
1807         if (((*dx) + (*cols)) > (dw))
1808           (*cols) = dw - (*dx);
1809      }
1810    else if (ox < 0)
1811      {
1812         (*dx) = 0;
1813         (*sx) = (-ox);
1814         (*cols) = sw - (*sx);
1815         if ((*cols) > dw) (*cols) = dw;
1816      }
1817    else
1818      {
1819         (*sx) = 0;
1820         (*dx) = 0;
1821         (*cols) = sw;
1822         if ((*cols) > dw) (*cols) = dw;
1823      }
1824 
1825    if (oy > 0)
1826      {
1827         (*sy) = 0;
1828         (*dy) = oy;
1829         (*rows) = sh;
1830         if (((*dy) + (*rows)) > (dh))
1831           (*rows) = dh - (*dy);
1832      }
1833    else if (oy < 0)
1834      {
1835         (*dy) = 0;
1836         (*sy) = (-oy);
1837         (*rows) = sh - (*sy);
1838         if ((*rows) > dh) (*rows) = dh;
1839      }
1840    else
1841      {
1842         (*sy) = 0;
1843         (*dy) = 0;
1844         (*rows) = sh;
1845         if ((*rows) > dh) (*rows) = dh;
1846      }
1847    if ((*cols) < 0) *cols = 0;
1848    if ((*rows) < 0) *rows = 0;
1849 }
1850 
1851 #ifdef FILTERS_DEBUG
1852 static const char *
_filter_name_get(int mode)1853 _filter_name_get(int mode)
1854 {
1855 #define FNAME(a) case EVAS_FILTER_MODE_ ## a: return "EVAS_FILTER_MODE_" #a
1856    switch (mode)
1857      {
1858       FNAME(SKIP);
1859       FNAME(BLEND);
1860       FNAME(BLUR);
1861       FNAME(CURVE);
1862       FNAME(DISPLACE);
1863       FNAME(MASK);
1864       FNAME(BUMP);
1865       FNAME(FILL);
1866       default: return "INVALID";
1867      }
1868 #undef FNAME
1869 }
1870 #endif
1871 
1872 static Eina_Bool
_filter_command_run(Evas_Filter_Command * cmd)1873 _filter_command_run(Evas_Filter_Command *cmd)
1874 {
1875    Evas_Filter_Support support = EVAS_FILTER_SUPPORT_NONE;
1876 
1877    if (cmd->mode == EVAS_FILTER_MODE_SKIP)
1878      return EINA_TRUE;
1879 
1880    EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output, EINA_FALSE);
1881    EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input, EINA_FALSE);
1882 
1883 #ifdef FILTERS_DEBUG
1884    XDBG("Command %d (%s): %d [%d] --> %d",
1885        cmd->id, _filter_name_get(cmd->mode),
1886        cmd->input->id, cmd->mask ? cmd->mask->id : 0, cmd->output->id);
1887 #endif
1888 
1889    if (!cmd->input->w && !cmd->input->h
1890        && (cmd->mode != EVAS_FILTER_MODE_FILL))
1891      {
1892         XDBG("Skipping processing of empty input buffer (size 0x0)");
1893         return EINA_TRUE;
1894      }
1895 
1896    if ((cmd->output->w <= 0) || (cmd->output->h <= 0))
1897      {
1898         ERR("Output size invalid: %dx%d", cmd->output->w, cmd->output->h);
1899         return EINA_FALSE;
1900      }
1901 
1902    support = cmd->ENFN->gfx_filter_supports(CMD_ENC, cmd);
1903    if (support == EVAS_FILTER_SUPPORT_NONE)
1904      {
1905         ERR("No function to process this filter (mode %d)", cmd->mode);
1906         return EINA_FALSE;
1907      }
1908 
1909    return cmd->ENFN->gfx_filter_process(CMD_ENC, cmd);
1910 }
1911 
1912 static Eina_Bool
_filter_chain_run(void * engine,void * output,Evas_Filter_Context * ctx)1913 _filter_chain_run(void *engine, void *output, Evas_Filter_Context *ctx)
1914 {
1915    Evas_Filter_Command *cmd;
1916    Eina_Bool ok = EINA_FALSE;
1917 
1918    DEBUG_TIME_BEGIN();
1919 
1920    EINA_INLIST_FOREACH(ctx->commands, cmd)
1921      {
1922         ok = _filter_command_run(cmd);
1923         if (!ok)
1924           {
1925              ERR("Filter processing failed!");
1926              goto end;
1927           }
1928      }
1929 
1930    ok = _filter_target_render(engine, output, ctx);
1931 
1932 end:
1933    ctx->running = EINA_FALSE;
1934    DEBUG_TIME_END();
1935 
1936    ctx->post_run.cb(ctx, ctx->post_run.data, ok);
1937    return ok;
1938 }
1939 
1940 typedef struct _Filter_Thread_Data Filter_Thread_Data;
1941 struct _Filter_Thread_Data
1942 {
1943    void *engine;
1944    void *output;
1945    Evas_Filter_Context *ctx;
1946 };
1947 
1948 static void
_filter_thread_run_cb(void * data)1949 _filter_thread_run_cb(void *data)
1950 {
1951    Filter_Thread_Data *ftd = data;
1952 
1953    _filter_chain_run(ftd->engine, ftd->output, ftd->ctx);
1954    _free(ftd);
1955 }
1956 
1957 static void
_filter_obscured_region_calc(Evas_Filter_Context * ctx)1958 _filter_obscured_region_calc(Evas_Filter_Context *ctx)
1959 {
1960    Eina_Rectangle rect = ctx->obscured.real;
1961 
1962    // left
1963    if (rect.x > 0)
1964      {
1965         rect.x += ctx->pad.calculated.l;
1966         rect.w -= ctx->pad.calculated.l;
1967      }
1968    else
1969      {
1970         rect.w -= (-rect.x);
1971         rect.x = 0;
1972      }
1973    if (rect.w < 0) rect.w = 0;
1974 
1975    // right
1976    if ((rect.x + rect.w) <= ctx->w)
1977      rect.w -= ctx->pad.calculated.r;
1978    else
1979      rect.w = ctx->w - rect.x;
1980 
1981    // top
1982    if (rect.y > 0)
1983      {
1984         rect.y += ctx->pad.calculated.t;
1985         rect.h -= ctx->pad.calculated.t;
1986      }
1987    else
1988      {
1989         rect.h -= (-rect.y);
1990         rect.y = 0;
1991      }
1992    if (rect.h < 0) rect.h = 0;
1993 
1994    // bottom
1995    if ((rect.y + rect.h) <= ctx->h)
1996      rect.h -= ctx->pad.calculated.b;
1997    else
1998      rect.h = ctx->h - rect.y;
1999 
2000    if ((rect.w <= 0) || (rect.h <= 0))
2001      memset(&rect, 0, sizeof(rect));
2002 
2003    ctx->obscured.effective = rect;
2004 }
2005 
2006 Eina_Bool
evas_filter_context_run(void * engine,void * output,Evas_Filter_Context * ctx)2007 evas_filter_context_run(void *engine, void *output, Evas_Filter_Context *ctx)
2008 {
2009    evas_filter_context_ref(ctx);
2010    _filter_obscured_region_calc(ctx);
2011 
2012    ctx->run_count++;
2013    ctx->running = EINA_TRUE;
2014    if (ctx->async)
2015      {
2016         Filter_Thread_Data *ftd;
2017 
2018         ftd = calloc(1, sizeof(*ftd));
2019         ftd->engine = engine;
2020         ftd->output = output;
2021         ftd->ctx = ctx;
2022 
2023         evas_thread_queue_flush(_filter_thread_run_cb, ftd);
2024         return EINA_TRUE;
2025      }
2026 
2027    return _filter_chain_run(engine, output, ctx);
2028 }
2029 
2030 
2031 /* Logging */
2032 
2033 static int init_cnt = 0;
2034 int _evas_filter_log_dom = 0;
2035 
2036 void
evas_filter_init(void)2037 evas_filter_init(void)
2038 {
2039    if ((init_cnt++) > 0) return;
2040    _evas_filter_log_dom = eina_log_domain_register("evas_filter", EVAS_FILTER_LOG_COLOR);
2041    evas_filter_mixin_init();
2042 }
2043 
2044 void
evas_filter_shutdown(void)2045 evas_filter_shutdown(void)
2046 {
2047    if ((--init_cnt) > 0) return;
2048    evas_filter_parser_shutdown();
2049    evas_filter_mixin_shutdown();
2050    eina_log_domain_unregister(_evas_filter_log_dom);
2051    _evas_filter_log_dom = 0;
2052 }
2053