Evas filters: Implement advanced text effects

Add a new module in Evas to implement text effects.
The basic idea is to create some buffers (alpha/rgba),
draw some text into them, and apply various filters in a
sequential way.

A small script language allows you to set a filter to a
text object. This can then be passed through an Eo API
or using Edje directly (under the field description.text.filter).

Right now, the software engine should work fine, but GL rendering
suffers from a few limitations (no scaling and no proxy rendering,
which means a few effects might not work well).

More documentation will follow, and examples as well.

But, please bear in mind: this is a HIGHLY EXPERIMENTAL feature.
I'm including it to EFL 1.9 so that people can play with it,
but it should not be considered stable at this point in time.
This commit is contained in:
Jean-Philippe Andre 2014-02-07 18:12:21 +09:00
commit 407eaf029c
24 changed files with 7293 additions and 51 deletions

View File

@ -364,6 +364,20 @@ lib/evas/common/evas_op_sub/op_sub_pixel_mask_.c \
lib/evas/common/evas_op_sub/op_sub_pixel_mask_i386.c
### Evas filters
lib_evas_libevas_la_SOURCES += lib/evas/filters/evas_filter.c \
lib/evas/filters/evas_filter_blend.c \
lib/evas/filters/evas_filter_blur.c \
lib/evas/filters/evas_filter_bump.c \
lib/evas/filters/evas_filter_curve.c \
lib/evas/filters/evas_filter_displace.c \
lib/evas/filters/evas_filter_mask.c \
lib/evas/filters/evas_filter_parser.c \
lib/evas/filters/evas_filter_transform.c \
lib/evas/filters/evas_filter_utils.c
### Engines
if EVAS_STATIC_BUILD_SOFTWARE_GENERIC

View File

@ -314,6 +314,7 @@ static void st_collections_group_parts_part_description_text_align(void);
static void st_collections_group_parts_part_description_text_source(void);
static void st_collections_group_parts_part_description_text_text_source(void);
static void st_collections_group_parts_part_description_text_elipsis(void);
static void st_collections_group_parts_part_description_text_filter(void);
static void st_collections_group_parts_part_description_box_layout(void);
static void st_collections_group_parts_part_description_box_align(void);
static void st_collections_group_parts_part_description_box_padding(void);
@ -651,6 +652,7 @@ New_Statement_Handler statement_handlers[] =
{"collections.group.parts.part.description.text.fonts.font", st_fonts_font}, /* dup */
{"collections.group.parts.part.description.text.elipsis", st_collections_group_parts_part_description_text_elipsis},
{"collections.group.parts.part.description.text.ellipsis", st_collections_group_parts_part_description_text_elipsis},
{"collections.group.parts.part.description.text.filter", st_collections_group_parts_part_description_text_filter},
{"collections.group.parts.part.description.box.layout", st_collections_group_parts_part_description_box_layout},
{"collections.group.parts.part.description.box.align", st_collections_group_parts_part_description_box_align},
{"collections.group.parts.part.description.box.padding", st_collections_group_parts_part_description_box_padding},
@ -5212,6 +5214,7 @@ st_collections_group_parts_part_description_inherit(void)
ted->text.text.str = STRDUP(ted->text.text.str);
ted->text.text_class = STRDUP(ted->text.text_class);
ted->text.font.str = STRDUP(ted->text.font.str);
ted->text.filter.str = STRDUP(ted->text.filter.str);
data_queue_copied_part_lookup(pc, &(tparent->text.id_source), &(ted->text.id_source));
data_queue_copied_part_lookup(pc, &(tparent->text.id_text_source), &(ted->text.id_text_source));
@ -7295,6 +7298,104 @@ st_collections_group_parts_part_description_text_elipsis(void)
ed->text.elipsis = parse_float_range(0, -1.0, 1.0);
}
/**
@page edcref
@property
filter
@parameters
[filter program as a string]
@effect
Applies a series of filtering operations to the text.
EXPERIMENTAL FEATURE. TO BE DOCUMENTED.
@endproperty
*/
static void
st_collections_group_parts_part_description_text_filter(void)
{
Edje_Part_Description_Text *ed;
Eina_List *sources = NULL;
Eina_Stringshare *name;
char *token, *code;
Eina_Bool valid = EINA_TRUE;
Edje_Part_Collection *pc;
static int part_key = 0;
static const char *allowed_name_chars =
"abcdefghijklmnopqrstuvwxyzABCDEFGHJIKLMNOPQRSTUVWXYZ0123456789_";
check_arg_count(1);
if (current_part->type != EDJE_PART_TYPE_TEXT)
{
ERR("parse error %s:%i. text attributes in non-TEXT part.",
file_in, line - 1);
exit(-1);
}
ed = (Edje_Part_Description_Text*) current_desc;
ed->text.filter_sources = NULL;
ed->text.filter.str = parse_str(0);
if (!ed->text.filter.str) return;
pc = eina_list_data_get(eina_list_last(edje_collections));
// Parse list of buffers that have a source
// note: does not support comments
code = strdup(ed->text.filter.str);
for (token = strtok(code, ";"); token; token = strtok(NULL, ";"))
{
size_t len;
len = strspn(token, " \n\t");
token += len;
if (!strncasecmp("buffer", token, 6))
{
// note: a valid string won't necessary compile at runtime
token = strchr(token, ':');
if (!token)
{
valid = EINA_FALSE;
break;
}
token = strchr(token, '(');
if (!token)
{
valid = EINA_FALSE;
break;
}
token = strcasestr(token, "src");
if (!token) continue;
token += 3;
len = strspn(token, " =\n\t");
if (!len || !token[len])
{
valid = EINA_FALSE;
break;
}
token += len;
len = strspn(token, allowed_name_chars);
if (!len || !token[len])
{
valid = EINA_FALSE;
break;
}
token[len] = '\0';
name = eina_stringshare_add(token);
sources = eina_list_append(sources, name);
data_queue_part_lookup(pc, name, &part_key);
}
}
free(code);
if (valid) ed->text.filter_sources = sources;
}
/**
@edcsubsection{collections_group_parts_description_box,Box}

View File

@ -805,6 +805,8 @@ _edje_edd_init(void)
EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_text, Edje_Part_Description_Text, "text.id_source", text.id_source, EET_T_INT);
EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_text, Edje_Part_Description_Text, "text.id_text_source", text.id_text_source, EET_T_INT);
EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_text, Edje_Part_Description_Text, "text.elipsis", text.elipsis, EET_T_DOUBLE);
EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_text, Edje_Part_Description_Text, "text.filter", text.filter, EET_T_STRING);
EET_DATA_DESCRIPTOR_ADD_LIST_STRING(_edje_edd_edje_part_description_text, Edje_Part_Description_Text, "text.filter_sources", text.filter_sources);
EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Edje_Part_Description_Text);
eddc.func.mem_free = mem_free_textblock;

View File

@ -1358,10 +1358,11 @@ _edje_file_del(Edje *ed)
else if ((rp->type == EDJE_RP_TYPE_TEXT) &&
(rp->typedata.text))
{
if (rp->typedata.text->text) eina_stringshare_del(rp->typedata.text->text);
if (rp->typedata.text->font) eina_stringshare_del(rp->typedata.text->font);
if (rp->typedata.text->cache.in_str) eina_stringshare_del(rp->typedata.text->cache.in_str);
if (rp->typedata.text->cache.out_str) eina_stringshare_del(rp->typedata.text->cache.out_str);
eina_stringshare_del(rp->typedata.text->text);
eina_stringshare_del(rp->typedata.text->font);
eina_stringshare_del(rp->typedata.text->cache.in_str);
eina_stringshare_del(rp->typedata.text->cache.out_str);
eina_stringshare_del(rp->typedata.text->filter);
free(rp->typedata.text);
}
else if ((rp->type == EDJE_RP_TYPE_SWALLOW) &&
@ -1746,10 +1747,11 @@ _edje_collection_free_part_description_clean(int type, Edje_Part_Description_Com
text = (Edje_Part_Description_Text *) desc;
if (text->text.text.str) eina_stringshare_del(text->text.text.str);
if (text->text.text_class) eina_stringshare_del(text->text.text_class);
if (text->text.style.str) eina_stringshare_del(text->text.style.str);
if (text->text.font.str) eina_stringshare_del(text->text.font.str);
eina_stringshare_del(text->text.text.str);
eina_stringshare_del(text->text.text_class);
eina_stringshare_del(text->text.style.str);
eina_stringshare_del(text->text.font.str);
eina_stringshare_del(text->text.filter.str);
}
break;
}

View File

@ -1175,6 +1175,8 @@ struct _Edje_Part_Description_Spec_Text
Edje_String style; /* the text style if a textblock */
Edje_String font; /* if a specific font is asked for */
Edje_String repch; /* replacement char for password mode entry */
Edje_String filter; /* special effects */
Eina_List *filter_sources; /* proxy sources for special effects */
Edje_Alignment align; /* text alignment within bounds */
Edje_Color color3;
@ -1530,6 +1532,8 @@ struct _Edje_Real_Part_Text
const char *text; // 4
const char *font; // 4
const char *style; // 4
const char *filter; // 4
Eina_List *filter_sources; // 4
Edje_Position offset; // 8
short size; // 2
struct {
@ -1542,7 +1546,7 @@ struct _Edje_Real_Part_Text
const char *out_str; // 4
FLOAT_T align_x, align_y; // 16
} cache;
}; // 76
}; // 84
// FIXME make text a potiner to struct and alloc at end
// if part type is TEXT move common members textblock +
// text to front and have smaller struct for textblock

View File

@ -169,6 +169,8 @@ _edje_text_recalc_apply(Edje *ed, Edje_Real_Part *ep,
char *font2 = NULL;
char *sfont = NULL;
int size;
const char *filter, *source_name;
Eina_List *filter_sources = NULL, *prev_sources = NULL, *li;
Evas_Coord tw, th;
Evas_Coord sw, sh;
int inlined_font = 0, free_text = 0;
@ -181,10 +183,18 @@ _edje_text_recalc_apply(Edje *ed, Edje_Real_Part *ep,
if (sc == 0.0) sc = _edje_scale;
text = edje_string_get(&chosen_desc->text.text);
font = _edje_text_class_font_get(ed, chosen_desc, &size, &sfont);
filter = chosen_desc->text.filter.str;
if (ep->typedata.text->text) text = ep->typedata.text->text;
if (ep->typedata.text->font) font = ep->typedata.text->font;
if (ep->typedata.text->size > 0) size = ep->typedata.text->size;
if (ep->typedata.text->filter) filter = ep->typedata.text->filter;
if (ep->typedata.text->filter_sources != chosen_desc->text.filter_sources)
{
prev_sources = ep->typedata.text->filter_sources;
filter_sources = chosen_desc->text.filter_sources;
ep->typedata.text->filter_sources = chosen_desc->text.filter_sources;
}
if (ep->typedata.text->text_source)
{
@ -420,6 +430,17 @@ arrange_text:
evas_obj_text_font_set(font, size),
evas_obj_text_text_set(text));
part_get_geometry(ep, &tw, &th);
/* filters */
EINA_LIST_FOREACH(prev_sources, li, source_name)
eo_do(ep->object, evas_obj_text_filter_source_set(source_name, NULL));
EINA_LIST_FOREACH(filter_sources, li, source_name)
{
Edje_Real_Part *rp = _edje_real_part_get(ed, source_name);
eo_do(ep->object, evas_obj_text_filter_source_set(source_name, rp ? rp->object : NULL));
}
eo_do(ep->object, evas_obj_text_filter_program_set(filter));
/* Handle alignment */
{
FLOAT_T align_x;

View File

@ -1846,6 +1846,8 @@ enum
EVAS_OBJ_TEXT_SUB_ID_STYLE_PAD_GET,
EVAS_OBJ_TEXT_SUB_ID_ELLIPSIS_SET,
EVAS_OBJ_TEXT_SUB_ID_ELLIPSIS_GET,
EVAS_OBJ_TEXT_SUB_ID_FILTER_PROGRAM_SET,
EVAS_OBJ_TEXT_SUB_ID_FILTER_SOURCE_SET,
EVAS_OBJ_TEXT_SUB_ID_LAST
};
@ -2271,6 +2273,26 @@ enum
*/
#define evas_obj_text_ellipsis_get(ellipsis) EVAS_OBJ_TEXT_ID(EVAS_OBJ_TEXT_SUB_ID_ELLIPSIS_GET), EO_TYPECHECK(double *, ellipsis)
/**
* @def evas_obj_text_filter_program_set
* @since 1.9
* @note EXPERIMENTAL code
*/
#define evas_obj_text_filter_program_set(str) EVAS_OBJ_TEXT_ID(EVAS_OBJ_TEXT_SUB_ID_FILTER_PROGRAM_SET), EO_TYPECHECK(const char *, str)
/**
*
* Bind an object to use as a mask or texture in special filter
*
* @param[in] name Object name as used in the program code
* @param[in] obj Eo object to use through proxy rendering
*
* @see evas_obj_text_filter_program_set
*
* @note EXPERIMENTAL FEATURE.
*/
#define evas_obj_text_filter_source_set(name, obj) EVAS_OBJ_TEXT_ID(EVAS_OBJ_TEXT_SUB_ID_FILTER_SOURCE_SET), EO_TYPECHECK(const char *, name), EO_TYPECHECK(Eo *, obj)
/**
* @}
*/

View File

@ -3231,8 +3231,9 @@ _proxy_set(Evas_Object *eo_proxy, Evas_Object *eo_src)
EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, src->proxy, Evas_Object_Proxy_Data, proxy_src_write)
{
proxy_src_write->proxies = eina_list_append(proxy_src_write->proxies,
eo_proxy);
if (!eina_list_data_find(proxy_src_write->proxies, eo_proxy))
proxy_src_write->proxies = eina_list_append(proxy_src_write->proxies,
eo_proxy);
proxy_src_write->redraw = EINA_TRUE;
}
EINA_COW_WRITE_END(evas_object_proxy_cow, src->proxy, proxy_src_write);

View File

@ -622,6 +622,8 @@ _destructor(Eo *eo_obj, void *_pd, va_list *list EINA_UNUSED)
MAGIC_CHECK_END();
Evas_Object_Protected_Data *obj = _pd;
Evas_Object_Protected_Data *tmp;
Evas_Object *proxy;
Eina_List *l, *l2;
evas_object_hide(eo_obj);
if (obj->focused)
@ -654,8 +656,8 @@ _destructor(Eo *eo_obj, void *_pd, va_list *list EINA_UNUSED)
evas_object_grabs_cleanup(eo_obj, obj);
EINA_LIST_FREE(obj->clip.clipees, tmp)
evas_object_clip_unset(tmp->object);
while (obj->proxy->proxies)
evas_object_image_source_unset(obj->proxy->proxies->data);
EINA_LIST_FOREACH_SAFE(obj->proxy->proxies, l, l2, proxy)
evas_object_image_source_unset(proxy);
if (obj->cur->clipper) evas_object_clip_unset(eo_obj);
evas_object_map_set(eo_obj, NULL);
if (obj->is_smart) evas_object_smart_del(eo_obj);

View File

@ -1,5 +1,6 @@
#include "evas_common_private.h" /* Includes evas_bidi_utils stuff. */
#include "evas_private.h"
#include "evas_filter.h"
#include "Eo.h"
@ -9,6 +10,10 @@ EAPI Eo_Op EVAS_OBJ_TEXT_BASE_ID = EO_NOOP;
#define MY_CLASS_NAME "Evas_Text"
#ifdef EVAS_CSERVE2
# include "evas_cs2_private.h"
#endif
/* save typing */
#define ENFN obj->layer->evas->engine.func
#define ENDT obj->layer->evas->engine.data.output
@ -38,7 +43,18 @@ struct _Evas_Object_Text
double ellipsis;
Evas_Font_Size size;
unsigned char style;
Evas_Text_Style_Type style;
// special effects. VERY EXPERIMENTAL for now.
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;
} cur, prev;
struct {
@ -491,6 +507,7 @@ _text_font_set(Eo *eo_obj, void *_pd, va_list *list)
_evas_object_text_items_clear(o);
_evas_object_text_recalc(eo_obj, o->cur.text);
o->changed = 1;
o->cur.filter.changed = EINA_TRUE;
evas_object_change(eo_obj, obj);
evas_object_clip_dirty(eo_obj, obj);
evas_object_coords_recalc(eo_obj, obj);
@ -786,7 +803,10 @@ _evas_object_text_layout(Evas_Object *eo_obj, Evas_Object_Text *o, Eina_Unicode
}
}
evas_text_style_pad_get(o->cur.style, &l, &r, NULL, NULL);
if (!o->cur.filter.chain)
evas_text_style_pad_get(o->cur.style, &l, &r, NULL, NULL);
else
evas_filter_program_padding_get(o->cur.filter.chain, &l, &r, NULL, NULL);
/* Handle ellipsis */
if (pos && (o->cur.ellipsis >= 0.0) && (advance + l + r > obj->cur->geometry.w) && (obj->cur->geometry.w > 0))
@ -979,6 +999,7 @@ _text_ellipsis_set(Eo *eo_obj, void *_pd, va_list *list)
o->cur.ellipsis = ellipsis;
o->changed = 1;
o->cur.filter.changed = EINA_TRUE;
evas_object_change(eo_obj, obj);
evas_object_clip_dirty(eo_obj, obj);
}
@ -1064,6 +1085,7 @@ _text_text_set(Eo *eo_obj, void *_pd, va_list *list)
if (o->cur.text != text) free(text);
o->changed = 1;
o->cur.filter.changed = EINA_TRUE;
evas_object_change(eo_obj, obj);
evas_object_clip_dirty(eo_obj, obj);
evas_object_coords_recalc(eo_obj, obj);
@ -1335,7 +1357,10 @@ _text_char_pos_get(Eo *eo_obj, void *_pd, va_list *list)
Eina_Bool int_ret = _evas_object_text_char_coords_get(eo_obj, o, (size_t) pos,
&x, &y, &w, &h);
evas_text_style_pad_get(o->cur.style, &l, &r, &t, &b);
if (!o->cur.filter.chain)
evas_text_style_pad_get(o->cur.style, &l, &r, &t, &b);
else
evas_filter_program_padding_get(o->cur.filter.chain, &l, &r, &t, &b);
y += o->max_ascent - t;
x -= l;
if (x < 0)
@ -1419,7 +1444,10 @@ _text_char_coords_get(Eo *eo_obj, void *_pd, va_list *list)
int int_ret = _evas_object_text_char_at_coords(eo_obj, o, x, y - o->max_ascent,
&rx, &ry, &rw, &rh);
evas_text_style_pad_get(o->cur.style, &l, &r, &t, &b);
if (!o->cur.filter.chain)
evas_text_style_pad_get(o->cur.style, &l, &r, &t, &b);
else
evas_filter_program_padding_get(o->cur.filter.chain, &l, &r, &t, &b);
ry += o->max_ascent - t;
rx -= l;
if (rx < 0)
@ -1741,7 +1769,10 @@ _text_style_pad_get(Eo *eo_obj EINA_UNUSED, void *_pd, va_list *list)
int sl = 0, sr = 0, st = 0, sb = 0;
const Evas_Object_Text *o = _pd;
/* use temps to be certain we have initialized values */
evas_text_style_pad_get(o->cur.style, &sl, &sr, &st, &sb);
if (!o->cur.filter.chain)
evas_text_style_pad_get(o->cur.style, &sl, &sr, &st, &sb);
else
evas_filter_program_padding_get(o->cur.filter.chain, &sl, &sr, &st, &sb);
if (l) *l = sl;
if (r) *r = sr;
if (t) *t = st;
@ -1946,6 +1977,18 @@ evas_object_text_free(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj)
{
Evas_Object_Text *o = eo_data_scope_get(eo_obj, MY_CLASS);
/* free filter output */
if (o->cur.filter.output)
ENFN->image_free(ENDT, o->cur.filter.output);
eina_hash_free(o->cur.filter.sources);
evas_filter_program_del(o->cur.filter.chain);
eina_stringshare_del(o->cur.filter.code);
o->cur.filter.output = NULL;
o->cur.filter.chain = NULL;
o->cur.filter.sources = NULL;
o->cur.filter.code = NULL;
o->cur.filter.sources_count = 0;
/* free obj */
_evas_object_text_items_clear(o);
if (o->cur.utf8_text) eina_stringshare_del(o->cur.utf8_text);
@ -1984,7 +2027,8 @@ static void
evas_object_text_render(Evas_Object *eo_obj EINA_UNUSED,
Evas_Object_Protected_Data *obj,
void *type_private_data,
void *output, void *context, void *surface, int x, int y, Eina_Bool do_async)
void *output, void *context, void *surface,
int x, int y, Eina_Bool do_async)
{
int i, j;
Evas_Object_Text *o = type_private_data;
@ -1998,10 +2042,13 @@ evas_object_text_render(Evas_Object *eo_obj EINA_UNUSED,
{0, 1, 2, 1, 0}
};
int sl = 0, st = 0;
int shad_dst, shad_sz, dx, dy, haveshad;
int shad_dst = 0, shad_sz = 0, dx = 0, dy = 0, haveshad = 0;
/* render object to surface with context, and offxet by x,y */
evas_text_style_pad_get(o->cur.style, &sl, NULL, &st, NULL);
if (!o->cur.filter.chain)
evas_text_style_pad_get(o->cur.style, &sl, NULL, &st, NULL);
else
evas_filter_program_padding_get(o->cur.filter.chain, &sl, NULL, &st, NULL);
ENFN->context_multiplier_unset(output, context);
ENFN->context_render_op_set(output, context, obj->cur->render_op);
/* FIXME: This clipping is just until we fix inset handling correctly. */
@ -2010,6 +2057,7 @@ evas_object_text_render(Evas_Object *eo_obj EINA_UNUSED,
obj->cur->geometry.y + y,
obj->cur->geometry.w,
obj->cur->geometry.h);
/*
ENFN->context_color_set(output,
context,
@ -2077,8 +2125,138 @@ evas_object_text_render(Evas_Object *eo_obj EINA_UNUSED,
&it->text_props, \
do_async);
/* FIXME/WARNING
* The code below is EXPERIMENTAL, and not to be considered usable or even
* remotely similar to its final form. You've been warned :)
*/
if (o->cur.filter.chain || (o->cur.filter.code && !o->cur.filter.invalid))
{
int X, Y, W, H;
Evas_Filter_Context *filter;
const int inbuf = 1;
const int outbuf = 2;
void *filter_ctx;
Eina_Bool ok;
int ox = 0, oy = 0;
Image_Entry *previous = o->cur.filter.output;
/* NOTE: Font effect rendering is now done ENTIRELY on CPU.
* So we rely on cache/cache2 to allocate a real image buffer,
* that we can draw to. The OpenGL texture will be created only
* after the rendering has been done, as we simply push the output
* image to GL.
*/
W = obj->cur->geometry.w;
H = obj->cur->geometry.h;
X = obj->cur->geometry.x;
Y = obj->cur->geometry.y;
if (!o->cur.filter.chain)
{
Evas_Filter_Program *pgm;
pgm = evas_filter_program_new("Evas_Text");
evas_filter_program_source_set_all(pgm, o->cur.filter.sources);
if (!evas_filter_program_parse(pgm, o->cur.filter.code))
{
ERR("Filter program parsing failed");
evas_filter_program_del(pgm);
o->cur.filter.invalid = EINA_TRUE;
goto normal_render;
}
o->cur.filter.chain = pgm;
o->cur.filter.invalid = EINA_FALSE;
}
else if (previous)
{
Eina_Bool redraw = o->cur.filter.changed;
// Scan proxies to find if any changed
if (!redraw && o->cur.filter.sources)
{
Evas_Filter_Proxy_Binding *pb;
Evas_Object_Protected_Data *source;
Eina_Iterator *it;
it = eina_hash_iterator_data_new(o->cur.filter.sources);
EINA_ITERATOR_FOREACH(it, pb)
{
source = eo_data_scope_get(pb->eo_source, EVAS_OBJ_CLASS);
if (source->changed)
{
redraw = EINA_TRUE;
break;
}
}
eina_iterator_free(it);
}
if (!redraw)
{
// Render this image only
ENFN->image_draw(ENDT, context,
surface, previous,
0, 0, W, H, // src
X + x, Y + y, W, H, // dst
EINA_FALSE, // smooth
do_async);
return;
}
}
filter = evas_filter_context_new(obj->layer->evas, do_async);
ok = evas_filter_context_program_use(filter, o->cur.filter.chain);
if (!filter || !ok)
{
ERR("Parsing failed?");
evas_filter_context_destroy(filter);
goto normal_render;
}
// Proxies
evas_filter_context_proxy_render_all(filter, eo_obj, EINA_FALSE);
// Context: FIXME it should be a sw context only
filter_ctx = ENFN->context_new(ENDT);
ENFN->context_color_set(ENDT, filter_ctx, 255, 255, 255, 255);
// Allocate all buffers now
evas_filter_context_buffers_allocate_all(filter, W, H);
evas_filter_target_set(filter, context, surface, X + x, Y + y);
// Steal output and release previous
o->cur.filter.output = evas_filter_buffer_backing_steal(filter, outbuf);
if (o->cur.filter.output != previous)
evas_filter_buffer_backing_release(filter, previous);
// Render text to input buffer
EINA_INLIST_FOREACH(EINA_INLIST_GET(o->items), it)
if ((o->font) && (it->text_props.len > 0))
{
evas_filter_font_draw(filter, filter_ctx, inbuf, o->font,
sl + ox + it->x,
st + oy + (int) o->max_ascent,
&it->text_props,
do_async);
}
ENFN->context_free(ENDT, filter_ctx);
// Add post-run callback and run filter
evas_filter_context_autodestroy(filter);
evas_filter_run(filter);
o->cur.filter.changed = EINA_FALSE;
DBG("Effect rendering done.");
return;
}
/* End of the EXPERIMENTAL code */
normal_render:
/* shadows */
shad_dst = shad_sz = dx = dy = haveshad = 0;
switch (o->cur.style & EVAS_TEXT_STYLE_MASK_BASIC)
{
case EVAS_TEXT_STYLE_SHADOW:
@ -2425,6 +2603,7 @@ _evas_object_text_rehint(Evas_Object *eo_obj)
/* DO II */
_evas_object_text_recalc(eo_obj, o->cur.text);
o->changed = 1;
o->cur.filter.changed = EINA_TRUE;
evas_object_change(eo_obj, obj);
evas_object_clip_dirty(eo_obj, obj);
evas_object_coords_recalc(eo_obj, obj);
@ -2490,7 +2669,10 @@ _evas_object_text_recalc(Evas_Object *eo_obj, Eina_Unicode *text)
w = _evas_object_text_horiz_advance_get(o);
h = _evas_object_text_vert_advance_get(eo_obj, o);
evas_text_style_pad_get(o->cur.style, &l, &r, &t, &b);
if (!o->cur.filter.chain)
evas_text_style_pad_get(o->cur.style, &l, &r, &t, &b);
else
evas_filter_program_padding_get(o->cur.filter.chain, &l, &r, &t, &b);
if (o->cur.ellipsis >= 0.0)
{
@ -2509,9 +2691,12 @@ _evas_object_text_recalc(Evas_Object *eo_obj, Eina_Unicode *text)
}
else
{
int t = 0, b = 0;
int t = 0, b = 0, l = 0, r = 0;
evas_text_style_pad_get(o->cur.style, NULL, NULL, &t, &b);
if (!o->cur.filter.chain)
evas_text_style_pad_get(o->cur.style, &l, &r, &t, &b);
else
evas_filter_program_padding_get(o->cur.filter.chain, &l, &r, &t, &b);
eo_do_super(eo_obj, MY_CLASS,
evas_obj_size_set(0, o->max_ascent + o->max_descent + t + b));
@ -2521,6 +2706,158 @@ _evas_object_text_recalc(Evas_Object *eo_obj, Eina_Unicode *text)
o->last_computed.h = obj->cur->geometry.h;
}
/* EXPERIMENTAL CODE BEGIN */
static void
_filter_program_set(Eo *eo_obj, void *_pd, va_list *list)
{
Evas_Object_Text *o = _pd;
const char *arg = va_arg(list, const char *);
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");
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;
}
}
o->cur.filter.chain = pgm;
o->cur.filter.changed = EINA_TRUE;
o->cur.filter.invalid = (pgm == NULL);
eina_stringshare_replace(&o->cur.filter.code, arg);
// Update object
obj = eo_data_scope_get(eo_obj, EVAS_OBJ_CLASS);
_evas_object_text_items_clear(o);
o->changed = 1;
_evas_object_text_recalc(eo_obj, o->cur.text);
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_Object_Text *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)
{
o->cur.filter.sources_count--;
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);
}
static void
_filter_source_set(Eo *eo_obj, void *_pd, va_list *list)
{
Evas_Object_Text *o = _pd;
Evas_Object_Protected_Data *obj;
Evas_Filter_Program *pgm = o->cur.filter.chain;
const char *name = va_arg(list, const char *);
Evas_Object *eo_source = va_arg(list, Evas_Object *);
Evas_Filter_Proxy_Binding *pb, *pb_old = NULL;
Evas_Object_Protected_Data *source = NULL;
obj = eo_data_scope_get(eo_obj, EVAS_OBJ_CLASS);
if (eo_source) source = eo_data_scope_get(eo_source, EVAS_OBJ_CLASS);
if (!o->cur.filter.sources)
{
o->cur.filter.sources = eina_hash_string_small_new
(EINA_FREE_CB(_filter_source_hash_free_cb));
}
else
{
pb_old = eina_hash_find(o->cur.filter.sources, name);
if (pb_old)
{
if (pb_old->eo_source == eo_source) goto update;
eina_hash_del(o->cur.filter.sources, name, pb_old);
}
}
if (!source)
{
pb_old = eina_hash_find(o->cur.filter.sources, name);
if (!pb_old) return;
eina_hash_del_by_key(o->cur.filter.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(o->cur.filter.sources, pb->name, pb);
o->cur.filter.sources_count++;
evas_filter_program_source_set_all(pgm, o->cur.filter.sources);
// Update object
update:
o->cur.filter.changed = EINA_TRUE;
o->cur.filter.invalid = EINA_FALSE;
_evas_object_text_items_clear(o);
o->changed = 1;
_evas_object_text_recalc(eo_obj, o->cur.text);
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);
}
/* EXPERIMENTAL CODE END */
static void
_class_constructor(Eo_Class *klass)
{
@ -2561,6 +2898,8 @@ _class_constructor(Eo_Class *klass)
EO_OP_FUNC(EVAS_OBJ_TEXT_ID(EVAS_OBJ_TEXT_SUB_ID_STYLE_PAD_GET), _text_style_pad_get),
EO_OP_FUNC(EVAS_OBJ_TEXT_ID(EVAS_OBJ_TEXT_SUB_ID_ELLIPSIS_SET), _text_ellipsis_set),
EO_OP_FUNC(EVAS_OBJ_TEXT_ID(EVAS_OBJ_TEXT_SUB_ID_ELLIPSIS_GET), _text_ellipsis_get),
EO_OP_FUNC(EVAS_OBJ_TEXT_ID(EVAS_OBJ_TEXT_SUB_ID_FILTER_PROGRAM_SET), _filter_program_set),
EO_OP_FUNC(EVAS_OBJ_TEXT_ID(EVAS_OBJ_TEXT_SUB_ID_FILTER_SOURCE_SET), _filter_source_set),
EO_OP_FUNC_SENTINEL
};
eo_class_funcs_set(klass, func_desc);
@ -2598,6 +2937,8 @@ static const Eo_Op_Description op_desc[] = {
EO_OP_DESCRIPTION(EVAS_OBJ_TEXT_SUB_ID_STYLE_PAD_GET, "Gets the text style pad of a text object."),
EO_OP_DESCRIPTION(EVAS_OBJ_TEXT_SUB_ID_ELLIPSIS_SET, "Gets the ellipsis of a text object."),
EO_OP_DESCRIPTION(EVAS_OBJ_TEXT_SUB_ID_ELLIPSIS_GET, "Sets the ellipsis of a text object."),
EO_OP_DESCRIPTION(EVAS_OBJ_TEXT_SUB_ID_FILTER_PROGRAM_SET, "Text special effects: Set the style program (string)."),
EO_OP_DESCRIPTION(EVAS_OBJ_TEXT_SUB_ID_FILTER_SOURCE_SET, "Text special effects: Bind an Evas_Object to a name for proxy rendering."),
EO_OP_DESCRIPTION_SENTINEL
};
static const Eo_Class_Description class_desc = {

View File

@ -8,6 +8,7 @@
#include "evas_private.h"
#include "evas_font_private.h"
#include "evas_blend_private.h"
#ifdef EVAS_CSERVE2
# include "../cserve2/evas_cs2_private.h"
@ -449,8 +450,8 @@ evas_common_font_glyph_uncompress(RGBA_Font_Glyph *fg, int *wret, int *hret)
int *iptr;
if (!buf) return NULL;
*wret = fgo->bitmap.width;
*hret = fgo->bitmap.rows;
if (wret) *wret = fgo->bitmap.width;
if (hret) *hret = fgo->bitmap.rows;
iptr = (int *)fgo->rle;
if (*iptr > 0) // rle4
decompress_rle4(fgo->rle, buf, fgo->bitmap.width,
@ -488,37 +489,63 @@ evas_common_font_glyph_draw(RGBA_Font_Glyph *fg,
x1 = 0; x2 = w;
if ((x + x1) < cx) x1 = cx - x;
if ((x + x2) > (cx + cw)) x2 = cx + cw - x;
// build fast multiply + mask color tables to avoid compute. this works
// because of our very limited 4bit range of alpha values
col = dc->col.col;
for (i = 0; i <= 0xf; i++)
if (dst_image->cache_entry.space == EVAS_COLORSPACE_GRY8)
{
v = (i << 4) | i;
coltab[i] = MUL_SYM(v, col);
tmp = (coltab[i] >> 24);
mtab[i] = 256 - (tmp + (tmp >> 7));
// FIXME: Font draw not optimized for Alpha targets! SLOW!
// This is not pretty :)
DATA8 *dst8 = dst_image->mask.data + x + (y * dst_pitch);
Alpha_Gfx_Func func;
DATA8 *src8;
int row;
func = evas_common_alpha_func_get(dc->render_op);
src8 = evas_common_font_glyph_uncompress(fg, NULL, NULL);
if (!src8) return;
for (row = y1; row < y2; row++)
{
DATA8 *d = dst8 + ((row - y1) * dst_pitch);
DATA8 *s = src8 + (row * w) + x1;
func(s, d, x2 - x1);
}
free(src8);
}
#ifdef BUILD_MMX
if (evas_common_cpu_has_feature(CPU_FEATURE_MMX))
else
{
// build fast multiply + mask color tables to avoid compute. this works
// because of our very limited 4bit range of alpha values
for (i = 0; i <= 0xf; i++)
{
v = (i << 4) | i;
coltab[i] = MUL_SYM(v, col);
tmp = (coltab[i] >> 24);
mtab[i] = 256 - (tmp + (tmp >> 7));
}
#ifdef BUILD_MMX
if (evas_common_cpu_has_feature(CPU_FEATURE_MMX))
{
#define MMX 1
#include "evas_font_compress_draw.c"
#undef MMX
}
else
}
else
#endif
#ifdef BUILD_NEON
if (evas_common_cpu_has_feature(CPU_FEATURE_NEON))
{
if (evas_common_cpu_has_feature(CPU_FEATURE_NEON))
{
#define NEON 1
#include "evas_font_compress_draw.c"
#undef NEON
}
else
}
else
#endif
{
// Plain C
{
#include "evas_font_compress_draw.c"
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,491 @@
#include "evas_filter.h"
#include "evas_filter_private.h"
#include "evas_blend_private.h"
#if DIV_USING_BITSHIFT
static int
_smallest_pow2_larger_than(int val)
{
int n;
for (n = 0; n < 32; n++)
if (val <= (1 << n)) return n;
ERR("Value %d is too damn high!", val);
return 32;
}
# define DEFINE_DIVIDER(div) const int pow2 = _smallest_pow2_larger_than((div) << 10); const int numerator = (1 << pow2) / (div);
# define DIVIDE(val) (((val) * numerator) >> pow2)
#else
# define DEFINE_DIAMETER(div) const int divider = (div);
# define DIVIDE(val) ((val) / divider)
#endif
typedef Eina_Bool (*image_draw_func) (void *data, void *context, void *surface, void *image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int dst_w, int dst_h, int smooth, Eina_Bool do_async);
static void _mapped_blend_cpu(void *data, void *drawctx, RGBA_Image *in, RGBA_Image *out, Evas_Filter_Fill_Mode fillmode, int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, image_draw_func image_draw);
static Eina_Bool
_filter_blend_cpu_alpha(Evas_Filter_Command *cmd)
{
RGBA_Image *in, *out;
Alpha_Gfx_Func func;
DATA8 *maskdata, *dstdata;
int sw, sh, dw, dh, ox, oy, sx = 0, sy = 0, dx = 0, dy = 0, rows, cols, y;
/*
* NOTE: Alpha to alpha blending does not support stretching operations
* yet, because we don't have any scaling functions for alpha buffers.
* Also, I'm not going to implement it by converting to RGBA either.
*/
if (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_XY)
{
CRI("Alpha to alpha blending does not support stretch");
return EINA_FALSE;
}
// TODO: Call _mapped_blend_cpu to implement repeat fill mode.
if (cmd->draw.fillmode != EVAS_FILTER_FILL_MODE_NONE)
ERR("Fill modes are not implemented for Alpha --> RGBA");
func = evas_common_alpha_func_get(cmd->draw.render_op);
if (!func)
return EINA_FALSE;
in = cmd->input->backing;
out = cmd->output->backing;
sw = in->cache_entry.w;
sh = in->cache_entry.h;
dw = out->cache_entry.w;
dh = out->cache_entry.h;
ox = cmd->draw.ox;
oy = cmd->draw.oy;
maskdata = in->mask.data;
dstdata = out->mask.data;
if (!ox && !oy && (dw == sw) && (dh == sh))
{
func(maskdata, dstdata, sw * sh);
return EINA_TRUE;
}
_clip_to_target(&sx, &sy, sw, sh, ox, oy, dw, dh, &dx, &dy, &rows, &cols);
// FIXME/TODO: Clip to context clip
if (cols <= 0 || rows <= 0)
return EINA_TRUE;
maskdata += sy * sw;
dstdata += dy * dw;
for (y = rows; y; y--)
{
func(maskdata + sx, dstdata + dx, cols);
maskdata += sw;
dstdata += dw;
}
return EINA_TRUE;
}
static Eina_Bool
_filter_blend_cpu_rgba(Evas_Filter_Command *cmd)
{
RGBA_Image *in, *out;
RGBA_Draw_Context *drawctx;
int sw, sh, dx, dy, dw, dh, sx, sy;
in = cmd->input->backing;
out = cmd->output->backing;
EINA_SAFETY_ON_NULL_RETURN_VAL(in, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(out, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(in->image.data, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(out->image.data, EINA_FALSE);
sx = 0;
sy = 0;
sw = in->cache_entry.w;
sh = in->cache_entry.h;
dx = cmd->draw.ox;
dy = cmd->draw.oy;
dw = out->cache_entry.w;
dh = out->cache_entry.h;
if ((dw <= 0) || (dh <= 0) || (sw <= 0) || (sh <= 0))
return EINA_TRUE;
drawctx = cmd->ENFN->context_new(cmd->ENDT);
cmd->ENFN->context_color_set(cmd->ENDT, drawctx, cmd->draw.R, cmd->draw.G,
cmd->draw.B, cmd->draw.A);
cmd->ENFN->context_render_op_set(cmd->ENDT, drawctx, cmd->draw.render_op);
if (cmd->draw.clip_use)
{
cmd->ENFN->context_clip_set(cmd->ENDT, drawctx,
cmd->draw.clip.x, cmd->draw.clip.y,
cmd->draw.clip.w, cmd->draw.clip.h);
cmd->ENFN->context_clip_clip(cmd->ENDT, drawctx, 0, 0,
out->cache_entry.w, out->cache_entry.h);
}
else
{
cmd->ENFN->context_clip_set(cmd->ENDT, drawctx, 0, 0,
out->cache_entry.w, out->cache_entry.h);
}
_mapped_blend_cpu(cmd->ENDT, drawctx, in, out, cmd->draw.fillmode,
sx, sy, sw, sh, dx, dy, dw, dh,
cmd->ENFN->image_draw);
cmd->ENFN->context_free(cmd->ENDT, drawctx);
return EINA_TRUE;
}
static void
_mapped_blend_cpu(void *data, void *drawctx,
RGBA_Image *in, RGBA_Image *out,
Evas_Filter_Fill_Mode fillmode,
int sx, int sy,
int sw, int sh,
int dx, int dy,
int dw, int dh,
image_draw_func image_draw)
{
int right = 0, bottom = 0, left = 0, top = 0;
int row, col, rows, cols;
if (fillmode == EVAS_FILTER_FILL_MODE_NONE)
{
int rows, cols;
_clip_to_target(&sx, &sy, sw, sh, dx, dy, out->cache_entry.w,
out->cache_entry.h, &dx, &dy, &rows, &cols);
DBG("blend: %d,%d,%d,%d --> %d,%d,%d,%d (from %dx%d to %dx%d +%d,%d)",
0, 0, sw, sh, dx, dy, cols, rows,
in->cache_entry.w, in->cache_entry.h,
out->cache_entry.w, out->cache_entry.h,
dx, dy);
image_draw(data, drawctx, out, in,
sx, sy, cols, rows, // src
dx, dy, cols, rows, // dst
EINA_TRUE, // smooth
EINA_FALSE); // Not async
return;
}
if (fillmode & EVAS_FILTER_FILL_MODE_REPEAT_X)
{
if (dx > 0) left = dx % sw;
else if (dx < 0) left = sw + (dx % sw);
cols = (dw /*- left*/) / sw;
if (left > 0)
right = dw - (sw * (cols - 1)) - left;
else
right = dw - (sw * cols);
dx = 0;
}
else if (fillmode & EVAS_FILTER_FILL_MODE_STRETCH_X)
{
cols = 1;
dw = out->cache_entry.w;
dx = 0;
}
else
{
cols = 0;
dw = out->cache_entry.w - dx;
}
if (fillmode & EVAS_FILTER_FILL_MODE_REPEAT_Y)
{
if (dy > 0) top = dy % sh;
else if (dy < 0) top = sh + (dy % sh);
rows = (dh /*- top*/) / sh;
if (top > 0)
bottom = dh - (sh * (rows - 1)) - top;
else
bottom = dh - (sh * rows);
dy = 0;
}
else if (fillmode & EVAS_FILTER_FILL_MODE_STRETCH_Y)
{
rows = 1;
dh = out->cache_entry.h;
dy = 0;
}
else
{
rows = 0;
dh = out->cache_entry.h - dy;
}
if (top > 0) row = -1;
else row = 0;
for (; row <= rows; row++)
{
int src_x, src_y, src_w, src_h;
int dst_x, dst_y, dst_w, dst_h;
if (row == -1 && top > 0)
{
// repeat only
src_h = top;
src_y = sh - top;
dst_y = dy;
dst_h = src_h;
}
else if (row == rows && bottom > 0)
{
// repeat only
src_h = bottom;
src_y = 0;
dst_y = top + dy + row * sh;
dst_h = src_h;
}
else
{
src_y = 0;
if (fillmode & EVAS_FILTER_FILL_MODE_STRETCH_Y)
{
src_h = sh;
dst_h = dh;
dst_y = 0;
}
else
{
dst_y = top + dy + row * sh;
src_h = MIN(dh - dst_y, sh);
dst_h = src_h;
}
}
if (src_h <= 0 || dst_h <= 0) break;
if (left > 0) col = -1;
else col = 0;
for (; col <= cols; col++)
{
if (col == -1 && left > 0)
{
// repeat only
src_w = left;
src_x = sw - left;
dst_x = dx;
dst_w = src_w;
}
else if (col == cols && right > 0)
{
// repeat only
src_w = right;
src_x = 0;
dst_x = left + dx + col * sw;
dst_w = src_w;
}
else
{
src_x = 0;
if (fillmode & EVAS_FILTER_FILL_MODE_STRETCH_X)
{
src_w = sw;
dst_w = dw;
dst_x = 0;
}
else
{
dst_x = left + dx + col * sw;
src_w = MIN(dw - dst_x, sw);
dst_w = src_w;
}
}
if (src_w <= 0 || dst_w <= 0) break;
DBG("blend: [%d,%d] %d,%d,%dx%d --> %d,%d,%dx%d "
"(src %dx%d, dst %dx%d)",
col, row, src_x, src_y, src_w, src_h,
dst_x, dst_y, dst_w, dst_h,
sw, sh, dw, dh);
image_draw(data, drawctx, out, in,
src_x, src_y, src_w, src_h,
dst_x, dst_y, dst_w, dst_h,
EINA_TRUE, EINA_FALSE);
}
}
}
static Eina_Bool
_filter_blend_cpu_mask_rgba(Evas_Filter_Command *cmd)
{
RGBA_Image *in, *out;
RGBA_Gfx_Func func;
DATA32 col;
DATA32 *dstdata;
DATA8 *maskdata;
int sw, sh, dw, dh, ox, oy, sx = 0, sy = 0, dx = 0, dy = 0, rows, cols, y;
// TODO: Call _mapped_blend_cpu to implement repeat fill mode.
if (cmd->draw.fillmode != EVAS_FILTER_FILL_MODE_NONE)
ERR("Fill modes are not implemented for Alpha --> RGBA");
in = cmd->input->backing;
out = cmd->output->backing;
sw = in->cache_entry.w;
sh = in->cache_entry.h;
dw = out->cache_entry.w;
dh = out->cache_entry.h;
ox = cmd->draw.ox;
oy = cmd->draw.oy;
dstdata = out->image.data;
maskdata = in->mask.data;
col = ARGB_JOIN(cmd->draw.A, cmd->draw.R, cmd->draw.G, cmd->draw.B);
// TODO: Fix this crash. Change proxy image and OUTPUT data is NULL. Why?
if (!dstdata)
{
ERR("Empty destination from buffer #%d %dx%d %p", cmd->output->id, dw, dh, out);
//abort();
return EINA_FALSE;
}
if (!maskdata)
abort();
//EINA_SAFETY_ON_NULL_RETURN_VAL(dstdata, EINA_FALSE);
//EINA_SAFETY_ON_NULL_RETURN_VAL(maskdata, EINA_FALSE);
func = evas_common_gfx_func_composite_mask_color_span_get
(col, out, 1, cmd->draw.render_op);
if (!func)
return EINA_FALSE;
if (!ox && !oy && (dw == sw) && (dh == sh))
{
func(NULL, maskdata, col, dstdata, sw * sh);
return EINA_TRUE;
}
_clip_to_target(&sx, &sy, sw, sh, ox, oy, dw, dh, &dx, &dy, &rows, &cols);
// FIXME/TODO: Clip to context clip
if (cols <= 0 || rows <= 0)
return EINA_TRUE;
maskdata += sy * sw;
dstdata += dy * dw;
for (y = rows; y; y--)
{
func(NULL, maskdata + sx, col, dstdata + dx, cols);
maskdata += sw;
dstdata += dw;
}
return EINA_TRUE;
}
static Eina_Bool
_image_draw_cpu_rgba2alpha(void *data EINA_UNUSED, void *context EINA_UNUSED,
void *surface, void *image,
int src_x, int src_y, int src_w, int src_h,
int dst_x, int dst_y, int dst_w, int dst_h,
int smooth EINA_UNUSED,
Eina_Bool do_async EINA_UNUSED)
{
RGBA_Image *src = image;
RGBA_Image *dst = surface;
DATA32* srcdata = src->image.data;
DATA8* dstdata = dst->mask.data;
int x, y, sw, dw;
DEFINE_DIVIDER(3);
EINA_SAFETY_ON_FALSE_RETURN_VAL((src_w == dst_w) && (src_h == dst_h), EINA_FALSE);
sw = src->cache_entry.w;
dw = dst->cache_entry.w;
srcdata += src_y * sw;
dstdata += dst_y * dw;
for (y = src_h; y; y--)
{
DATA32 *s = srcdata + src_x;
DATA8 *d = dstdata + dst_x;
for (x = src_w; x; x--, d++, s++)
{
// TODO: Add weights like in YUV <--> RGB?
*d = DIVIDE(R_VAL(s) + G_VAL(s) + B_VAL(s));
}
srcdata += sw;
dstdata += dw;
}
return EINA_TRUE;
}
static Eina_Bool
_filter_blend_cpu_rgba2alpha(Evas_Filter_Command *cmd)
{
RGBA_Image *in, *out;
int sw, sh, dx, dy, dw, dh, sx, sy;
in = cmd->input->backing;
out = cmd->output->backing;
EINA_SAFETY_ON_NULL_RETURN_VAL(in, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(out, EINA_FALSE);
sx = 0;
sy = 0;
sw = in->cache_entry.w;
sh = in->cache_entry.h;
dw = out->cache_entry.w;
dh = out->cache_entry.h;
dx = cmd->draw.ox;
dy = cmd->draw.oy;
if ((dw <= 0) || (dh <= 0) || (sw <= 0) || (sh <= 0))
return EINA_TRUE;
// Stretch if necessary.
if ((sw != dw || sh != dh) && (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_XY))
{
Evas_Filter_Buffer *fb;
if (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_X)
sw = dw;
if (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_Y)
sh = dh;
BUFFERS_LOCK();
fb = evas_filter_buffer_scaled_get(cmd->ctx, cmd->input, sw, sh);
BUFFERS_UNLOCK();
EINA_SAFETY_ON_NULL_RETURN_VAL(fb, EINA_FALSE);
fb->locked = EINA_FALSE;
in = fb->backing;
}
_mapped_blend_cpu(cmd->ENDT, NULL, in, out, cmd->draw.fillmode,
sx, sy, sw, sh, dx, dy, dw, dh,
_image_draw_cpu_rgba2alpha);
return EINA_TRUE;
}
Evas_Filter_Apply_Func
evas_filter_blend_cpu_func_get(Evas_Filter_Command *cmd)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input, NULL);
if (cmd->input->alpha_only)
{
if (cmd->output->alpha_only)
return _filter_blend_cpu_alpha;
else
return _filter_blend_cpu_mask_rgba;
}
else
{
if (cmd->output->alpha_only)
return _filter_blend_cpu_rgba2alpha;
else
return _filter_blend_cpu_rgba;
}
}

View File

@ -0,0 +1,774 @@
#include "evas_filter.h"
#include "evas_filter_private.h"
#include <math.h>
#include <time.h>
#define DEBUG_TIME 1
#if DIV_USING_BITSHIFT
static int
_smallest_pow2_larger_than(int val)
{
int n;
for (n = 0; n < 32; n++)
if (val <= (1 << n)) return n;
ERR("Value %d is too damn high!", val);
return 32;
}
/* Input:
* const int pow2 = _smallest_pow2_larger_than(divider * 1024);
* const int numerator = (1 << pow2) / divider;
* Result:
* r = ((val * numerator) >> pow2);
*/
# define DEFINE_DIAMETER(rad) const int pow2 = _smallest_pow2_larger_than((radius * 2 + 1) << 10); const int numerator = (1 << pow2) / (radius * 2 + 1);
# define DIVIDE_BY_DIAMETER(val) (((val) * numerator) >> pow2)
#else
# define DEFINE_DIAMETER(rad) const int diameter = radius * 2 + 1;
# define DIVIDE_BY_DIAMETER(val) ((val) / diameter)
#endif
// Switch from Pascal Triangle based gaussian to Sine.
// Gaussian is now disabled (because gauss and sine are too different)
#define MAX_GAUSSIAN_RADIUS 0
#if MAX_GAUSSIAN_RADIUS > 12
# error Impossible value
#endif
#if DEBUG_TIME
# define DEBUG_TIME_BEGIN() \
struct timespec ts1, ts2; \
clock_gettime(CLOCK_MONOTONIC, &ts1);
# define DEBUG_TIME_END() \
clock_gettime(CLOCK_MONOTONIC, &ts2); \
long long int t = 1000000LL * (ts2.tv_sec - ts1.tv_sec) \
+ (ts2.tv_nsec - ts1.tv_nsec) / 1000LL; \
INF("TIME SPENT: %lldus", t);
#else
# define DEBUG_TIME_BEGIN() do {} while(0)
# define DEBUG_TIME_END() do {} while(0)
#endif
/* RGBA functions */
static void
_box_blur_step_rgba(DATA32 *src, DATA32 *dst, int radius, int len, int step)
{
DEFINE_DIAMETER(radius);
int acc[4] = {0};
DATA8 *d, *rs, *ls;
int x, k;
int divider;
int left = MIN(radius, len);
int right = MIN(radius, (len - radius));
d = (DATA8 *) dst;
ls = (DATA8 *) src;
rs = (DATA8 *) src;
// Read-ahead
for (x = left; x; x--)
{
for (k = 0; k < 4; k++)
acc[k] += rs[k];
rs += step;
}
// Left
for (x = 0; x < left; x++)
{
for (k = 0; k < 4; k++)
acc[k] += rs[k];
rs += step;
divider = x + left + 1;
d[ALPHA] = acc[ALPHA] / divider;
d[RED] = acc[RED] / divider;
d[GREEN] = acc[GREEN] / divider;
d[BLUE] = acc[BLUE] / divider;
d += step;
}
// Main part
for (x = len - (2 * radius); x > 0; x--)
{
for (k = 0; k < 4; k++)
acc[k] += rs[k];
rs += step;
d[ALPHA] = DIVIDE_BY_DIAMETER(acc[ALPHA]);
d[RED] = DIVIDE_BY_DIAMETER(acc[RED]);
d[GREEN] = DIVIDE_BY_DIAMETER(acc[GREEN]);
d[BLUE] = DIVIDE_BY_DIAMETER(acc[BLUE]);
d += step;
for (k = 0; k < 4; k++)
acc[k] -= ls[k];
ls += step;
}
// Right part
for (x = right; x; x--)
{
divider = x + right;
d[ALPHA] = acc[ALPHA] / divider;
d[RED] = acc[RED] / divider;
d[GREEN] = acc[GREEN] / divider;
d[BLUE] = acc[BLUE] / divider;
d += step;
for (k = 0; k < 4; k++)
acc[k] -= ls[k];
ls += step;
}
}
static void
_box_blur_horiz_rgba(DATA32 *src, DATA32 *dst, int radius, int w, int h)
{
int y;
int step = sizeof(DATA32);
DEBUG_TIME_BEGIN();
for (y = 0; y < h; y++)
{
_box_blur_step_rgba(src, dst, radius, w, step);
src += w;
dst += w;
}
DEBUG_TIME_END();
}
static void
_box_blur_vert_rgba(DATA32 *src, DATA32 *dst, int radius, int w, int h)
{
int x;
int step = w * sizeof(DATA32);
DEBUG_TIME_BEGIN();
for (x = 0; x < w; x++)
{
_box_blur_step_rgba(src, dst, radius, h, step);
src += 1;
dst += 1;
}
DEBUG_TIME_END();
}
static Eina_Bool
_box_blur_horiz_apply_rgba(Evas_Filter_Command *cmd)
{
RGBA_Image *in, *out;
unsigned int r;
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input->backing, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output->backing, EINA_FALSE);
r = abs(cmd->blur.dx);
in = cmd->input->backing;
out = cmd->output->backing;
EINA_SAFETY_ON_NULL_RETURN_VAL(in->image.data, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(out->image.data, EINA_FALSE);
EINA_SAFETY_ON_FALSE_RETURN_VAL(out->cache_entry.w >= (2*r + 1), EINA_FALSE);
_box_blur_horiz_rgba(in->image.data, out->image.data, r,
in->cache_entry.w, in->cache_entry.h);
return EINA_TRUE;
}
static Eina_Bool
_box_blur_vert_apply_rgba(Evas_Filter_Command *cmd)
{
RGBA_Image *in, *out;
unsigned int r;
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input->backing, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output->backing, EINA_FALSE);
r = abs(cmd->blur.dy);
in = cmd->input->backing;
out = cmd->output->backing;
EINA_SAFETY_ON_NULL_RETURN_VAL(in->image.data, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(out->image.data, EINA_FALSE);
EINA_SAFETY_ON_FALSE_RETURN_VAL(out->cache_entry.h >= (2*r + 1), EINA_FALSE);
_box_blur_vert_rgba(in->image.data, out->image.data, r,
in->cache_entry.w, in->cache_entry.h);
return EINA_TRUE;
}
/* Alpha only functions */
/* Box blur */
static void
_box_blur_step_alpha(DATA8 *src, DATA8 *dst, int radius, int len, int step)
{
int k;
int acc = 0;
DATA8 *sr = src, *sl = src, *d = dst;
DEFINE_DIAMETER(radius);
int left = MIN(radius, len);
int right = MIN(radius, (len - radius));
for (k = left; k; k--)
{
acc += *sr;
sr += step;
}
for (k = 0; k < left; k++)
{
acc += *sr;
*d = acc / (k + left + 1);
sr += step;
d += step;
}
for (k = len - (2 * radius); k; k--)
{
acc += *sr;
*d = DIVIDE_BY_DIAMETER(acc);
acc -= *sl;
sl += step;
sr += step;
d += step;
}
for (k = right; k; k--)
{
*d = acc / (k + right);
acc -= *sl;
d += step;
sl += step;
}
}
static void
_box_blur_horiz_alpha(DATA8 *src, DATA8 *dst, int radius, int w, int h)
{
int k;
DEBUG_TIME_BEGIN();
for (k = h; k; k--)
{
_box_blur_step_alpha(src, dst, radius, w, 1);
dst += w;
src += w;
}
DEBUG_TIME_END();
}
static void
_box_blur_vert_alpha(DATA8 *src, DATA8 *dst, int radius, int w, int h)
{
int k;
DEBUG_TIME_BEGIN();
for (k = w; k; k--)
{
_box_blur_step_alpha(src, dst, radius, h, w);
dst += 1;
src += 1;
}
DEBUG_TIME_END();
}
static Eina_Bool
_box_blur_horiz_apply_alpha(Evas_Filter_Command *cmd)
{
RGBA_Image *in, *out;
unsigned int r;
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input->backing, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output->backing, EINA_FALSE);
r = abs(cmd->blur.dx);
in = cmd->input->backing;
out = cmd->output->backing;
EINA_SAFETY_ON_NULL_RETURN_VAL(in->mask.data, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(out->mask.data, EINA_FALSE);
EINA_SAFETY_ON_FALSE_RETURN_VAL(out->cache_entry.w >= (2*r + 1), EINA_FALSE);
_box_blur_horiz_alpha(in->mask.data, out->mask.data, r,
in->cache_entry.w, in->cache_entry.h);
return EINA_TRUE;
}
static Eina_Bool
_box_blur_vert_apply_alpha(Evas_Filter_Command *cmd)
{
RGBA_Image *in, *out;
unsigned int r;
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input->backing, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output->backing, EINA_FALSE);
r = abs(cmd->blur.dy);
in = cmd->input->backing;
out = cmd->output->backing;
EINA_SAFETY_ON_NULL_RETURN_VAL(in->mask.data, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(out->mask.data, EINA_FALSE);
EINA_SAFETY_ON_FALSE_RETURN_VAL(out->cache_entry.h >= (2*r + 1), EINA_FALSE);
_box_blur_vert_alpha(in->mask.data, out->mask.data, r,
in->cache_entry.w, in->cache_entry.h);
return EINA_TRUE;
}
/* Gaussian blur */
static void
_gaussian_blur_weights_get(int *weights, int *pow2_divider, int radius)
{
int even[radius + 1];
int odd[radius + 1];
int k, j;
EINA_SAFETY_ON_FALSE_RETURN(radius >= 0 && radius <= 12);
/* Uses Pascal's Triangle to compute the integer gaussian weights:
*
* 0 1 / 1 [1]
* 1 1 [1 1]
* 1 1 2 1 / 4 [1 2]
* 1 3 3 1 [1 3 3]
* 2 1 4 6 4 1 / 16 [1 4 6]
* 1 ..................1
*
* Limitation: max radius is 12 when using 32 bits integers:
* pow2_divider = 24, leaving exactly 8 bits for the data
*/
if (pow2_divider)
*pow2_divider = radius * 2;
memset(odd, 0, sizeof(odd));
memset(even, 0, sizeof(even));
odd[0] = 1;
even[0] = 1;
for (k = 1; k <= radius; k++)
{
for (j = 1; j <= k; j++)
odd[j] = even[j] + even[j - 1];
odd[k] = 2 * even[k - 1];
for (j = 1; j <= k; j++)
even[j] = odd[j] + odd[j - 1];
}
for (k = 0; k <= radius; k++)
weights[k] = odd[k];
for (k = 0; k <= radius; k++)
weights[k + radius] = weights[radius - k];
}
static void
_sin_blur_weights_get(int *weights, int *pow2_divider, int radius)
{
const int diameter = 2 * radius + 1;
double x, divider, sum = 0.0;
double dweights[diameter];
int k, nextpow2, isum = 0;
const int FAKE_PI = 3.0;
/* Base curve:
* f(x) = sin(x+pi/2)/2+1/2
*/
for (k = 0; k < diameter; k++)
{
x = ((double) k / (double) (diameter - 1)) * FAKE_PI * 2.0 - FAKE_PI;
dweights[k] = ((sin(x + M_PI_2) + 1.0) / 2.0) * 1024.0;
sum += dweights[k];
}
// Now we need to normalize to have a 2^N divider.
nextpow2 = log2(2 * sum);
divider = (double) (1 << nextpow2);
for (k = 0; k < diameter; k++)
{
weights[k] = round(dweights[k] * divider / sum);
isum += weights[k];
}
// Final correction. The difference SHOULD be small...
weights[radius] += (int) divider - isum;
if (pow2_divider)
*pow2_divider = nextpow2;
}
static void
_gaussian_blur_step_alpha(DATA8 *src, DATA8 *dst, int radius, int len, int step,
int *weights, int pow2_divider)
{
int j, k, acc, divider;
DATA8 *s = src;
const int diameter = 2 * radius + 1;
int left = MIN(radius, len);
int right = MIN(radius, (len - radius));
// left
for (k = 0; k < left; k++, dst += step)
{
acc = 0;
divider = 0;
s = src;
for (j = 0; j <= k + radius; j++, s += step)
{
acc += (*s) * weights[j + radius - k];
divider += weights[j + radius - k];
}
*dst = acc / divider;
}
// middle
for (k = radius; k < (len - radius); k++, src += step, dst += step)
{
acc = 0;
s = src;
for (j = 0; j < diameter; j++, s += step)
acc += (*s) * weights[j];
*dst = acc >> pow2_divider;
}
// right
for (k = 0; k < right; k++, dst += step, src += step)
{
acc = 0;
divider = 0;
s = src;
for (j = 0; j < 2 * radius - k; j++, s += step)
{
acc += (*s) * weights[j];
divider += weights[j];
}
*dst = acc / divider;
}
}
static void
_gaussian_blur_step_rgba(DATA32 *src, DATA32 *dst, int radius, int len, int step,
int *weights, int pow2_divider)
{
const int diameter = 2 * radius + 1;
int left = MIN(radius, len);
int right = MIN(radius, (len - radius));
int j, k;
// left
for (k = 0; k < left; k++, dst += step)
{
int acc[4] = {0};
int divider = 0;
DATA32 *s = src;
for (j = 0; j <= k + radius; j++, s += step)
{
const int weightidx = j + radius - k;
acc[ALPHA] += A_VAL(s) * weights[weightidx];
acc[RED] += R_VAL(s) * weights[weightidx];
acc[GREEN] += G_VAL(s) * weights[weightidx];
acc[BLUE] += B_VAL(s) * weights[weightidx];
divider += weights[weightidx];
}
A_VAL(dst) = acc[ALPHA] / divider;
R_VAL(dst) = acc[RED] / divider;
G_VAL(dst) = acc[GREEN] / divider;
B_VAL(dst) = acc[BLUE] / divider;
}
// middle
for (k = len - (2 * radius); k > 0; k--, src += step, dst += step)
{
int acc[4] = {0};
DATA32 *s = src;
for (j = 0; j < diameter; j++, s += step)
{
acc[ALPHA] += A_VAL(s) * weights[j];
acc[RED] += R_VAL(s) * weights[j];
acc[GREEN] += G_VAL(s) * weights[j];
acc[BLUE] += B_VAL(s) * weights[j];
}
A_VAL(dst) = acc[ALPHA] >> pow2_divider;
R_VAL(dst) = acc[RED] >> pow2_divider;
G_VAL(dst) = acc[GREEN] >> pow2_divider;
B_VAL(dst) = acc[BLUE] >> pow2_divider;
}
// right
for (k = 0; k < right; k++, dst += step, src += step)
{
int acc[4] = {0};
int divider = 0;
DATA32 *s = src;
for (j = 0; j < 2 * radius - k; j++, s += step)
{
acc[ALPHA] += A_VAL(s) * weights[j];
acc[RED] += R_VAL(s) * weights[j];
acc[GREEN] += G_VAL(s) * weights[j];
acc[BLUE] += B_VAL(s) * weights[j];
divider += weights[j];
}
A_VAL(dst) = acc[ALPHA] / divider;
R_VAL(dst) = acc[RED] / divider;
G_VAL(dst) = acc[GREEN] / divider;
B_VAL(dst) = acc[BLUE] / divider;
}
}
static void
_gaussian_blur_horiz_alpha(DATA8 *src, DATA8 *dst, int radius, int w, int h)
{
int *weights;
int k, pow2_div;
weights = alloca((2 * radius + 1) * sizeof(int));
if (radius <= MAX_GAUSSIAN_RADIUS)
_gaussian_blur_weights_get(weights, &pow2_div, radius);
else
_sin_blur_weights_get(weights, &pow2_div, radius);
for (k = h; k; k--)
{
_gaussian_blur_step_alpha(src, dst, radius, w, 1, weights, pow2_div);
dst += w;
src += w;
}
}
static void
_gaussian_blur_vert_alpha(DATA8 *src, DATA8 *dst, int radius, int w, int h)
{
int *weights;
int k, pow2_div;
weights = alloca((2 * radius + 1) * sizeof(int));
if (radius <= MAX_GAUSSIAN_RADIUS)
_gaussian_blur_weights_get(weights, &pow2_div, radius);
else
_sin_blur_weights_get(weights, &pow2_div, radius);
for (k = w; k; k--)
{
_gaussian_blur_step_alpha(src, dst, radius, h, w, weights, pow2_div);
dst += 1;
src += 1;
}
}
static void
_gaussian_blur_horiz_rgba(DATA32 *src, DATA32 *dst, int radius, int w, int h)
{
int *weights;
int k, pow2_div;
weights = alloca((2 * radius + 1) * sizeof(int));
if (radius <= MAX_GAUSSIAN_RADIUS)
_gaussian_blur_weights_get(weights, &pow2_div, radius);
else
_sin_blur_weights_get(weights, &pow2_div, radius);
for (k = h; k; k--)
{
_gaussian_blur_step_rgba(src, dst, radius, w, 1, weights, pow2_div);
dst += w;
src += w;
}
}
static void
_gaussian_blur_vert_rgba(DATA32 *src, DATA32 *dst, int radius, int w, int h)
{
int *weights;
int k, pow2_div;
weights = alloca((2 * radius + 1) * sizeof(int));
if (radius <= MAX_GAUSSIAN_RADIUS)
_gaussian_blur_weights_get(weights, &pow2_div, radius);
else
_sin_blur_weights_get(weights, &pow2_div, radius);
for (k = w; k; k--)
{
_gaussian_blur_step_rgba(src, dst, radius, h, w, weights, pow2_div);
dst += 1;
src += 1;
}
}
static Eina_Bool
_gaussian_blur_horiz_apply_alpha(Evas_Filter_Command *cmd)
{
RGBA_Image *in, *out;
unsigned int r;
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input->backing, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output->backing, EINA_FALSE);
r = abs(cmd->blur.dx);
in = cmd->input->backing;
out = cmd->output->backing;
EINA_SAFETY_ON_NULL_RETURN_VAL(in->mask.data, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(out->mask.data, EINA_FALSE);
EINA_SAFETY_ON_FALSE_RETURN_VAL(out->cache_entry.w >= (2*r + 1), EINA_FALSE);
_gaussian_blur_horiz_alpha(in->mask.data, out->mask.data, r,
in->cache_entry.w, in->cache_entry.h);
return EINA_TRUE;
}
static Eina_Bool
_gaussian_blur_vert_apply_alpha(Evas_Filter_Command *cmd)
{
RGBA_Image *in, *out;
unsigned int r;
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input->backing, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output->backing, EINA_FALSE);
r = abs(cmd->blur.dy);
in = cmd->input->backing;
out = cmd->output->backing;
EINA_SAFETY_ON_NULL_RETURN_VAL(in->mask.data, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(out->mask.data, EINA_FALSE);
EINA_SAFETY_ON_FALSE_RETURN_VAL(out->cache_entry.h >= (2*r + 1), EINA_FALSE);
_gaussian_blur_vert_alpha(in->mask.data, out->mask.data, r,
in->cache_entry.w, in->cache_entry.h);
return EINA_TRUE;
}
static Eina_Bool
_gaussian_blur_horiz_apply_rgba(Evas_Filter_Command *cmd)
{
RGBA_Image *in, *out;
unsigned int r;
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input->backing, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output->backing, EINA_FALSE);
r = abs(cmd->blur.dx);
in = cmd->input->backing;
out = cmd->output->backing;
EINA_SAFETY_ON_NULL_RETURN_VAL(in->image.data, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(out->image.data, EINA_FALSE);
EINA_SAFETY_ON_FALSE_RETURN_VAL(out->cache_entry.w >= (2*r + 1), EINA_FALSE);
_gaussian_blur_horiz_rgba(in->image.data, out->image.data, r,
in->cache_entry.w, in->cache_entry.h);
return EINA_TRUE;
}
static Eina_Bool
_gaussian_blur_vert_apply_rgba(Evas_Filter_Command *cmd)
{
RGBA_Image *in, *out;
unsigned int r;
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input->backing, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output->backing, EINA_FALSE);
r = abs(cmd->blur.dy);
in = cmd->input->backing;
out = cmd->output->backing;
EINA_SAFETY_ON_NULL_RETURN_VAL(in->image.data, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(out->image.data, EINA_FALSE);
EINA_SAFETY_ON_FALSE_RETURN_VAL(out->cache_entry.h >= (2*r + 1), EINA_FALSE);
_gaussian_blur_vert_rgba(in->image.data, out->image.data, r,
in->cache_entry.w, in->cache_entry.h);
return EINA_TRUE;
}
/* Main entry point */
Evas_Filter_Apply_Func
evas_filter_blur_cpu_func_get(Evas_Filter_Command *cmd)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, NULL);
EINA_SAFETY_ON_FALSE_RETURN_VAL(cmd->mode == EVAS_FILTER_MODE_BLUR, NULL);
switch (cmd->blur.type)
{
case EVAS_FILTER_BLUR_BOX:
if (!cmd->input->alpha_only && !cmd->output->alpha_only)
{
if (cmd->blur.dx)
return _box_blur_horiz_apply_rgba;
else if (cmd->blur.dy)
return _box_blur_vert_apply_rgba;
}
else if (cmd->input->alpha_only && cmd->output->alpha_only)
{
if (cmd->blur.dx)
return _box_blur_horiz_apply_alpha;
else if (cmd->blur.dy)
return _box_blur_vert_apply_alpha;
}
CRI("Unsupported operation: mixing RGBA and Alpha surfaces.");
return NULL;
case EVAS_FILTER_BLUR_GAUSSIAN:
if (!cmd->input->alpha_only && !cmd->output->alpha_only)
{
if (cmd->blur.dx)
return _gaussian_blur_horiz_apply_rgba;
else if (cmd->blur.dy)
return _gaussian_blur_vert_apply_rgba;
}
else if (cmd->input->alpha_only && cmd->output->alpha_only)
{
if (cmd->blur.dx)
return _gaussian_blur_horiz_apply_alpha;
else if (cmd->blur.dy)
return _gaussian_blur_vert_apply_alpha;
}
CRI("Unsupported operation: mixing RGBA and Alpha surfaces.");
return NULL;
default:
CRI("Not implemented yet!");
return NULL;
}
}

View File

@ -0,0 +1,428 @@
/**
* @brief Simple bump maps algorithms for the software engine
* @file evas_filter_bump.c
* @author Jean-Philippe ANDRE <jpeg@videolan.org>
*/
#include "evas_common_private.h"
#include "evas_private.h"
#include "evas_filter.h"
#include "evas_filter_private.h"
#include "evas_blend_private.h"
#include <math.h>
#undef ENFN
#undef ENDT
#define ENFN cmd->ctx->evas->engine.func
#define ENDT cmd->ctx->evas->engine.data.output
#ifdef CLAMP
# undef CLAMP
#endif
#define CLAMP(a,b,c) MIN(MAX((b),(a)),(c))
#define DEFAULT_ZANGLE 45.f
static Eina_Bool _bump_map_cpu_alpha_alpha(Evas_Filter_Command *cmd);
static Eina_Bool _bump_map_cpu_alpha_rgba(Evas_Filter_Command *cmd);
static Eina_Bool _bump_map_cpu_rgba_rgba(Evas_Filter_Command *cmd);
Evas_Filter_Apply_Func
evas_filter_bump_map_cpu_func_get(Evas_Filter_Command *cmd)
{
int w, h;
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->mask, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output, NULL);
EINA_SAFETY_ON_FALSE_RETURN_VAL(cmd->input != cmd->output, NULL);
EINA_SAFETY_ON_FALSE_RETURN_VAL(cmd->mask->alpha_only, NULL);
EINA_SAFETY_ON_FALSE_RETURN_VAL((!cmd->output->alpha_only)
|| cmd->input->alpha_only, NULL);
w = cmd->input->w;
h = cmd->input->h;
EINA_SAFETY_ON_FALSE_RETURN_VAL(cmd->output->w == w, NULL);
EINA_SAFETY_ON_FALSE_RETURN_VAL(cmd->output->h == h, NULL);
EINA_SAFETY_ON_FALSE_RETURN_VAL(cmd->mask->w == w, NULL);
EINA_SAFETY_ON_FALSE_RETURN_VAL(cmd->mask->h == h, NULL);
if (cmd->input->alpha_only)
{
if (cmd->output->alpha_only)
return _bump_map_cpu_alpha_alpha;
else
return _bump_map_cpu_alpha_rgba;
}
else
return _bump_map_cpu_rgba_rgba;
}
static void
_phong_alpha_generate(DATA8 *phong, DATA8 dark, DATA8 color, DATA8 white,
float sf)
{
int x, y;
// FIXME: Flat surfaces should be of color COLOR when compensate is set
// FIXME: Include white in the computation for specular light
(void) white;
(void) sf;
/*
float3 lightDir = light.position - pos3D; //3D position in space of the surface
float distance = length( lightDir );
lightDir = lightDir / distance; // = normalize( lightDir );
distance = distance * distance; //This line may be optimised using Inverse square root
//Intensity of the diffuse light. Saturate to keep within the 0-1 range.
float NdotL = dot( normal, lightDir );
float intensity = saturate( NdotL );
// Calculate the diffuse light factoring in light color, power and the attenuation
OUT.Diffuse = intensity * light.diffuseColor * light.diffusePower / distance;
//Calculate the half vector between the light vector and the view vector.
//This is faster than calculating the actual reflective vector.
float3 H = normalize( lightDir + viewDir );
//Intensity of the specular light
float NdotH = dot( normal, H );
intensity = pow( saturate( NdotH ), specularHardness );
//Sum up the specular light factoring
OUT.Specular = intensity * light.specularColor * light.specularPower / distance;
*/
for (y = 0; y < 256; y++)
for (x = 0; x < 256; x++)
{
float dx = (127.5 - x);
float dy = (127.5 - y);
float dist = sqrt(dx*dx + dy*dy) * 2.;
int diff = dark + MAX(255 - dist, 0) * (color - dark) / 255;
int spec = 0; // TODO
phong[x + (y << 8)] = MIN(MAX(diff + spec, 0), 255);
}
}
static Eina_Bool
_bump_map_cpu_alpha_alpha(Evas_Filter_Command *cmd)
{
DATA8 *src, *map, *dst, *map_y1, *map_y2;
DATA8 dark, color, white;
DATA8 *phong;
int x, y, w, h, lx, ly;
float xyangle, zangle, sf, lxy;
w = cmd->input->w;
h = cmd->input->h;
EINA_SAFETY_ON_FALSE_RETURN_VAL(w > 2 && h > 2, EINA_FALSE);
src = ((RGBA_Image *) cmd->input->backing)->mask.data;
map = ((RGBA_Image *) cmd->mask->backing)->mask.data;
dst = ((RGBA_Image *) cmd->output->backing)->mask.data;
EINA_SAFETY_ON_NULL_RETURN_VAL(src, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(map, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(dst, EINA_FALSE);
xyangle = cmd->bump.xyangle;
zangle = cmd->bump.zangle;
sf = cmd->bump.specular_factor;
dark = cmd->bump.dark >> 24;
white = cmd->bump.white >> 24;
color = cmd->bump.color >> 24;
// Convenience for alpha output only
if ((!dark && !white && !color) ||
(dark == 0xff && white == 0xff && color == 0xff))
{
INF("Bump colors are all 0 or 255. Using low byte instead of alpha.");
dark = cmd->bump.dark & 0xff;
white = cmd->bump.white & 0xff;
color = cmd->bump.color & 0xff;
}
// Compute appropriate lx, ly
if (abs(zangle) >= 90)
{
WRN("Z angle was defined as %.0f, out of range. Defaults to %.0f.",
zangle, DEFAULT_ZANGLE);
zangle = DEFAULT_ZANGLE;
}
lxy = sin(abs(zangle * M_PI / 180.));
lx = (int) (40.f * (lxy + 1.0) * cos(xyangle * M_PI / 180.));
ly = (int) (40.f * (lxy + 1.0) * sin(xyangle * M_PI / 180.));
INF("Using light vector (%d,%d)", lx, ly);
// Generate light table
phong = malloc(256 * 256 * sizeof(*phong));
EINA_SAFETY_ON_NULL_RETURN_VAL(phong, EINA_FALSE);
_phong_alpha_generate(phong, dark, color, white, sf);
for (y = 0; y < h; y++)
{
int gx, gy, vx, vy;
if (!y)
{
map_y1 = map;
map_y2 = map + w;
}
else if (y == (h - 1))
{
map_y1 = map - w;
map_y2 = map;
}
else
{
map_y1 = map - w;
map_y2 = map + w;
}
// x = 0
gx = (map[1] - map[0]) / 2;
gy = (*map_y2 - *map_y1) / 2;
vx = gx + lx + 127;
vy = (-gy) + ly + 127;
//printf("dx,dy: %d,%d, lx,ly: %d,%d, vx,vy: %d,%d\n", gx, gy, lx, ly, vx, vy);
if ((vx & 0xFF00) || (vy & 0xFF00))
*dst = *src * dark;
else
*dst = (*src * phong[(vy << 8) + vx]) >> 8;
dst++, src++, map_y1++, map_y2++;
for (x = 1; x < (w - 1); x++, map++, map_y1++, map_y2++, src++, dst++)
{
// note: map is one pixel left of (x,y)
if (!*src)
{
*dst = 0;
continue;
}
// compute gradient (gx, gy). this gives us the normal vector
gx = (map[2] - map[0]) / 2;
gy = (*map_y2 - *map_y1) / 2;
// compute halfway vector between light and gradient vectors
vx = gx + lx + 127;
vy = (-gy) + ly + 127;
// take light from the phong table
if ((vx & 0xFF00) || (vy & 0xFF00))
*dst = *src * dark;
else
*dst = (*src * phong[(vy << 8) + vx]) >> 8;
}
// x = (w - 1)
gx = (map[1] - map[0]) / 2;
gy = (*map_y2 - *map_y1) / 2;
vx = gx + lx + 127;
vy = (-gy) + ly + 127;
if ((vx & 0xFF00) || (vy & 0xFF00))
*dst = *src * dark;
else
*dst = (*src * phong[(vy << 8) + vx]) >> 8;
map += 2;
dst++;
src++;
}
free(phong);
return EINA_TRUE;
}
static Eina_Bool
_bump_map_cpu_alpha_rgba(Evas_Filter_Command *cmd)
{
DATA8 *src, *map, *map_y1, *map_y2;
DATA32 *dst;
DATA32 dark, color, white, col;
//DATA32 *phong;
Eina_Bool compensate;
int x, y, w, h, lx, ly, lz, gz, NL, diffusion, gzlz, gz2;
double xyangle, zangle, sf, lxy, elevation;
w = cmd->input->w;
h = cmd->input->h;
EINA_SAFETY_ON_FALSE_RETURN_VAL(w > 2 && h > 2, EINA_FALSE);
src = ((RGBA_Image *) cmd->input->backing)->mask.data;
map = ((RGBA_Image *) cmd->mask->backing)->mask.data;
dst = ((RGBA_Image *) cmd->output->backing)->image.data;
EINA_SAFETY_ON_NULL_RETURN_VAL(src, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(map, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(dst, EINA_FALSE);
xyangle = cmd->bump.xyangle;
zangle = cmd->bump.zangle;
sf = cmd->bump.specular_factor;
compensate = cmd->bump.compensate;
elevation = cmd->bump.elevation;
dark = cmd->bump.dark;
white = cmd->bump.white;
color = cmd->bump.color;
// Compute appropriate lx, ly
if (abs(zangle) >= 90)
{
WRN("Z angle was defined as %.0f, out of range. Defaults to %.0f.",
zangle, DEFAULT_ZANGLE);
zangle = DEFAULT_ZANGLE;
}
lxy = 255. * cos(zangle * M_PI / 180.);
lx = (int) (lxy * cos(xyangle * M_PI / 180.));
ly = (int) (lxy * sin(xyangle * M_PI / 180.));
lz = (int) (255. * sin(zangle));
INF("Using light vector (%d,%d,%d)", lx, ly, lz);
if (elevation <= 0)
{
WRN("Invalid elevation value of %.0f, using 10 instead.", elevation);
elevation = 10.0;
}
gz = (6*255) / elevation;
gzlz = gz * lz;
gz2 = gz * gz;
// Generate light table
// FIXME: phong LUT not used (we need two)
//phong = malloc(256 * 256 * sizeof(*phong));
//EINA_SAFETY_ON_NULL_RETURN_VAL(phong, EINA_FALSE);
//_phong_rgba_generate(phong, 1.5, sf, 20, dark, color, white);
// FIXME: x=0 and x=w-1 are NOT implemented.
for (y = 0; y < h; y++)
{
int gx, gy;
if (!y)
{
map_y1 = map;
map_y2 = map + w;
}
else if (y == (h - 1))
{
map_y1 = map - w;
map_y2 = map;
}
else
{
map_y1 = map - w;
map_y2 = map + w;
}
for (x = 0; x < w; x++, dst++, src++, map++, map_y1++, map_y2++)
{
if (!*src) continue;
/* Color intensity is defined as:
* I = Kd*N.L*Cd + Ks*N.H*Cs
* Where Ks and Kd are 1 in this implementation
* And Cs is white, Cd is color
*/
/* Compute N.L
* N = (gx,gy,gz)
* L = (lx,ly,lz) |L| = 255
*/
if (EINA_LIKELY(x && (x < (w - 1))))
{
gx = map[-1] + map_y1[-1] + map_y2[-1] - map[1] - map_y1[1] - map_y2[1];
gy = map_y2[-1] + map_y2[0] + map_y2[1] - map_y1[-1] - map_y1[0] - map_y1[1];
}
else if (!x)
{
gx = map[0] + map_y1[0] + map_y2[0] - map[1] - map_y1[1] - map_y2[1];
gy = map_y2[0] + map_y2[1] + map_y2[1] - map_y1[0] - map_y1[1] - map_y1[1];
}
else
{
gx = map[-1] + map_y1[-1] + map_y2[-1] - map[0] - map_y1[0] - map_y2[0];
gy = map_y2[-1] + map_y2[0] + map_y2[0] - map_y1[-1] - map_y1[0] - map_y1[0];
}
NL = gx*lx + gy*ly + gzlz;
if (0 && NL < 0)
{
// TODO: Check this
diffusion = lz;
}
else
{
int g2 = gx*gx + gy*gy + gz2;
diffusion = NL / sqrt(MAX(g2, 1));
//diffusion += MAX(0, lz - diffusion);
}
if (compensate)
diffusion = diffusion * 255 / lz;
diffusion = CLAMP(1, diffusion + 1, 256);
col = INTERP_256(diffusion, color, dark);
if (sf > 0)
{
/* Compute N.H
* H = (L+V) / |L+V|
* V = (0,0,255)
* L = (lx,ly,lz) |L| = 255
*/
// FIXME: All these doubles :)
int shine;
const double hnorm = sqrt(lx*lx + ly*ly + (lz+255)*(lz+255));
const double hx = (double) lx / hnorm;
const double hy = (double) ly / hnorm;
const double hz = (double) (lz+255) / hnorm;
double NHx = hx*gx / 255.0;
double NHy = hy*gy / 255.0;
double nz = sqrt(255.0*255.0 - gx*gx - gy*gy);
double NHz = (hz*nz) / 255.0;
double NH = NHx + NHy + NHz;
double specular = NH > 0 ? pow(NH, sf) : 0;
if (compensate)
{
const double basespecular = pow(hz, sf);
shine = (specular - basespecular) * 255.0 / (1.0 - basespecular);
}
else shine = specular * 255.0;
col = INTERP_256(CLAMP(1, shine + 1, 256), white, col);
}
*dst = INTERP_256(*src + 1, col, *dst);
}
}
return EINA_TRUE;
}
static Eina_Bool
_bump_map_cpu_rgba_rgba(Evas_Filter_Command *cmd)
{
(void) cmd;
CRI("Not implemented yet.");
return EINA_FALSE;
}

View File

@ -0,0 +1,125 @@
#include "evas_filter.h"
#include "evas_filter_private.h"
static Eina_Bool
_filter_curve_cpu_rgba(Evas_Filter_Command *cmd)
{
RGBA_Image *in, *out;
DATA32 *src, *dst, *d, *s;
DATA8 *curve;
int k, offset = -1, len;
#define C_VAL(p) (((DATA8 *)(p))[offset])
in = cmd->input->backing;
out = cmd->output->backing;
EINA_SAFETY_ON_NULL_RETURN_VAL(in, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(out, EINA_FALSE);
src = in->image.data;
dst = out->image.data;
EINA_SAFETY_ON_NULL_RETURN_VAL(src, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(dst, EINA_FALSE);
curve = cmd->curve.data;
len = in->cache_entry.w * in->cache_entry.h;
switch (cmd->curve.channel)
{
#ifndef WORDS_BIGENDIAN
case EVAS_FILTER_CHANNEL_RED: offset = 2; break;
case EVAS_FILTER_CHANNEL_GREEN: offset = 1; break;
case EVAS_FILTER_CHANNEL_BLUE: offset = 0; break;
#else
case EVAS_FILTER_CHANNEL_RED: offset = 1; break;
case EVAS_FILTER_CHANNEL_GREEN: offset = 2; break;
case EVAS_FILTER_CHANNEL_BLUE: offset = 3; break;
#endif
case EVAS_FILTER_CHANNEL_ALPHA: break;
case EVAS_FILTER_CHANNEL_RGB: break;
default:
ERR("Invalid color channel %d", (int) cmd->curve.channel);
return EINA_FALSE;
}
// One channel
if (offset >= 0)
{
for (k = len; k; k--, dst++, src++)
C_VAL(dst) = curve[C_VAL(src)];
return EINA_TRUE;
}
// RGB
if (cmd->curve.channel == EVAS_FILTER_CHANNEL_RGB)
{
#ifndef WORDS_BIGENDIAN
for (offset = 0; offset <= 2; offset++)
#else
for (offset = 1; offset <= 3; offset++)
#endif
{
for (k = len, s = src, d = dst; k; k--, d++, s++)
C_VAL(d) = curve[C_VAL(s)];
}
return EINA_TRUE;
}
// Alpha
#ifndef WORDS_BIGENDIAN
offset = 3;
#else
offset = 0;
#endif
if (src != dst)
memcpy(dst, src, len * sizeof(DATA32));
evas_data_argb_unpremul(dst, len);
for (k = len, d = dst; k; k--, d++, src++)
C_VAL(d) = curve[C_VAL(src)];
evas_data_argb_premul(dst, len);
return EINA_TRUE;
}
static Eina_Bool
_filter_curve_cpu_alpha(Evas_Filter_Command *cmd)
{
RGBA_Image *in, *out;
DATA8 *src, *dst;
DATA8 *curve;
int k;
in = cmd->input->backing;
out = cmd->output->backing;
EINA_SAFETY_ON_NULL_RETURN_VAL(in, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(out, EINA_FALSE);
src = in->mask.data;
dst = out->mask.data;
EINA_SAFETY_ON_NULL_RETURN_VAL(src, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(dst, EINA_FALSE);
curve = cmd->curve.data;
for (k = in->cache_entry.w * in->cache_entry.h; k; k--)
*dst++ = curve[*src++];
return EINA_TRUE;
}
Evas_Filter_Apply_Func
evas_filter_curve_cpu_func_get(Evas_Filter_Command *cmd)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input, NULL);
EINA_SAFETY_ON_FALSE_RETURN_VAL((cmd->input->w == cmd->output->w)
&& (cmd->input->h == cmd->output->h), 0);
if (!cmd->input->alpha_only && !cmd->output->alpha_only)
return _filter_curve_cpu_rgba;
if (cmd->input->alpha_only && cmd->output->alpha_only)
return _filter_curve_cpu_alpha;
CRI("Incompatible image formats");
return NULL;
}

View File

@ -0,0 +1,273 @@
#include "evas_filter.h"
#include "evas_filter_private.h"
static void
_filter_displace_cpu_alpha_do(int w, int h, int map_w, int map_h, int intensity,
DATA8 *src, DATA8 *dst, DATA8 *map_start,
Eina_Bool stretch, Eina_Bool smooth,
Eina_Bool blend)
{
int x, y, map_x, map_y;
const int map_stride = map_w * sizeof(DATA32);
const int dx = RED;
const int dy = GREEN;
DATA8 *map;
for (y = 0, map_y = 0; y < h; y++, map_y++)
{
if (map_y >= map_h) map_y = 0;
map = map_start + (map_y * map_stride);
for (x = 0, map_x = 0; x < w; x++, dst++, src++, map_x++)
{
int offx = 0, offy = 0, offx_dec = 0, offy_dec = 0, val = 0;
Eina_Bool out = 0;
// wrap around (x)
if (map_x >= map_w)
{
map_x = 0;
map = map_start + (map_y * map_stride);
}
else map += sizeof(DATA32);
// x
val = ((int) map[dx] - 128) * intensity;
offx = val >> 7;
offx_dec = val & 0x7f;
if ((x + offx) < 0) { offx = -x; out = 1; }
if ((x + offx + 1) >= w) { offx = w - x - 2; out = 1; }
// y
val = ((int) map[dy] - 128) * intensity;
offy = val >> 7;
offy_dec = val & 0x7f;
if ((y + offy) < 0) { offy = -y; out = 1; }
if ((y + offy + 1) >= h) { offy = h - y - 2; out = 1; }
// get value
if (out && !stretch)
val = 0;
else
{
if (!smooth)
val = src[offx + offy * w];
else
{
val = src[offx + offy * w] * (128 - offx_dec) * (128 - offy_dec);
val += src[offx + 1 + offy * w] * offx_dec * (128 - offy_dec);
val += src[offx + (offy + 1) * w] * (128 - offx_dec) * offy_dec;
val += src[offx + 1 + (offy + 1) * w] * offx_dec * offy_dec;
val = val >> 14; // <=> *dst = val / (128 * 128)
}
}
// apply alpha
if (map[ALPHA] != 255)
val = (val * map[ALPHA]) / 255;
// write to dest
if (blend)
*dst = (*dst * (255 - val)) / 255 + val;
else
*dst = val;
}
}
}
static void
_filter_displace_cpu_rgba_do(int w, int h, int map_w, int map_h, int intensity,
DATA32 *src, DATA32 *dst, DATA8 *map_start,
Eina_Bool stretch, Eina_Bool smooth,
Eina_Bool blend)
{
int x, y, map_x, map_y;
const int map_stride = sizeof(DATA32) * map_w;
const int dx = RED;
const int dy = GREEN;
DATA8 *map;
for (y = 0, map_y = 0; y < h; y++, map_y++)
{
if (map_y >= map_h) map_y = 0;
map = map_start + (map_y * map_stride);
for (x = 0, map_x = 0; x < w; x++, dst++, src++, map_x++)
{
int offx = 0, offy = 0, offx_dec = 0, offy_dec = 0, val = 0;
Eina_Bool out = 0;
// wrap (x)
if (map_x >= map_w)
{
map_x = 0;
map = map_start + (map_y * map_stride);
}
else map += sizeof(DATA32);
// x
val = ((int) map[dx] - 128) * intensity;
offx = val >> 7;
offx_dec = val & 0x7f;
if ((x + offx) < 0) { offx = -x; out = 1; }
if ((x + offx + 1) >= w) { offx = w - x - 2; out = 1; }
// y
val = ((int) map[dy] - 128) * intensity;
offy = val >> 7;
offy_dec = val & 0x7f;
if ((y + offy) < 0) { offy = -y; out = 1; }
if ((y + offy + 1) >= h) { offy = h - y - 2; out = 1; }
// get value
if (out && !stretch)
val = A_VAL(src + offx + offy * w) << (ALPHA * 8);
else if (!smooth)
val = src[offx + offy * w];
else
{
int R, G, B, A;
DATA32 s00, s01, s10, s11; // indexes represent x,y
int mul00, mul01, mul10, mul11;
mul00 = (128 - offx_dec) * (128 * offy_dec);
mul01 = (128 - offx_dec) * offy_dec;
mul10 = offx_dec * (128 - offy_dec);
mul11 = offx_dec * offy_dec;
s00 = src[offx + offy * w];
s01 = src[offx + (offy + 1) * w];
s10 = src[offx + 1 + offy * w];
s11 = src[offx + 1 + (offy + 1) * w];
A = (ALPHA_OF(s00) * mul00) + (ALPHA_OF(s10) * mul10)
+ (ALPHA_OF(s01) * mul01) + (ALPHA_OF(s11) * mul11);
R = (RED_OF(s00) * mul00) + (RED_OF(s10) * mul10)
+ (RED_OF(s01) * mul01) + (RED_OF(s11) * mul11);
G = (GREEN_OF(s00) * mul00) + (GREEN_OF(s10) * mul10)
+ (GREEN_OF(s01) * mul01) + (GREEN_OF(s11) * mul11);
B = (BLUE_OF(s00) * mul00) + (BLUE_OF(s10) * mul10)
+ (BLUE_OF(s01) * mul01) + (BLUE_OF(s11) * mul11);
R >>= 14;
G >>= 14;
B >>= 14;
val = ARGB_JOIN(A, R, G, B);
}
// apply alpha
if (blend && map[ALPHA] != 0xFF)
*dst = INTERP_256(map[ALPHA], val, *dst);
else
*dst = val;
}
}
}
/**
* Apply distortion map on alpha image
* input: alpha
* output: alpha
* map: rg+a (rgba)
*/
static Eina_Bool
_filter_displace_cpu_alpha(Evas_Filter_Command *cmd)
{
int w, h, map_w, map_h, intensity;
DATA8 *dst, *src, *map_start;
Eina_Bool stretch, smooth, blend;
w = cmd->input->w;
h = cmd->input->h;
EINA_SAFETY_ON_FALSE_RETURN_VAL(w == cmd->output->w, EINA_FALSE);
EINA_SAFETY_ON_FALSE_RETURN_VAL(h == cmd->output->h, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input->backing, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->mask->backing, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output->backing, EINA_FALSE);
src = ((RGBA_Image *) cmd->input->backing)->mask.data;
map_start = ((RGBA_Image *) cmd->mask->backing)->mask.data;
dst = ((RGBA_Image *) cmd->output->backing)->mask.data;
EINA_SAFETY_ON_NULL_RETURN_VAL(src, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(map_start, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(dst, EINA_FALSE);
stretch = cmd->displacement.flags & EVAS_FILTER_DISPLACE_STRETCH;
smooth = cmd->displacement.flags & EVAS_FILTER_DISPLACE_LINEAR;
map_w = cmd->mask->w;
map_h = cmd->mask->h;
intensity = cmd->displacement.intensity;
blend = (cmd->draw.render_op == EVAS_RENDER_BLEND);
_filter_displace_cpu_alpha_do(w, h, map_w, map_h, intensity,
src, dst, map_start, stretch, smooth, blend);
return EINA_TRUE;
}
/**
* Apply distortion map on rgba image
* input: rgba
* output: rgba
* map: rg+a (rgba)
*/
static Eina_Bool
_filter_displace_cpu_rgba(Evas_Filter_Command *cmd)
{
int w, h, map_w, map_h, intensity;
DATA32 *dst, *src;
DATA8 *map_start;
Eina_Bool stretch, smooth, blend;
w = cmd->input->w;
h = cmd->input->h;
EINA_SAFETY_ON_FALSE_RETURN_VAL(w == cmd->output->w, EINA_FALSE);
EINA_SAFETY_ON_FALSE_RETURN_VAL(h == cmd->output->h, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input->backing, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->mask->backing, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output->backing, EINA_FALSE);
src = ((RGBA_Image *) cmd->input->backing)->image.data;
map_start = ((RGBA_Image *) cmd->mask->backing)->mask.data;
dst = ((RGBA_Image *) cmd->output->backing)->image.data;
EINA_SAFETY_ON_NULL_RETURN_VAL(src, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(map_start, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(dst, EINA_FALSE);
stretch = cmd->displacement.flags & EVAS_FILTER_DISPLACE_STRETCH;
smooth = cmd->displacement.flags & EVAS_FILTER_DISPLACE_LINEAR;
map_w = cmd->mask->w;
map_h = cmd->mask->h;
intensity = cmd->displacement.intensity;
blend = (cmd->draw.render_op == EVAS_RENDER_BLEND);
_filter_displace_cpu_rgba_do(w, h, map_w, map_h, intensity,
src, dst, map_start, stretch, smooth, blend);
return EINA_TRUE;
}
Evas_Filter_Apply_Func
evas_filter_displace_cpu_func_get(Evas_Filter_Command *cmd)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->mask, NULL);
EINA_SAFETY_ON_FALSE_RETURN_VAL(!cmd->mask->alpha_only, NULL);
if (cmd->input->alpha_only != cmd->output->alpha_only)
{
CRI("Invalid color formats");
return NULL;
}
if (cmd->input->alpha_only)
return _filter_displace_cpu_alpha;
else
return _filter_displace_cpu_rgba;
}

View File

@ -0,0 +1,397 @@
/* Implementation of some masking functions for the software engine */
#include "evas_common_private.h"
#include "evas_private.h"
#include "evas_filter.h"
#include "evas_filter_private.h"
#include "evas_blend_private.h"
// Naming convention: _func_engine_incolor_maskcolor_outcolor()
static Eina_Bool _mask_cpu_alpha_alpha_alpha(Evas_Filter_Command *cmd);
static Eina_Bool _mask_cpu_alpha_rgba_rgba(Evas_Filter_Command *cmd);
static Eina_Bool _mask_cpu_alpha_alpha_rgba(Evas_Filter_Command *cmd);
static Eina_Bool _mask_cpu_rgba_alpha_rgba(Evas_Filter_Command *cmd);
static Eina_Bool _mask_cpu_rgba_rgba_rgba(Evas_Filter_Command *cmd);
Evas_Filter_Apply_Func
evas_filter_mask_cpu_func_get(Evas_Filter_Command *cmd)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->mask, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input->backing, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output->backing, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->mask->backing, NULL);
EINA_SAFETY_ON_FALSE_RETURN_VAL(cmd->input->w == cmd->output->w, NULL);
EINA_SAFETY_ON_FALSE_RETURN_VAL(cmd->input->h == cmd->output->h, NULL);
if (cmd->input->alpha_only)
{
if (cmd->mask->alpha_only && cmd->output->alpha_only)
return _mask_cpu_alpha_alpha_alpha;
else if (!cmd->mask->alpha_only && !cmd->output->alpha_only)
return _mask_cpu_alpha_rgba_rgba;
else if (cmd->mask->alpha_only && !cmd->output->alpha_only)
return _mask_cpu_alpha_alpha_rgba;
}
else
{
if (cmd->mask->alpha_only && !cmd->output->alpha_only)
return _mask_cpu_rgba_alpha_rgba;
else if (!cmd->mask->alpha_only && !cmd->output->alpha_only)
return _mask_cpu_rgba_rgba_rgba;
}
CRI("If input or mask is RGBA, then output must also be RGBA: %s [%s] %s",
cmd->input->alpha_only ? "alpha" : "rgba",
cmd->mask->alpha_only ? "alpha" : "rgba",
cmd->output->alpha_only ? "alpha" : "rgba");
return NULL;
}
static Eina_Bool
_mask_cpu_alpha_alpha_alpha(Evas_Filter_Command *cmd)
{
Alpha_Gfx_Func func;
RGBA_Image *in, *out, *mask;
DATA8 *src, *dst, *msk;
int render_op = cmd->draw.render_op;
int w, h, mw, mh, x, y, my;
int stepsize, stepcount, step;
/* Mechanism:
* 1. Stretch mask as requested in fillmode
* 2. Copy source to destination
* 3. Render mask into destination using alpha function
*
* FIXME: Could probably be optimized into a single op :)
*/
in = (RGBA_Image *) cmd->input->backing;
out = (RGBA_Image *) cmd->output->backing;
mask = (RGBA_Image *) cmd->mask->backing;
w = cmd->input->w;
h = cmd->input->h;
mw = cmd->mask->w;
mh = cmd->mask->h;
src = in->mask.data;
dst = out->mask.data;
EINA_SAFETY_ON_FALSE_RETURN_VAL((w > 0) && (mw > 0), EINA_FALSE);
stepsize = MIN(mw, w);
stepcount = w / stepsize;
// Stretch if necessary.
if ((mw != w || mh != h) && (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_XY))
{
Evas_Filter_Buffer *fb;
if (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_X)
mw = w;
if (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_Y)
mh = h;
BUFFERS_LOCK();
fb = evas_filter_buffer_scaled_get(cmd->ctx, cmd->mask, mw, mh);
BUFFERS_UNLOCK();
EINA_SAFETY_ON_NULL_RETURN_VAL(fb, EINA_FALSE);
fb->locked = EINA_FALSE;
mask = fb->backing;
}
msk = mask->mask.data;
// First pass: copy to dest
if (src != dst)
memcpy(dst, src, w * h * sizeof(DATA8));
// Second pass: apply render op
func = evas_common_alpha_func_get(render_op);
for (y = 0, my = 0; y < h; y++, my++, msk += mw)
{
if (my >= mh)
{
my = 0;
msk = mask->mask.data;
}
for (step = 0; step < stepcount; step++, dst += stepsize)
func(msk, dst, stepsize);
x = stepsize * stepcount;
if (x < w)
{
func(msk, dst, w - x);
dst += w - x;
}
}
return EINA_TRUE;
}
static Eina_Bool
_mask_cpu_rgba_alpha_rgba(Evas_Filter_Command *cmd)
{
Evas_Filter_Buffer *fb;
Eina_Bool ok;
/* Mechanism:
* 1. Swap input and mask
* 2. Apply mask operation for alpha+rgba+rgba
* 3. Swap input and mask
*/
fb = cmd->input;
cmd->input = cmd->mask;
cmd->mask = fb;
ok = _mask_cpu_alpha_rgba_rgba(cmd);
fb = cmd->input;
cmd->input = cmd->mask;
cmd->mask = fb;
return ok;
}
static Eina_Bool
_mask_cpu_alpha_rgba_rgba(Evas_Filter_Command *cmd)
{
RGBA_Gfx_Func func1, func2;
RGBA_Image *in, *out, *mask;
DATA8 *src;
DATA32 *dst, *msk, *span;
int op = cmd->draw.render_op;
int w, h, mw, mh, y, my, r;
int stepsize, stepcount, step;
DATA32 color2;
/* Mechanism:
* 1. Stretch mask as requested in fillmode
* 2. Render mask to span using input as mask
* 3. Render span into destination
*
* FIXME: Could probably be optimized into a single op :)
*/
in = (RGBA_Image *) cmd->input->backing;
out = (RGBA_Image *) cmd->output->backing;
mask = (RGBA_Image *) cmd->mask->backing;
w = cmd->input->w;
h = cmd->input->h;
mw = cmd->mask->w;
mh = cmd->mask->h;
src = in->mask.data;
dst = out->image.data;
// Stretch if necessary.
if ((mw != w || mh != h) && (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_XY))
{
Evas_Filter_Buffer *fb;
if (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_X)
mw = w;
if (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_Y)
mh = h;
BUFFERS_LOCK();
fb = evas_filter_buffer_scaled_get(cmd->ctx, cmd->mask, mw, mh);
BUFFERS_UNLOCK();
EINA_SAFETY_ON_NULL_RETURN_VAL(fb, EINA_FALSE);
fb->locked = EINA_FALSE;
mask = fb->backing;
}
color2 = ARGB_JOIN(cmd->draw.A, cmd->draw.R, cmd->draw.G, cmd->draw.B);
msk = mask->image.data;
EINA_SAFETY_ON_NULL_RETURN_VAL(src, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(dst, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(msk, EINA_FALSE);
EINA_SAFETY_ON_FALSE_RETURN_VAL((w > 0) && (mw > 0), EINA_FALSE);
stepsize = MIN(mw, w);
stepcount = w / stepsize;
span = malloc(stepsize * sizeof(DATA32));
func1 = evas_common_gfx_func_composite_pixel_mask_span_get(mask, out, 1, EVAS_RENDER_COPY);
func2 = evas_common_gfx_func_composite_pixel_color_span_get(mask, color2, out, 1, op);
// Apply mask using Gfx functions
for (y = 0, my = 0; y < h; y++, my++, msk += mw)
{
if (my >= mh)
{
my = 0;
msk = mask->image.data;
}
for (step = 0; step < stepcount; step++, dst += stepsize, src += stepsize)
{
memset(span, 0, stepsize * sizeof(DATA32));
func1(msk, src, 0, span, stepsize);
func2(span, NULL, color2, dst, stepsize);
}
r = w - (stepsize * stepcount);
if (r > 0)
{
memset(span, 0, r * sizeof(DATA32));
func1(msk, src, 0, span, r);
func2(span, NULL, color2, dst, r);
dst += r;
src += r;
}
}
free(span);
return EINA_TRUE;
}
static Eina_Bool
_mask_cpu_alpha_alpha_rgba(Evas_Filter_Command *cmd)
{
RGBA_Gfx_Func func;
Alpha_Gfx_Func span_func;
RGBA_Image *in, *out, *mask;
DATA8 *src, *msk, *span;
DATA32 *dst;
DATA32 color;
int op = cmd->draw.render_op;
int w, h, mw, mh, y, my, r;
int stepsize, stepcount, step;
/* Mechanism:
* 1. Copy mask to span buffer (1 line)
* 2. Multiply source by span (so that: span = mask * source)
* 3. Render span to destination using color (blend)
*
* FIXME: Could probably be optimized into a single op :)
*/
in = (RGBA_Image *) cmd->input->backing;
out = (RGBA_Image *) cmd->output->backing;
mask = (RGBA_Image *) cmd->mask->backing;
w = cmd->input->w;
h = cmd->input->h;
mw = cmd->mask->w;
mh = cmd->mask->h;
src = in->mask.data;
dst = out->image.data;
color = ARGB_JOIN(cmd->draw.A, cmd->draw.R, cmd->draw.G, cmd->draw.B);
EINA_SAFETY_ON_FALSE_RETURN_VAL((w > 0) && (mw > 0), EINA_FALSE);
// Stretch if necessary.
if ((mw != w || mh != h) && (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_XY))
{
Evas_Filter_Buffer *fb;
if (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_X)
mw = w;
if (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_Y)
mh = h;
BUFFERS_LOCK();
fb = evas_filter_buffer_scaled_get(cmd->ctx, cmd->mask, mw, mh);
BUFFERS_UNLOCK();
EINA_SAFETY_ON_NULL_RETURN_VAL(fb, EINA_FALSE);
fb->locked = EINA_FALSE;
mask = fb->backing;
}
msk = mask->mask.data;
stepsize = MIN(mw, w);
stepcount = w / stepsize;
span = malloc(stepsize * sizeof(DATA8));
func = evas_common_gfx_func_composite_mask_color_span_get(color, out, 1, op);
span_func = evas_common_alpha_func_get(EVAS_RENDER_MASK);
for (y = 0, my = 0; y < h; y++, my++, msk += mw)
{
if (my >= mh)
{
my = 0;
msk = mask->mask.data;
}
for (step = 0; step < stepcount; step++, dst += stepsize, src += stepsize)
{
memcpy(span, msk, stepsize * sizeof(DATA8));
span_func(src, span, stepsize);
func(NULL, span, color, dst, stepsize);
}
r = w - (stepsize * stepcount);
if (r > 0)
{
memcpy(span, msk, r * sizeof(DATA8));
span_func(src, span, r);
func(NULL, span, color, dst, r);
dst += r;
src += r;
}
}
free(span);
return EINA_TRUE;
}
static Eina_Bool
_mask_cpu_rgba_rgba_rgba(Evas_Filter_Command *cmd)
{
Evas_Filter_Command fake_cmd;
Evas_Filter_Apply_Func blend;
Evas_Filter_Buffer *fb;
int w, h;
fake_cmd = *cmd;
w = cmd->input->w;
h = cmd->input->h;
/* Blend 2 rgba images into rgba destination.
* Mechanism:
* 1. Copy input to temp (COPY)
* 2. Blend mask to temp (MUL)
* 3. Blend temp to output (render_op)
*/
// Copy
BUFFERS_LOCK();
fb = evas_filter_buffer_scaled_get(cmd->ctx, cmd->input, w, h);
BUFFERS_UNLOCK();
EINA_SAFETY_ON_NULL_RETURN_VAL(fb, EINA_FALSE);
fb->locked = EINA_TRUE;
// Mask --> Temp
fake_cmd.input = cmd->mask;
fake_cmd.mask = NULL;
fake_cmd.output = fb;
fake_cmd.draw.render_op = EVAS_RENDER_MUL;
blend = evas_filter_blend_cpu_func_get(&fake_cmd);
EINA_SAFETY_ON_NULL_RETURN_VAL(blend, EINA_FALSE);
blend(&fake_cmd);
// Temp --> Output
fake_cmd.draw.render_op = EVAS_RENDER_BLEND;
fake_cmd.input = fb;
fake_cmd.output = cmd->output;
blend = evas_filter_blend_cpu_func_get(&fake_cmd);
EINA_SAFETY_ON_NULL_RETURN_VAL(blend, EINA_FALSE);
blend(&fake_cmd);
fb->locked = EINA_FALSE;
return EINA_TRUE;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,197 @@
#ifndef EVAS_FILTER_PRIVATE_H
#define EVAS_FILTER_PRIVATE_H
#include "evas_filter.h"
#include "evas_private.h"
// This is a potential optimization.
#define DIV_USING_BITSHIFT 1
#ifdef LITTLE_ENDIAN
#define ALPHA 3
#define RGB0 0
#define RGB3 3
#define RED 2
#define GREEN 1
#define BLUE 0
#else
#define ALPHA 0
#define RGB0 1
#define RGB3 4
#define RED 0
#define GREEN 1
#define BLUE 2
#endif
// RGBA = (((a) << 24) + ((r) << 16) + ((g) << 8) + (b))
#define ALPHA_OF(a) ((a) >> 24)
#define RED_OF(a) (((a) >> 16) & 0xff)
#define GREEN_OF(a) (((a) >> 8) & 0xff)
#define BLUE_OF(a) ((a) & 0xff)
// Helpers
#define ENFN ctx->evas->engine.func
#define ENDT ctx->evas->engine.data.output
#define BUFFERS_LOCK() do { if (cmd->input) cmd->input->locked = 1; if (cmd->output) cmd->output->locked = 1; if (cmd->mask) cmd->mask->locked = 1; } while (0)
#define BUFFERS_UNLOCK() do { if (cmd->input) cmd->input->locked = 0; if (cmd->output) cmd->output->locked = 0; if (cmd->mask) cmd->mask->locked = 0; } while (0)
typedef enum _Evas_Filter_Interpolation_Mode Evas_Filter_Interpolation_Mode;
struct _Evas_Filter_Context
{
Evas_Public_Data *evas;
Eina_Inlist *commands;
Eina_List *buffers; // Evas_Filter_Buffer *
int last_buffer_id;
int last_command_id;
// Variables changing at each run
int w, h; // Dimensions of the input/output buffers
int padl, padt, padr, padb; // Padding in the current input/output buffers
struct
{
/** Post-processing callback. The context can be safely destroyed here. */
Evas_Filter_Cb cb;
void *data;
} post_run;
struct
{
int bufid;
void *context;
int x, y;
} target;
Eina_Bool async : 1;
Eina_Bool gl_engine : 1;
Eina_Bool running : 1;
};
struct _Evas_Filter_Command
{
EINA_INLIST;
int id;
Evas_Filter_Mode mode;
Evas_Filter_Context *ctx;
Evas_Filter_Buffer *input;
Evas_Filter_Buffer *mask;
Evas_Filter_Buffer *output;
union
{
struct
{
int dx, dy;
Evas_Filter_Blur_Type type;
} blur;
struct
{
DATA8 *data; // Pointer to 256 char array
Evas_Filter_Channel channel;
} curve;
struct
{
// mask contains the map data
Evas_Filter_Displacement_Flags flags;
int intensity; // Max displacement in pixels
} displacement;
struct
{
float xyangle; // in degrees: 0-360 (modulo)
float zangle; // degrees: 0-90 (defaults to 0)
float specular_factor; // range TBD: 0-...
float elevation;
DATA32 dark;
DATA32 color;
DATA32 white;
Eina_Bool compensate : 1; // Compensate for darkening
//Eina_Bool specular : 1; // Use specular light as well (needs specular_factor > 0)
} bump;
struct
{
Evas_Filter_Transform_Flags flags;
} transform;
};
struct {
int render_op;
int R, G, B, A;
int ox, oy;
union {
struct {
int x, y, w, h;
};
struct {
int l, r, t, b;
};
} clip;
Evas_Filter_Fill_Mode fillmode;
Eina_Bool clip_use : 1;
Eina_Bool clip_mode_lrtb : 1;
Eina_Bool need_temp_buffer : 1;
} draw;
};
struct _Evas_Filter_Buffer
{
EINA_REFCOUNT;
int id;
Evas_Filter_Context *ctx;
Evas_Object *source;
Eina_Stringshare *source_name;
void *backing;
void *glimage;
int w, h;
Evas_Object *proxy;
Eina_Bool alpha_only : 1; // 1 channel (A) instead of 4 (RGBA)
Eina_Bool allocated : 1; // allocated on demand, belongs to this context
Eina_Bool transient : 1; // temporary buffer (automatic allocation)
Eina_Bool locked : 1; // internal flag
Eina_Bool stolen : 1; // stolen by the client
Eina_Bool delete_me : 1; // request delete asap (after released by client)
};
enum _Evas_Filter_Interpolation_Mode
{
EVAS_FILTER_INTERPOLATION_MODE_NONE,
EVAS_FILTER_INTERPOLATION_MODE_LINEAR
};
void evas_filter_context_clear(Evas_Filter_Context *ctx);
void evas_filter_context_source_set(Evas_Filter_Context *ctx, Evas_Object *eo_proxy, Evas_Object *eo_source, int bufid, Eina_Stringshare *name);
/* FIXME: CPU filters entry points. Move these to the Evas Engine itself. */
Evas_Filter_Apply_Func evas_filter_blend_cpu_func_get(Evas_Filter_Command *cmd);
Evas_Filter_Apply_Func evas_filter_blur_cpu_func_get(Evas_Filter_Command *cmd);
Evas_Filter_Apply_Func evas_filter_bump_map_cpu_func_get(Evas_Filter_Command *cmd);
Evas_Filter_Apply_Func evas_filter_curve_cpu_func_get(Evas_Filter_Command *cmd);
Evas_Filter_Apply_Func evas_filter_displace_cpu_func_get(Evas_Filter_Command *cmd);
Evas_Filter_Apply_Func evas_filter_fill_cpu_func_get(Evas_Filter_Command *cmd);
Evas_Filter_Apply_Func evas_filter_mask_cpu_func_get(Evas_Filter_Command *cmd);
Evas_Filter_Apply_Func evas_filter_transform_cpu_func_get(Evas_Filter_Command *cmd);
/* Utility functions */
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);
Eina_Bool evas_filter_buffer_alloc(Evas_Filter_Buffer *fb, int w, int h);
Evas_Filter_Buffer *_filter_buffer_get(Evas_Filter_Context *ctx, int bufid);
Eina_Bool _filter_buffer_data_set(Evas_Filter_Context *ctx, int bufid, void *data, int w, int h, Eina_Bool alpha_only);
Evas_Filter_Buffer *_filter_buffer_data_new(Evas_Filter_Context *ctx, void *data, int w, int h, Eina_Bool alpha_only);
#define evas_filter_buffer_alloc_new(ctx, w, h, a) _filter_buffer_data_new(ctx, NULL, w, h, a)
Evas_Filter_Buffer *evas_filter_temporary_buffer_get(Evas_Filter_Context *ctx, int w, int h, Eina_Bool alpha_only);
Evas_Filter_Buffer *evas_filter_buffer_scaled_get(Evas_Filter_Context *ctx, Evas_Filter_Buffer *src, unsigned w, unsigned h);
Eina_Bool evas_filter_interpolate(DATA8* output /* 256 values */, DATA8* points /* pairs x + y */, int point_count, Evas_Filter_Interpolation_Mode mode);
Evas_Filter_Command *_evas_filter_command_get(Evas_Filter_Context *ctx, int cmdid);
#endif // EVAS_FILTER_PRIVATE_H

View File

@ -0,0 +1,86 @@
#include "evas_filter_private.h"
static Eina_Bool
_vflip_cpu(Evas_Filter_Command *cmd)
{
size_t datasize, stride;
DATA8 *in, *out, *span;
int w, h, sy, dy, oy, center, t, b, objh;
int s0, s1, d0, d1;
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input->backing, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output->backing, EINA_FALSE);
w = cmd->input->w;
h = cmd->input->h;
EINA_SAFETY_ON_FALSE_RETURN_VAL(cmd->output->w == w, EINA_FALSE);
EINA_SAFETY_ON_FALSE_RETURN_VAL(cmd->output->h == h, EINA_FALSE);
EINA_SAFETY_ON_FALSE_RETURN_VAL(cmd->output->alpha_only == cmd->input->alpha_only, EINA_FALSE);
in = ((RGBA_Image *) cmd->input->backing)->mask.data;
out = ((RGBA_Image *) cmd->output->backing)->mask.data;
datasize = cmd->input->alpha_only ? sizeof(DATA8) : sizeof(DATA32);
stride = w * datasize;
oy = cmd->draw.oy;
t = cmd->ctx->padt;
b = cmd->ctx->padb;
objh = h - t - b;
center = t + objh / 2 + oy;
s0 = t;
s1 = h - b - 1;
if (oy >= 0)
{
d0 = center + (objh / 2) + oy;
d1 = center - (objh / 2) - oy;
}
else
{
d0 = center + (objh / 2) - oy;
d1 = center - (objh / 2) + oy;
}
if (in == out)
{
span = malloc(stride);
if (!span) return EINA_FALSE;
}
for (sy = s0, dy = d0; (dy >= d1) && (sy <= s1); sy++, dy--)
{
DATA8* src = in + stride * sy;
DATA8* dst = out + stride * dy;
if (in == out)
{
if (src == dst) break;
memcpy(span, dst, stride);
memcpy(dst, src, stride);
memcpy(src, span, stride);
if (sy >= center) break;
}
else
memcpy(dst, src, stride);
}
return EINA_TRUE;
}
Evas_Filter_Apply_Func
evas_filter_transform_cpu_func_get(Evas_Filter_Command *cmd)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, NULL);
switch (cmd->transform.flags)
{
case EVAS_FILTER_TRANSFORM_VFLIP:
return _vflip_cpu;
default:
CRI("Unknown transform flag %d", (int) cmd->transform.flags);
return NULL;
}
}

View File

@ -0,0 +1,121 @@
/** @file evas_filter_utils.c
* @brief Utility functions for the filters.
*/
#include "evas_filter_private.h"
Evas_Filter_Buffer *
evas_filter_buffer_scaled_get(Evas_Filter_Context *ctx,
Evas_Filter_Buffer *src,
unsigned w, unsigned h)
{
Evas_Filter_Buffer *fb;
Image_Entry *dstdata = NULL;
Image_Entry *srcdata;
void *drawctx;
srcdata = evas_filter_buffer_backing_get(ctx, src->id);
EINA_SAFETY_ON_NULL_RETURN_VAL(srcdata, NULL);
fb = evas_filter_temporary_buffer_get(ctx, w, h, src->alpha_only);
if (!fb) return NULL;
dstdata = evas_filter_buffer_backing_get(ctx, fb->id);
if (!dstdata)
{
CRI("No backing found for buffer %d", fb->id);
return NULL;
}
if ((dstdata->w != w) || (dstdata->h != h))
{
CRI("Buffer size mismatch: got %dx%d requested %dx%d",
dstdata->w, dstdata->h, w, h);
return NULL;
}
// FIXME: Not supported on GL engine.
// Yeah, we need to call the CPU scaling functions and not the engine.
if (ctx->gl_engine)
CRI("Support for stretching not implemened yet for GL.");
if (!src->alpha_only)
{
drawctx = ENFN->context_new(ENDT);
ENFN->context_color_set(ENDT, drawctx, 255, 255, 255, 255);
ENFN->context_render_op_set(ENDT, drawctx, EVAS_RENDER_COPY);
ENFN->image_draw(ENDT, drawctx, dstdata, srcdata,
0, 0, src->w, src->h, // src
0, 0, w, h, // dst
EINA_TRUE, // smooth
EINA_FALSE); // Not async
ENFN->context_free(ENDT, drawctx);
}
else
{
// FIXME: How to scale alpha buffer?
// For now, we could draw to RGBA, scale from there, draw back to alpha
CRI("Alpha buffer scaling is not supported");
return NULL;
}
return fb;
}
static Eina_Bool
_interpolate_none(DATA8 *output, DATA8 *points, int point_count)
{
int j, k, val, x1, x2;
for (j = 0; j < point_count; j++)
{
x1 = points[j * 2];
val = points[j * 2 + 1];
if (j < (point_count - 1))
x2 = points[(j + 1) * 2];
else
x2 = 256;
if (x2 < x1) return EINA_FALSE;
for (k = x1; k < x2; k++)
output[k] = val;
}
return EINA_TRUE;
}
static Eina_Bool
_interpolate_linear(DATA8 *output, DATA8 *points, int point_count)
{
int j, k, val1, val2, x1, x2;
for (j = 0; j < point_count; j++)
{
x1 = points[j * 2];
val1 = points[j * 2 + 1];
if (j < (point_count - 1))
{
x2 = points[(j + 1) * 2];
val2 = points[(j + 1) * 2 + 1];
}
else
{
x2 = 256;
val2 = val1;
}
if (x2 < x1) return EINA_FALSE;
for (k = x1; k < x2; k++)
output[k] = val1 + ((val2 - val1) * (k - x1)) / (x2 - x1);
}
return EINA_TRUE;
}
Eina_Bool
evas_filter_interpolate(DATA8 *output, DATA8 *points, int point_count,
Evas_Filter_Interpolation_Mode mode)
{
switch (mode)
{
case EVAS_FILTER_INTERPOLATION_MODE_NONE:
return _interpolate_none(output, points, point_count);
case EVAS_FILTER_INTERPOLATION_MODE_LINEAR:
default:
return _interpolate_linear(output, points, point_count);
}
}

View File

@ -0,0 +1,252 @@
#ifndef _EVAS_FILTER_H
#define _EVAS_FILTER_H
#include "evas_common_private.h"
#include "evas_private.h"
typedef struct _Evas_Filter_Context Evas_Filter_Context;
typedef struct _Evas_Filter_Command Evas_Filter_Command;
typedef struct _Evas_Filter_Program Evas_Filter_Program;
typedef struct _Evas_Filter_Instruction Evas_Filter_Instruction;
typedef struct _Evas_Filter_Buffer Evas_Filter_Buffer;
typedef struct _Evas_Filter_Proxy_Binding Evas_Filter_Proxy_Binding;
typedef enum _Evas_Filter_Mode Evas_Filter_Mode;
typedef enum _Evas_Filter_Blur_Type Evas_Filter_Blur_Type;
typedef enum _Evas_Filter_Channel Evas_Filter_Channel;
typedef enum _Evas_Filter_Displacement_Flags Evas_Filter_Displacement_Flags;
typedef enum _Evas_Filter_Bump_Flags Evas_Filter_Bump_Flags;
typedef enum _Evas_Filter_Fill_Mode Evas_Filter_Fill_Mode;
typedef enum _Evas_Filter_Transform_Flags Evas_Filter_Transform_Flags;
typedef Eina_Bool (* Evas_Filter_Apply_Func) (Evas_Filter_Command *cmd);
typedef void (* Evas_Filter_Cb) (Evas_Filter_Context *ctx, void *data);
#define EVAS_FILTER_BUFFER_RGBA EINA_FALSE
#define EVAS_FILTER_BUFFER_ALPHA EINA_TRUE
#define EVAS_FILTER_BUFFER_INPUT_ID 1
#define EVAS_FILTER_BUFFER_OUTPUT_ID 2
enum _Evas_Filter_Mode
{
EVAS_FILTER_MODE_BLEND, /**< Blend with current context render_op */
EVAS_FILTER_MODE_BLUR, /**< @see Evas_Filter_Blur_Type */
EVAS_FILTER_MODE_CURVE, /**< Apply color curve */
EVAS_FILTER_MODE_DISPLACE, /**< Apply XY displacement based on RG mask */
EVAS_FILTER_MODE_FILL, /**< Fill a buffer with a solid color */
EVAS_FILTER_MODE_MASK, /**< Apply Alpha or RGBA texture on image */
EVAS_FILTER_MODE_BUMP, /**< Apply bump mapping (light effect) */
EVAS_FILTER_MODE_TRANSFORM, /**< Apply a simple geometrical transformation */
EVAS_FILTER_MODE_LAST
};
enum _Evas_Filter_Blur_Type
{
EVAS_FILTER_BLUR_GAUSSIAN = 0x0, // Gaussian or sine curve. O(nm)
EVAS_FILTER_BLUR_BOX = 0x1, // Optimizable on CPU. But, UGLY. O(n)
EVAS_FILTER_BLUR_LAST,
EVAS_FILTER_BLUR_DEFAULT = EVAS_FILTER_BLUR_GAUSSIAN
};
enum _Evas_Filter_Channel
{
EVAS_FILTER_CHANNEL_ALPHA = 0,
EVAS_FILTER_CHANNEL_RED = 1,
EVAS_FILTER_CHANNEL_GREEN = 2,
EVAS_FILTER_CHANNEL_BLUE = 3,
EVAS_FILTER_CHANNEL_RGB = 4
};
enum _Evas_Filter_Displacement_Flags
{
EVAS_FILTER_DISPLACE_NEAREST = 0x0, /**< Interpolate between pixels (linear interpolation) */
EVAS_FILTER_DISPLACE_LINEAR = 0x1, /**< Interpolate between pixels (linear interpolation) */
EVAS_FILTER_DISPLACE_BLACK = 0x0, /**< Use black (or transparent) when going out of bounds) */
EVAS_FILTER_DISPLACE_STRETCH = 0x2, /**< Stretch border pixels when going out of bounds */
EVAS_FILTER_DISPLACE_BITMASK = 0x3
};
enum _Evas_Filter_Bump_Flags
{
EVAS_FILTER_BUMP_NORMAL = 0x0,
EVAS_FILTER_BUMP_COMPENSATE = 0x1 /**< Compensate for darkening (diffuse light) or brightening (specular light) of zero gradient surfaces */
};
enum _Evas_Filter_Fill_Mode
{
EVAS_FILTER_FILL_MODE_NONE = 0x0,
EVAS_FILTER_FILL_MODE_STRETCH_X = 0x1,
EVAS_FILTER_FILL_MODE_STRETCH_Y = 0x2,
EVAS_FILTER_FILL_MODE_REPEAT_X = 0x4,
EVAS_FILTER_FILL_MODE_REPEAT_Y = 0x8,
EVAS_FILTER_FILL_MODE_REPEAT_X_STRETCH_Y = EVAS_FILTER_FILL_MODE_REPEAT_X | EVAS_FILTER_FILL_MODE_STRETCH_Y,
EVAS_FILTER_FILL_MODE_REPEAT_Y_STRETCH_X = EVAS_FILTER_FILL_MODE_REPEAT_Y | EVAS_FILTER_FILL_MODE_STRETCH_X,
EVAS_FILTER_FILL_MODE_REPEAT_XY = EVAS_FILTER_FILL_MODE_REPEAT_X | EVAS_FILTER_FILL_MODE_REPEAT_Y,
EVAS_FILTER_FILL_MODE_STRETCH_XY = EVAS_FILTER_FILL_MODE_STRETCH_X | EVAS_FILTER_FILL_MODE_STRETCH_Y
};
enum _Evas_Filter_Transform_Flags
{
EVAS_FILTER_TRANSFORM_VFLIP = 1
};
/* Parser stuff (high level API) */
Evas_Filter_Program *evas_filter_program_new(const char *name);
Eina_Bool evas_filter_program_parse(Evas_Filter_Program *pgm, const char *str);
void evas_filter_program_del(Evas_Filter_Program *pgm);
Eina_Bool evas_filter_context_program_use(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm);
Eina_Bool evas_filter_program_padding_get(Evas_Filter_Program *pgm, int *l, int *r, int *t, int *b);
//void evas_filter_program_source_set(Evas_Filter_Program *pgm, const char *name, Evas_Object *object);
void evas_filter_program_source_set_all(Evas_Filter_Program *pgm, Eina_Hash *sources);
void evas_filter_context_proxy_render_all(Evas_Filter_Context *ctx, Eo *eo_obj, Eina_Bool do_async);
/* Filter context (low level) */
Evas_Filter_Context *evas_filter_context_new(Evas_Public_Data *evas, Eina_Bool async);
void evas_filter_context_destroy(Evas_Filter_Context *ctx);
void evas_filter_context_post_run_callback_set(Evas_Filter_Context *ctx, Evas_Filter_Cb cb, void *data);
#define evas_filter_context_autodestroy(ctx) evas_filter_context_post_run_callback_set(ctx, ((Evas_Filter_Cb) evas_filter_context_destroy), ctx)
Eina_Bool evas_filter_context_buffers_allocate_all(Evas_Filter_Context *ctx, unsigned w, unsigned h);
int evas_filter_buffer_empty_new(Evas_Filter_Context *ctx, Eina_Bool alpha_only);
int evas_filter_buffer_image_new(Evas_Filter_Context *ctx, RGBA_Image *image);
void *evas_filter_buffer_backing_get(Evas_Filter_Context *ctx, int bufid);
void *evas_filter_buffer_backing_steal(Evas_Filter_Context *ctx, int bufid);
Eina_Bool evas_filter_buffer_backing_release(Evas_Filter_Context *ctx, void *stolen_buffer);
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_target_set(Evas_Filter_Context *ctx, void *draw_context, void *surface, int x, int y);
/**
* @brief Blend a source buffer into a destination buffer, allowing X,Y offsets, Alpha to RGBA conversion with color
* @param ctx Current filter chain
* @param draw_context Current Evas draw context. Current color is used when inbuf is ALPHA and outbuf is RGBA.
* @param inbuf Source buffer: ALPHA or RGBA
* @param outbuf Destination buffer: ALPHA or RGBA (note: must be RGBA if inbuf is RGBA)
* @param ox X offset in the destination buffer
* @param oy Y offset in the destination buffer
* @param fillmode Specifies whether to repeat or stretch the input onto its destination, and on which axes
* @return Filter command ID or -1 in case of error
*/
int evas_filter_command_blend_add(Evas_Filter_Context *ctx, void *draw_context, int inbuf, int outbuf, int ox, int oy, Evas_Filter_Fill_Mode fillmode);
/**
* @brief Apply a blur effect on a buffer
* @param ctx Current filter chain
* @param draw_context Current Evas draw context. Current color is used when inbuf is ALPHA and outbuf is RGBA.
* @param inbuf Source buffer: ALPHA or RGBA
* @param outbuf Destination buffer: ALPHA or RGBA (note: must be RGBA if inbuf is RGBA)
* @param type Type of blur: BOX, GAUSSIAN or MOTION
* @param dx X radius of blur. Can be negative ONLY for MOTION blur
* @param dy Y radius of blur. Can be negative ONLY for MOTION blur
* @param ox X offset in the destination buffer
* @param oy Y offset in the destination buffer
* @return Filter command ID or -1 in case of error
*/
int evas_filter_command_blur_add(Evas_Filter_Context *ctx, void *draw_context, int inbuf, int outbuf, Evas_Filter_Blur_Type type, int dx, int dy, int ox, int oy);
/**
* @brief Fill a buffer with the current color
* @param ctx Current filter chain
* @param draw_context Current Evas draw context. Current color is used when buf is RGBA, and clip is used to specify the fill area.
* @param buf Buffer: ALPHA or RGBA
* @return Filter command ID or -1 in case of error
* @note The current draw context's render operation is ignored (always uses COPY mode).
*/
int evas_filter_command_fill_add(Evas_Filter_Context *ctx, void *draw_context, int buf);
/**
* @brief evas_filter_command_curve_add
* @param ctx Current filter chain
* @param draw_context Current Evas draw context. Current color is used when buf is RGBA, and clip is used to specify the fill area.
* @param inbuf Input buffer, ALPHA or RGBA.
* @param outbuf Output buffer, must have same colorspace as inbuf.
* @param curve The data points to use, must contain 256 values.
* @param channel Which channel to apply the curve (red, green, blue, alpha or RGB)
* @return Filter command ID or -1 in case of error
*/
int evas_filter_command_curve_add(Evas_Filter_Context *ctx, void *draw_context, int inbuf, int outbuf, DATA8 *curve /* 256 elements */, Evas_Filter_Channel channel);
/**
* @brief Grow/Shrink an image, as defined in image processing (this is not a scale algorithm!)
* @param ctx Current filter chain
* @param draw_context Current Evas draw context. Current color is used when inbuf is ALPHA and outbuf is RGBA.
* @param inbuf Source buffer: ALPHA or RGBA
* @param outbuf Destination buffer: ALPHA or RGBA (note: must be RGBA if inbuf is RGBA)
* @param radius Number of pixels to grow by. If negative, shrink instead of grow
* @param smooth Use smooth blur and curve for grow (default: true)
* @return Filter command ID or -1 in case of error
*/
int evas_filter_command_grow_add(Evas_Filter_Context *ctx, void *draw_context, int inbuf, int outbuf, int radius, Eina_Bool smooth);
/**
* @brief Apply a displacement map to a buffer. This will move pixels from the source to the destination based on pixel per pixel offset, as defined in the displacement map
* @param ctx Current filter chain
* @param draw_context Current Evas draw context (ignored)
* @param inbuf Input buffer (Alpha or RGBA)
* @param outbuf Output buffer (Alpha or RGBA), same size as inbuf
* @param dispbuf Displacement map. Should be an RGBA buffer, where the Red and Green channels are the displacement maps for X and Y. Can be also ALPHA buffer, in which case only one dimension can be specified (X or Y).
* @param flags Alters how the map is interpreted, @see Evas_Filter_Displacement_Flags
* @param intensity Maximum offset possible, if the map's value is maximal at this point (ie. 0 or 255)
* @param fillmode Specifies how to repeat and stretch the map to fit the target size
* @return Filter command ID or -1 in case of error
*/
int evas_filter_command_displacement_map_add(Evas_Filter_Context *ctx, void *draw_context, int inbuf, int outbuf, int dispbuf, Evas_Filter_Displacement_Flags flags, int intensity, Evas_Filter_Fill_Mode fillmode);
/**
* @brief Apply a texture to a buffer
* @param ctx Current filter chain
* @param draw_context Current Evas draw context (ignored)
* @param inbuf Input buffer (Alpha or RGBA)
* @param maskbuf Texture buffer (Alpha or RGBA)
* @param outbuf Output buffer (Alpha or RGBA)
* @param fillmode Specifies how to repeat and stretch the mask to fit the target size
* @return Filter command ID or -1 in case of error
* @note For the moment, inbuf can only be ALPHA, and output must be RGBA if mask is RGBA as well
*/
int evas_filter_command_mask_add(Evas_Filter_Context *ctx, void *draw_context, int inbuf, int maskbuf, int outbuf, Evas_Filter_Fill_Mode fillmode);
/**
* @brief Apply a relief effect based on a bump map (Z map)
* @param ctx Current filter chain
* @param draw_context Current Evas draw context (ignored)
* @param inbuf Input buffer (Alpha or RGBA)
* @param bumpbuf Bump map (Alpha only), same size as inbuf. By definition, lows are black (alpha 0) and highs are white (alpha 255).
* @param outbuf Output buffer (Alpha or RGBA), same size as inbuf
* @param azimuth CCW angle in degrees from the X axis of the light direction. 0 is light from the right, 90 from the top, 180 from the left, 270 from the bottom. All values are valid.
* @param elevation Angle in degrees between the XY plane and the light. Only values from 0 (light is perfectly horizontal) to 90 (light comes from the viewer herself) are acceptable.
* @param depth Max depth in the bump map. Default value is 10.
* @param specular_factor Factor for the specular light effect (shininess). Ranges from 1.0 to 1000+ with logarithmic effects
* @param black Darkest color, defines the ambiant light
* @param color Light's normal color
* @param white Brightest color, used in the shininess effect
* @param flags Optional flags: compensation for darkening
* @param fillmode Specifies how to repeat and stretch the map to fit the target size
* @return Filter command ID or -1 in case of error
*/
int evas_filter_command_bump_map_add(Evas_Filter_Context *ctx, void *draw_context, int inbuf, int bumpbuf, int outbuf, float azimuth, float elevation, float depth, float specular_factor, DATA32 black, DATA32 color, DATA32 white, Evas_Filter_Bump_Flags flags, Evas_Filter_Fill_Mode fillmode);
/**
* @brief Apply a geometrical transformation to the buffer
* @param ctx Current filter chain
* @param draw_context Current Evas draw context (ignored)
* @param inbuf Input buffer (Alpha or RGBA)
* @param outbuf Output buffer (Alpha or RGBA), same size as inbuf
* @param flags Specifies the operation to apply (eg. vflip)
* @param ox X offset
* @param oy Y offset
* @return Filter command ID or -1 in case of error
*/
int evas_filter_command_transform_add(Evas_Filter_Context *ctx, void *draw_context, int inbuf, int outbuf, Evas_Filter_Transform_Flags flags, int ox, int oy);
/* Simple binding between a filter object and its sources */
struct _Evas_Filter_Proxy_Binding
{
Evas_Object *eo_proxy;
Evas_Object *eo_source;
Eina_Stringshare *name;
};
#endif

View File

@ -328,20 +328,21 @@ evas_gl_common_image_new_from_copied_data(Evas_Engine_GL_Context *gc, unsigned i
switch (cspace)
{
case EVAS_COLORSPACE_ARGB8888:
break;
case EVAS_COLORSPACE_GRY8:
break;
case EVAS_COLORSPACE_YCBCR422P601_PL:
case EVAS_COLORSPACE_YCBCR422P709_PL:
if (im->tex) evas_gl_common_texture_free(im->tex, EINA_TRUE);
im->tex = NULL;
im->cs.no_free = 0;
im->cs.no_free = 0;
if (im->im->cache_entry.h > 0)
im->cs.data = calloc(1, im->im->cache_entry.h * sizeof(unsigned char *) * 2);
if ((data) && (im->cs.data))
memcpy(im->cs.data, data, im->im->cache_entry.h * sizeof(unsigned char *) * 2);
break;
memcpy(im->cs.data, data, im->im->cache_entry.h * sizeof(unsigned char *) * 2);
break;
default:
abort();
break;
abort();
break;
}
return im;
}