Evas filters: Add support for Evas_Object_Image

This adds filter support to Image objects as well.
The exact same filters can run on Text and on Images
(provided some colorspace limitations are respected).

This basically adds:
- Support for RGBA input buffer
- Eo entry points for Image filter support
- Implement basic filter support in Evas_Image
This commit is contained in:
Jean-Philippe Andre 2014-03-17 16:10:03 +09:00 committed by Jean-Philippe ANDRE
parent de4969ccb2
commit 59cf013db9
7 changed files with 394 additions and 10 deletions

View File

@ -1023,6 +1023,46 @@ class Evas_Image (Evas_Object)
Evas_3D_Scene *scene; /*@ 3D scene on an image object. */
}
}
filter_program {
set {
/*@ Set an Evas filter program on this Text Object.
If the program fails to compile (syntax error, invalid
buffer name, etc...), the standard text effects will be
applied instead (SHADOW, etc...). switch back to the
standard text effects.
@since 1.9
@note EXPERIMENTAL FEATURE. This is an unstable API,
please use only for testing purposes.
@see @ref evasfiltersref "Evas filters reference"
*/
}
values {
const char* program; /*@ The program code, as defined
by the @ref evasfiltersref "Evas filters script language".
Pass NULL to remove the former program and switch back
to the standard text effect
@since 1.9
@note EXPERIMENTAL FEATURE. This is an unstable API,
please use only for testing purposes.
@see @ref evasfiltersref "Evas filters reference"
*/
}
}
filter_source {
set {
/*@ Bind an object to use as a mask or texture with Evas Filters.
This will create automatically a new RGBA buffer containing
the source object's pixels (as it is rendered). */
}
values {
const char* name; /*@ Object name as used in the program code */
Eo* eobj; /*@ Eo object to use through proxy rendering */
}
}
}
methods {
preload_begin {

View File

@ -18,6 +18,7 @@
#include "../common/evas_convert_color.h"
#include "../common/evas_convert_colorspace.h"
#include "../common/evas_convert_yuv.h"
#include "evas_filter.h"
#include <Eo.h>
@ -91,6 +92,16 @@ struct _Evas_Object_Image_State
Evas_Colorspace cspace;
struct {
Eina_Stringshare *code;
Evas_Filter_Program *chain;
Eina_Hash *sources; // Evas_Filter_Proxy_Binding
int sources_count;
void *output;
Eina_Bool changed : 1;
Eina_Bool invalid : 1; // Code parse failed
} filter;
Eina_Bool smooth_scale : 1;
Eina_Bool has_alpha :1;
Eina_Bool opaque_valid : 1;
@ -233,6 +244,11 @@ static const Evas_Object_Image_State default_state = {
0, //frame
EVAS_TEXTURE_REPEAT,
EVAS_COLORSPACE_ARGB8888,
// filter
{ NULL, NULL, NULL, 0, NULL, EINA_FALSE, EINA_FALSE },
// flags
EINA_TRUE, EINA_FALSE, EINA_FALSE, EINA_FALSE, EINA_FALSE
};
@ -3103,6 +3119,14 @@ evas_object_image_render(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj, v
}
else
{
Evas_Filter_Context *filter = NULL;
void * const surface_save = surface;
void * const context_save = context;
const Eina_Bool do_async_save = do_async;
int l = 0, r = 0, t = 0, b = 0;
Eina_Bool clear = EINA_FALSE;
int W, H, offx, offy;
obj->layer->evas->engine.func->image_scale_hint_set(output,
pixels,
o->scale_hint);
@ -3118,6 +3142,69 @@ evas_object_image_render(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj, v
if (idh < 1) idh = 1;
if (idx > 0) idx -= idw;
if (idy > 0) idy -= idh;
offx = obj->cur->geometry.x + x;
offy = obj->cur->geometry.y + y;
/* Evas Filters on Image Object. Yet another experimental piece of code.
* Replace current context, offset and surface by filter-made targets.
*/
if (!o->cur->filter.invalid && o->cur->filter.code)
{
Evas_Filter_Program *pgm = o->cur->filter.chain;;
Eina_Bool ok;
evas_filter_program_padding_get(pgm, &l, &r, &t, &b);
W = obj->cur->geometry.w + l + r;
H = obj->cur->geometry.h + t + b;
if (!pgm)
{
pgm = evas_filter_program_new("Image", EINA_FALSE);
evas_filter_program_source_set_all(pgm, o->cur->filter.sources);
ok = evas_filter_program_parse(pgm, o->cur->filter.code);
if (!ok) goto state_write;
}
filter = evas_filter_context_new(obj->layer->evas, do_async);
ok = evas_filter_context_program_use(filter, pgm);
if (!ok) goto state_write;
evas_filter_context_proxy_render_all(filter, eo_obj, EINA_FALSE);
ok = evas_filter_context_buffers_allocate_all(filter, W, H);
if (!ok) goto state_write;
if (obj->layer->evas->engine.func->gl_surface_read_pixels)
{
evas_filter_context_proxy_render_all(filter, eo_obj, EINA_FALSE);
surface = obj->layer->evas->engine.func->image_map_surface_new(output, W, H, EINA_TRUE);
}
state_write:
EINA_COW_IMAGE_STATE_WRITE_BEGIN(o, state_write)
{
if (ok)
{
offx = l;
offy = t;
context = obj->layer->evas->engine.func->context_new(output);
clear = EINA_TRUE;
do_async = EINA_FALSE;
state_write->filter.chain = pgm;
}
else
{
ERR("Failed to apply this filter");
evas_filter_context_destroy(filter);
evas_filter_program_del(pgm);
state_write->filter.invalid = EINA_TRUE;
state_write->filter.chain = NULL;
filter = NULL;
}
}
EINA_COW_IMAGE_STATE_WRITE_END(o, state_write)
}
while ((int)idx < obj->cur->geometry.w)
{
Evas_Coord ydy;
@ -3133,6 +3220,16 @@ evas_object_image_render(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj, v
}
else
iw = ((int)(idx + idw)) - ix;
if (clear)
{
obj->layer->evas->engine.func->context_color_set(output, context, 0, 0, 0, 0);
obj->layer->evas->engine.func->context_render_op_set(output, context, EVAS_RENDER_COPY);
obj->layer->evas->engine.func->rectangle_draw(output, context, surface, 0, 0, W, H, EINA_FALSE);
obj->layer->evas->engine.func->context_color_set(output, context, 255, 255, 255, 255);
obj->layer->evas->engine.func->context_render_op_set(output, context, EVAS_RENDER_BLEND);
}
while ((int)idy < obj->cur->geometry.h)
{
int dobreak_h = 0;
@ -3156,8 +3253,8 @@ evas_object_image_render(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj, v
(obj, output, context, surface, pixels,
0, 0,
imagew, imageh,
obj->cur->geometry.x + ix + x,
obj->cur->geometry.y + iy + y,
offx + ix,
offy + iy,
iw, ih,
o->cur->smooth_scale,
do_async);
@ -3168,8 +3265,8 @@ evas_object_image_render(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj, v
int bl, br, bt, bb, bsl, bsr, bst, bsb;
int imw, imh, ox, oy;
ox = obj->cur->geometry.x + ix + x;
oy = obj->cur->geometry.y + iy + y;
ox = offx + ix;
oy = offy + iy;
imw = imagew;
imh = imageh;
bl = o->cur->border.l;
@ -3327,6 +3424,24 @@ evas_object_image_render(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj, v
idy = ydy;
if (dobreak_w) break;
}
/* Evas filters wrap-up */
if (filter)
{
//void *outbuf = evas_filter_buffer_backing_steal(filter, EVAS_FILTER_BUFFER_OUTPUT_ID);
//EINA_COW_IMAGE_STATE_WRITE_BEGIN(o, state_write)
//state_write->filter.output = outbuf;
//EINA_COW_IMAGE_STATE_WRITE_END(o, state_write)
evas_filter_image_draw(filter, context, EVAS_FILTER_BUFFER_INPUT_ID, surface, do_async_save);
obj->layer->evas->engine.func->context_free(output, context);
evas_filter_target_set(filter, context_save, surface_save, obj->cur->geometry.x + x, obj->cur->geometry.y + y);
evas_filter_context_autodestroy(filter);
evas_filter_run(filter);
obj->layer->evas->engine.func->image_map_surface_free(output, surface);
}
}
}
}
@ -4477,6 +4592,176 @@ _evas_object_image_video_overlay_do(Evas_Object *eo_obj)
o->delayed.video_hide = EINA_FALSE;
}
/* Evas Filters functions. Similar to Evas_Text. */
EOLIAN static void
_evas_image_filter_program_set(Eo *eo_obj, Evas_Image_Data *o, const char *arg)
{
Evas_Object_Protected_Data *obj;
Evas_Filter_Program *pgm = NULL;
if (!o) return;
if (o->cur->filter.code == arg) return;
if (o->cur->filter.code && arg && !strcmp(arg, o->cur->filter.code)) return;
// Parse filter program
evas_filter_program_del(o->cur->filter.chain);
if (arg)
{
pgm = evas_filter_program_new("Evas_Text: Filter Program", EINA_FALSE);
evas_filter_program_source_set_all(pgm, o->cur->filter.sources);
if (!evas_filter_program_parse(pgm, arg))
{
ERR("Parsing failed!");
evas_filter_program_del(pgm);
pgm = NULL;
}
}
EINA_COW_IMAGE_STATE_WRITE_BEGIN(o, state_write)
{
state_write->filter.chain = pgm;
state_write->filter.changed = EINA_TRUE;
state_write->filter.invalid = (pgm == NULL);
eina_stringshare_replace(&state_write->filter.code, arg);
}
EINA_COW_IMAGE_STATE_WRITE_END(o, state_write);
// Update object
obj = eo_data_scope_get(eo_obj, EVAS_OBJ_CLASS);
o->changed = EINA_TRUE;
o->written = EINA_FALSE;
evas_object_change(eo_obj, obj);
evas_object_clip_dirty(eo_obj, obj);
evas_object_coords_recalc(eo_obj, obj);
evas_object_inform_call_resize(eo_obj);
}
static void
_filter_source_hash_free_cb(void *data)
{
Evas_Filter_Proxy_Binding *pb = data;
Evas_Object_Protected_Data *proxy, *source;
Evas_Image_Data *o;
proxy = eo_data_scope_get(pb->eo_proxy, EVAS_OBJ_CLASS);
source = eo_data_scope_get(pb->eo_source, EVAS_OBJ_CLASS);
if (source)
{
EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, source->proxy,
Evas_Object_Proxy_Data, source_write)
source_write->proxies = eina_list_remove(source_write->proxies, pb->eo_proxy);
EINA_COW_WRITE_END(evas_object_proxy_cow, source->proxy, source_write)
}
o = eo_data_scope_get(pb->eo_proxy, MY_CLASS);
if (o && proxy)
{
EINA_COW_IMAGE_STATE_WRITE_BEGIN(o, state_write)
state_write->filter.sources_count--;
EINA_COW_IMAGE_STATE_WRITE_END(o, state_write)
if (!o->cur->filter.sources_count)
{
EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, proxy->proxy,
Evas_Object_Proxy_Data, proxy_write)
proxy_write->is_proxy = EINA_FALSE;
EINA_COW_WRITE_END(evas_object_proxy_cow, source->proxy, proxy_write)
}
}
eina_stringshare_del(pb->name);
free(pb);
}
EOLIAN static void
_evas_image_filter_source_set(Eo *eo_obj, Evas_Image_Data *o, const char *name,
Evas_Object *eo_source)
{
Evas_Object_Protected_Data *obj;
Evas_Filter_Program *pgm = o->cur->filter.chain;
Evas_Filter_Proxy_Binding *pb, *pb_old = NULL;
Evas_Object_Protected_Data *source = NULL;
Eina_Hash *sources = o->cur->filter.sources;
int sources_count = o->cur->filter.sources_count;
obj = eo_data_scope_get(eo_obj, EVAS_OBJ_CLASS);
if (eo_source) source = eo_data_scope_get(eo_source, EVAS_OBJ_CLASS);
if (!name)
{
if (!eo_source || !o->cur->filter.sources) return;
if (eina_hash_del_by_data(o->cur->filter.sources, eo_source))
goto update;
return;
}
if (!sources)
{
if (!source) return;
sources = eina_hash_string_small_new
(EINA_FREE_CB(_filter_source_hash_free_cb));
}
else
{
pb_old = eina_hash_find(sources, name);
if (pb_old)
{
if (pb_old->eo_source == eo_source) goto update;
eina_hash_del(sources, name, pb_old);
}
}
if (!source)
{
pb_old = eina_hash_find(sources, name);
if (!pb_old) return;
eina_hash_del_by_key(sources, name);
goto update;
}
pb = calloc(1, sizeof(*pb));
pb->eo_proxy = eo_obj;
pb->eo_source = eo_source;
pb->name = eina_stringshare_add(name);
EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, source->proxy,
Evas_Object_Proxy_Data, source_write)
if (!eina_list_data_find(source_write->proxies, eo_obj))
source_write->proxies = eina_list_append(source_write->proxies, eo_obj);
EINA_COW_WRITE_END(evas_object_proxy_cow, source->proxy, source_write)
EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, obj->proxy,
Evas_Object_Proxy_Data, proxy_write)
proxy_write->is_proxy = EINA_TRUE;
EINA_COW_WRITE_END(evas_object_proxy_cow, obj->proxy, proxy_write)
eina_hash_add(sources, pb->name, pb);
sources_count++;
evas_filter_program_source_set_all(pgm, sources);
update:
EINA_COW_IMAGE_STATE_WRITE_BEGIN(o, state_write)
{
state_write->filter.changed = EINA_TRUE;
state_write->filter.invalid = EINA_FALSE;
state_write->filter.sources = sources;
state_write->filter.sources_count = sources_count;
}
EINA_COW_IMAGE_STATE_WRITE_END(o, state_write);
// Update object
o->changed = 1;
evas_object_change(eo_obj, obj);
evas_object_clip_dirty(eo_obj, obj);
evas_object_coords_recalc(eo_obj, obj);
evas_object_inform_call_resize(eo_obj);
}
/* vim:set ts=8 sw=3 sts=3 expandtab cino=>5n-2f0^-2{2(0W1st0 :*/
#include "canvas/evas_image.eo.c"

View File

@ -1740,7 +1740,7 @@ evas_object_text_render(Evas_Object *eo_obj,
if (!o->cur.filter.chain)
{
Evas_Filter_Program *pgm;
pgm = evas_filter_program_new("Evas_Text");
pgm = evas_filter_program_new("Evas_Text", EINA_TRUE);
evas_filter_program_source_set_all(pgm, o->cur.filter.sources);
if (!evas_filter_program_parse(pgm, o->cur.filter.code))
{
@ -2315,7 +2315,7 @@ _evas_text_filter_program_set(Eo *eo_obj, Evas_Text_Data *o, const char *arg)
evas_filter_program_del(o->cur.filter.chain);
if (arg)
{
pgm = evas_filter_program_new("Evas_Text: Filter Program");
pgm = evas_filter_program_new("Evas_Text: Filter Program", EINA_TRUE);
evas_filter_program_source_set_all(pgm, o->cur.filter.sources);
if (!evas_filter_program_parse(pgm, arg))
{

View File

@ -1753,6 +1753,64 @@ evas_filter_font_draw(Evas_Filter_Context *ctx, void *draw_context, int bufid,
}
/* Image draw: glReadPixels or just use SW buffer */
Eina_Bool
evas_filter_image_draw(Evas_Filter_Context *ctx, void *draw_context, int bufid,
void *image, Eina_Bool do_async)
{
int w = 0, h = 0;
ENFN->image_size_get(ENDT, image, &w, &h);
if (!w || !h) return EINA_FALSE;
if (!ctx->gl_engine)
{
Eina_Bool async_unref;
int dw = 0, dh = 0;
// Copy the image into our input buffer. We could optimize by reusing the buffer.
void *surface = evas_filter_buffer_backing_get(ctx, bufid);
if (!surface) return EINA_FALSE;
ENFN->image_size_get(ENDT, image, &dw, &dh);
if (!dw || !dh) return EINA_FALSE;
if (w != dw || h != dh)
WRN("Target surface size differs from the image to draw");
async_unref = ENFN->image_draw(ENDT, draw_context, surface, image,
0, 0, w, h,
0, 0, dw, dh,
EINA_TRUE, do_async);
if (do_async && async_unref)
{
#ifdef EVAS_CSERVE2
if (evas_cserve2_use_get())
evas_cache2_image_ref((Image_Entry *)image);
else
#endif
evas_cache_image_ref((Image_Entry *)image);
evas_unref_queue_image_put(ctx->evas, image);
}
}
else
{
Evas_Filter_Buffer *fb;
fb = _filter_buffer_get(ctx, bufid);
_filter_buffer_backing_free(fb);
fb->glimage = image;
fb->allocated_gl = EINA_FALSE;
_filter_buffer_glimage_pixels_read(fb);
fb->glimage = NULL;
}
return EINA_TRUE;
}
/* Clip full input rect (0, 0, sw, sh) to target (dx, dy, dw, dh)
* and get source's clipped sx, sy as well as destination x, y, cols and rows */
void

View File

@ -2094,14 +2094,14 @@ evas_filter_program_padding_get(Evas_Filter_Program *pgm,
/** Create an empty filter program for style parsing */
EAPI Evas_Filter_Program *
evas_filter_program_new(const char *name)
evas_filter_program_new(const char *name, Eina_Bool input_alpha)
{
Evas_Filter_Program *pgm;
pgm = calloc(1, sizeof(Evas_Filter_Program));
if (!pgm) return NULL;
pgm->name = eina_stringshare_add(name);
_buffer_add(pgm, "input", EINA_TRUE, NULL);
_buffer_add(pgm, "input", input_alpha, NULL);
_buffer_add(pgm, "output", EINA_FALSE, NULL);
return pgm;

View File

@ -92,7 +92,7 @@ enum _Evas_Filter_Transform_Flags
};
/* Parser stuff (high level API) */
EAPI Evas_Filter_Program *evas_filter_program_new(const char *name);
EAPI Evas_Filter_Program *evas_filter_program_new(const char *name, Eina_Bool input_alpha);
EAPI Eina_Bool evas_filter_program_parse(Evas_Filter_Program *pgm, const char *str);
EAPI void evas_filter_program_del(Evas_Filter_Program *pgm);
Eina_Bool evas_filter_context_program_use(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm);
@ -116,6 +116,7 @@ Eina_Bool evas_filter_buffer_backing_release(Evas_Filter_Context
Eina_Bool evas_filter_run(Evas_Filter_Context *ctx);
Eina_Bool evas_filter_font_draw(Evas_Filter_Context *ctx, void *draw_context, int bufid, Evas_Font_Set *font, int x, int y, Evas_Text_Props *text_props, Eina_Bool do_async);
Eina_Bool evas_filter_image_draw(Evas_Filter_Context *ctx, void *draw_context, int bufid, void *image, Eina_Bool do_async);
Eina_Bool evas_filter_target_set(Evas_Filter_Context *ctx, void *draw_context, void *surface, int x, int y);
/**

View File

@ -71,7 +71,7 @@ START_TEST(evas_filter_parser)
Evas_Filter_Program *pgm;
#define CHECK_FILTER(_a, _v) do { \
pgm = evas_filter_program_new("evas_suite"); \
pgm = evas_filter_program_new("evas_suite", EINA_TRUE); \
fail_if(evas_filter_program_parse(pgm, _a) != _v); \
evas_filter_program_del(pgm); \
} while (0)