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