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:
commit
407eaf029c
|
@ -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
|
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
|
### Engines
|
||||||
|
|
||||||
if EVAS_STATIC_BUILD_SOFTWARE_GENERIC
|
if EVAS_STATIC_BUILD_SOFTWARE_GENERIC
|
||||||
|
|
|
@ -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_source(void);
|
||||||
static void st_collections_group_parts_part_description_text_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_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_layout(void);
|
||||||
static void st_collections_group_parts_part_description_box_align(void);
|
static void st_collections_group_parts_part_description_box_align(void);
|
||||||
static void st_collections_group_parts_part_description_box_padding(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.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.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.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.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.align", st_collections_group_parts_part_description_box_align},
|
||||||
{"collections.group.parts.part.description.box.padding", st_collections_group_parts_part_description_box_padding},
|
{"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.str = STRDUP(ted->text.text.str);
|
||||||
ted->text.text_class = STRDUP(ted->text.text_class);
|
ted->text.text_class = STRDUP(ted->text.text_class);
|
||||||
ted->text.font.str = STRDUP(ted->text.font.str);
|
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_source), &(ted->text.id_source));
|
||||||
data_queue_copied_part_lookup(pc, &(tparent->text.id_text_source), &(ted->text.id_text_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);
|
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}
|
@edcsubsection{collections_group_parts_description_box,Box}
|
||||||
|
|
|
@ -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_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.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.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);
|
EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Edje_Part_Description_Text);
|
||||||
eddc.func.mem_free = mem_free_textblock;
|
eddc.func.mem_free = mem_free_textblock;
|
||||||
|
|
|
@ -1358,10 +1358,11 @@ _edje_file_del(Edje *ed)
|
||||||
else if ((rp->type == EDJE_RP_TYPE_TEXT) &&
|
else if ((rp->type == EDJE_RP_TYPE_TEXT) &&
|
||||||
(rp->typedata.text))
|
(rp->typedata.text))
|
||||||
{
|
{
|
||||||
if (rp->typedata.text->text) eina_stringshare_del(rp->typedata.text->text);
|
eina_stringshare_del(rp->typedata.text->text);
|
||||||
if (rp->typedata.text->font) eina_stringshare_del(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);
|
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->cache.out_str);
|
||||||
|
eina_stringshare_del(rp->typedata.text->filter);
|
||||||
free(rp->typedata.text);
|
free(rp->typedata.text);
|
||||||
}
|
}
|
||||||
else if ((rp->type == EDJE_RP_TYPE_SWALLOW) &&
|
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;
|
text = (Edje_Part_Description_Text *) desc;
|
||||||
|
|
||||||
if (text->text.text.str) eina_stringshare_del(text->text.text.str);
|
eina_stringshare_del(text->text.text.str);
|
||||||
if (text->text.text_class) eina_stringshare_del(text->text.text_class);
|
eina_stringshare_del(text->text.text_class);
|
||||||
if (text->text.style.str) eina_stringshare_del(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.font.str);
|
||||||
|
eina_stringshare_del(text->text.filter.str);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1175,6 +1175,8 @@ struct _Edje_Part_Description_Spec_Text
|
||||||
Edje_String style; /* the text style if a textblock */
|
Edje_String style; /* the text style if a textblock */
|
||||||
Edje_String font; /* if a specific font is asked for */
|
Edje_String font; /* if a specific font is asked for */
|
||||||
Edje_String repch; /* replacement char for password mode entry */
|
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_Alignment align; /* text alignment within bounds */
|
||||||
Edje_Color color3;
|
Edje_Color color3;
|
||||||
|
@ -1530,6 +1532,8 @@ struct _Edje_Real_Part_Text
|
||||||
const char *text; // 4
|
const char *text; // 4
|
||||||
const char *font; // 4
|
const char *font; // 4
|
||||||
const char *style; // 4
|
const char *style; // 4
|
||||||
|
const char *filter; // 4
|
||||||
|
Eina_List *filter_sources; // 4
|
||||||
Edje_Position offset; // 8
|
Edje_Position offset; // 8
|
||||||
short size; // 2
|
short size; // 2
|
||||||
struct {
|
struct {
|
||||||
|
@ -1542,7 +1546,7 @@ struct _Edje_Real_Part_Text
|
||||||
const char *out_str; // 4
|
const char *out_str; // 4
|
||||||
FLOAT_T align_x, align_y; // 16
|
FLOAT_T align_x, align_y; // 16
|
||||||
} cache;
|
} cache;
|
||||||
}; // 76
|
}; // 84
|
||||||
// FIXME make text a potiner to struct and alloc at end
|
// FIXME make text a potiner to struct and alloc at end
|
||||||
// if part type is TEXT move common members textblock +
|
// if part type is TEXT move common members textblock +
|
||||||
// text to front and have smaller struct for textblock
|
// text to front and have smaller struct for textblock
|
||||||
|
|
|
@ -169,6 +169,8 @@ _edje_text_recalc_apply(Edje *ed, Edje_Real_Part *ep,
|
||||||
char *font2 = NULL;
|
char *font2 = NULL;
|
||||||
char *sfont = NULL;
|
char *sfont = NULL;
|
||||||
int size;
|
int size;
|
||||||
|
const char *filter, *source_name;
|
||||||
|
Eina_List *filter_sources = NULL, *prev_sources = NULL, *li;
|
||||||
Evas_Coord tw, th;
|
Evas_Coord tw, th;
|
||||||
Evas_Coord sw, sh;
|
Evas_Coord sw, sh;
|
||||||
int inlined_font = 0, free_text = 0;
|
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;
|
if (sc == 0.0) sc = _edje_scale;
|
||||||
text = edje_string_get(&chosen_desc->text.text);
|
text = edje_string_get(&chosen_desc->text.text);
|
||||||
font = _edje_text_class_font_get(ed, chosen_desc, &size, &sfont);
|
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->text) text = ep->typedata.text->text;
|
||||||
if (ep->typedata.text->font) font = ep->typedata.text->font;
|
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->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)
|
if (ep->typedata.text->text_source)
|
||||||
{
|
{
|
||||||
|
@ -420,6 +430,17 @@ arrange_text:
|
||||||
evas_obj_text_font_set(font, size),
|
evas_obj_text_font_set(font, size),
|
||||||
evas_obj_text_text_set(text));
|
evas_obj_text_text_set(text));
|
||||||
part_get_geometry(ep, &tw, &th);
|
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 */
|
/* Handle alignment */
|
||||||
{
|
{
|
||||||
FLOAT_T align_x;
|
FLOAT_T align_x;
|
||||||
|
|
|
@ -1846,6 +1846,8 @@ enum
|
||||||
EVAS_OBJ_TEXT_SUB_ID_STYLE_PAD_GET,
|
EVAS_OBJ_TEXT_SUB_ID_STYLE_PAD_GET,
|
||||||
EVAS_OBJ_TEXT_SUB_ID_ELLIPSIS_SET,
|
EVAS_OBJ_TEXT_SUB_ID_ELLIPSIS_SET,
|
||||||
EVAS_OBJ_TEXT_SUB_ID_ELLIPSIS_GET,
|
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
|
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)
|
#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)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @}
|
* @}
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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)
|
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,
|
if (!eina_list_data_find(proxy_src_write->proxies, eo_proxy))
|
||||||
eo_proxy);
|
proxy_src_write->proxies = eina_list_append(proxy_src_write->proxies,
|
||||||
|
eo_proxy);
|
||||||
proxy_src_write->redraw = EINA_TRUE;
|
proxy_src_write->redraw = EINA_TRUE;
|
||||||
}
|
}
|
||||||
EINA_COW_WRITE_END(evas_object_proxy_cow, src->proxy, proxy_src_write);
|
EINA_COW_WRITE_END(evas_object_proxy_cow, src->proxy, proxy_src_write);
|
||||||
|
|
|
@ -622,6 +622,8 @@ _destructor(Eo *eo_obj, void *_pd, va_list *list EINA_UNUSED)
|
||||||
MAGIC_CHECK_END();
|
MAGIC_CHECK_END();
|
||||||
Evas_Object_Protected_Data *obj = _pd;
|
Evas_Object_Protected_Data *obj = _pd;
|
||||||
Evas_Object_Protected_Data *tmp;
|
Evas_Object_Protected_Data *tmp;
|
||||||
|
Evas_Object *proxy;
|
||||||
|
Eina_List *l, *l2;
|
||||||
|
|
||||||
evas_object_hide(eo_obj);
|
evas_object_hide(eo_obj);
|
||||||
if (obj->focused)
|
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);
|
evas_object_grabs_cleanup(eo_obj, obj);
|
||||||
EINA_LIST_FREE(obj->clip.clipees, tmp)
|
EINA_LIST_FREE(obj->clip.clipees, tmp)
|
||||||
evas_object_clip_unset(tmp->object);
|
evas_object_clip_unset(tmp->object);
|
||||||
while (obj->proxy->proxies)
|
EINA_LIST_FOREACH_SAFE(obj->proxy->proxies, l, l2, proxy)
|
||||||
evas_object_image_source_unset(obj->proxy->proxies->data);
|
evas_object_image_source_unset(proxy);
|
||||||
if (obj->cur->clipper) evas_object_clip_unset(eo_obj);
|
if (obj->cur->clipper) evas_object_clip_unset(eo_obj);
|
||||||
evas_object_map_set(eo_obj, NULL);
|
evas_object_map_set(eo_obj, NULL);
|
||||||
if (obj->is_smart) evas_object_smart_del(eo_obj);
|
if (obj->is_smart) evas_object_smart_del(eo_obj);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "evas_common_private.h" /* Includes evas_bidi_utils stuff. */
|
#include "evas_common_private.h" /* Includes evas_bidi_utils stuff. */
|
||||||
#include "evas_private.h"
|
#include "evas_private.h"
|
||||||
|
#include "evas_filter.h"
|
||||||
|
|
||||||
#include "Eo.h"
|
#include "Eo.h"
|
||||||
|
|
||||||
|
@ -9,6 +10,10 @@ EAPI Eo_Op EVAS_OBJ_TEXT_BASE_ID = EO_NOOP;
|
||||||
|
|
||||||
#define MY_CLASS_NAME "Evas_Text"
|
#define MY_CLASS_NAME "Evas_Text"
|
||||||
|
|
||||||
|
#ifdef EVAS_CSERVE2
|
||||||
|
# include "evas_cs2_private.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
/* save typing */
|
/* save typing */
|
||||||
#define ENFN obj->layer->evas->engine.func
|
#define ENFN obj->layer->evas->engine.func
|
||||||
#define ENDT obj->layer->evas->engine.data.output
|
#define ENDT obj->layer->evas->engine.data.output
|
||||||
|
@ -38,7 +43,18 @@ struct _Evas_Object_Text
|
||||||
double ellipsis;
|
double ellipsis;
|
||||||
|
|
||||||
Evas_Font_Size size;
|
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;
|
} cur, prev;
|
||||||
|
|
||||||
struct {
|
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_items_clear(o);
|
||||||
_evas_object_text_recalc(eo_obj, o->cur.text);
|
_evas_object_text_recalc(eo_obj, o->cur.text);
|
||||||
o->changed = 1;
|
o->changed = 1;
|
||||||
|
o->cur.filter.changed = EINA_TRUE;
|
||||||
evas_object_change(eo_obj, obj);
|
evas_object_change(eo_obj, obj);
|
||||||
evas_object_clip_dirty(eo_obj, obj);
|
evas_object_clip_dirty(eo_obj, obj);
|
||||||
evas_object_coords_recalc(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 */
|
/* Handle ellipsis */
|
||||||
if (pos && (o->cur.ellipsis >= 0.0) && (advance + l + r > obj->cur->geometry.w) && (obj->cur->geometry.w > 0))
|
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->cur.ellipsis = ellipsis;
|
||||||
o->changed = 1;
|
o->changed = 1;
|
||||||
|
o->cur.filter.changed = EINA_TRUE;
|
||||||
evas_object_change(eo_obj, obj);
|
evas_object_change(eo_obj, obj);
|
||||||
evas_object_clip_dirty(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);
|
if (o->cur.text != text) free(text);
|
||||||
|
|
||||||
o->changed = 1;
|
o->changed = 1;
|
||||||
|
o->cur.filter.changed = EINA_TRUE;
|
||||||
evas_object_change(eo_obj, obj);
|
evas_object_change(eo_obj, obj);
|
||||||
evas_object_clip_dirty(eo_obj, obj);
|
evas_object_clip_dirty(eo_obj, obj);
|
||||||
evas_object_coords_recalc(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,
|
Eina_Bool int_ret = _evas_object_text_char_coords_get(eo_obj, o, (size_t) pos,
|
||||||
&x, &y, &w, &h);
|
&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;
|
y += o->max_ascent - t;
|
||||||
x -= l;
|
x -= l;
|
||||||
if (x < 0)
|
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,
|
int int_ret = _evas_object_text_char_at_coords(eo_obj, o, x, y - o->max_ascent,
|
||||||
&rx, &ry, &rw, &rh);
|
&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;
|
ry += o->max_ascent - t;
|
||||||
rx -= l;
|
rx -= l;
|
||||||
if (rx < 0)
|
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;
|
int sl = 0, sr = 0, st = 0, sb = 0;
|
||||||
const Evas_Object_Text *o = _pd;
|
const Evas_Object_Text *o = _pd;
|
||||||
/* use temps to be certain we have initialized values */
|
/* 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 (l) *l = sl;
|
||||||
if (r) *r = sr;
|
if (r) *r = sr;
|
||||||
if (t) *t = st;
|
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);
|
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 */
|
/* free obj */
|
||||||
_evas_object_text_items_clear(o);
|
_evas_object_text_items_clear(o);
|
||||||
if (o->cur.utf8_text) eina_stringshare_del(o->cur.utf8_text);
|
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_text_render(Evas_Object *eo_obj EINA_UNUSED,
|
||||||
Evas_Object_Protected_Data *obj,
|
Evas_Object_Protected_Data *obj,
|
||||||
void *type_private_data,
|
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;
|
int i, j;
|
||||||
Evas_Object_Text *o = type_private_data;
|
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}
|
{0, 1, 2, 1, 0}
|
||||||
};
|
};
|
||||||
int sl = 0, st = 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 */
|
/* 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_multiplier_unset(output, context);
|
||||||
ENFN->context_render_op_set(output, context, obj->cur->render_op);
|
ENFN->context_render_op_set(output, context, obj->cur->render_op);
|
||||||
/* FIXME: This clipping is just until we fix inset handling correctly. */
|
/* 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.y + y,
|
||||||
obj->cur->geometry.w,
|
obj->cur->geometry.w,
|
||||||
obj->cur->geometry.h);
|
obj->cur->geometry.h);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
ENFN->context_color_set(output,
|
ENFN->context_color_set(output,
|
||||||
context,
|
context,
|
||||||
|
@ -2077,8 +2125,138 @@ evas_object_text_render(Evas_Object *eo_obj EINA_UNUSED,
|
||||||
&it->text_props, \
|
&it->text_props, \
|
||||||
do_async);
|
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 */
|
/* shadows */
|
||||||
shad_dst = shad_sz = dx = dy = haveshad = 0;
|
|
||||||
switch (o->cur.style & EVAS_TEXT_STYLE_MASK_BASIC)
|
switch (o->cur.style & EVAS_TEXT_STYLE_MASK_BASIC)
|
||||||
{
|
{
|
||||||
case EVAS_TEXT_STYLE_SHADOW:
|
case EVAS_TEXT_STYLE_SHADOW:
|
||||||
|
@ -2425,6 +2603,7 @@ _evas_object_text_rehint(Evas_Object *eo_obj)
|
||||||
/* DO II */
|
/* DO II */
|
||||||
_evas_object_text_recalc(eo_obj, o->cur.text);
|
_evas_object_text_recalc(eo_obj, o->cur.text);
|
||||||
o->changed = 1;
|
o->changed = 1;
|
||||||
|
o->cur.filter.changed = EINA_TRUE;
|
||||||
evas_object_change(eo_obj, obj);
|
evas_object_change(eo_obj, obj);
|
||||||
evas_object_clip_dirty(eo_obj, obj);
|
evas_object_clip_dirty(eo_obj, obj);
|
||||||
evas_object_coords_recalc(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);
|
w = _evas_object_text_horiz_advance_get(o);
|
||||||
h = _evas_object_text_vert_advance_get(eo_obj, 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)
|
if (o->cur.ellipsis >= 0.0)
|
||||||
{
|
{
|
||||||
|
@ -2509,9 +2691,12 @@ _evas_object_text_recalc(Evas_Object *eo_obj, Eina_Unicode *text)
|
||||||
}
|
}
|
||||||
else
|
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,
|
eo_do_super(eo_obj, MY_CLASS,
|
||||||
evas_obj_size_set(0, o->max_ascent + o->max_descent + t + b));
|
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;
|
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
|
static void
|
||||||
_class_constructor(Eo_Class *klass)
|
_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_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_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_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_OP_FUNC_SENTINEL
|
||||||
};
|
};
|
||||||
eo_class_funcs_set(klass, func_desc);
|
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_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_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_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
|
EO_OP_DESCRIPTION_SENTINEL
|
||||||
};
|
};
|
||||||
static const Eo_Class_Description class_desc = {
|
static const Eo_Class_Description class_desc = {
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "evas_private.h"
|
#include "evas_private.h"
|
||||||
|
|
||||||
#include "evas_font_private.h"
|
#include "evas_font_private.h"
|
||||||
|
#include "evas_blend_private.h"
|
||||||
|
|
||||||
#ifdef EVAS_CSERVE2
|
#ifdef EVAS_CSERVE2
|
||||||
# include "../cserve2/evas_cs2_private.h"
|
# 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;
|
int *iptr;
|
||||||
|
|
||||||
if (!buf) return NULL;
|
if (!buf) return NULL;
|
||||||
*wret = fgo->bitmap.width;
|
if (wret) *wret = fgo->bitmap.width;
|
||||||
*hret = fgo->bitmap.rows;
|
if (hret) *hret = fgo->bitmap.rows;
|
||||||
iptr = (int *)fgo->rle;
|
iptr = (int *)fgo->rle;
|
||||||
if (*iptr > 0) // rle4
|
if (*iptr > 0) // rle4
|
||||||
decompress_rle4(fgo->rle, buf, fgo->bitmap.width,
|
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;
|
x1 = 0; x2 = w;
|
||||||
if ((x + x1) < cx) x1 = cx - x;
|
if ((x + x1) < cx) x1 = cx - x;
|
||||||
if ((x + x2) > (cx + cw)) x2 = cx + cw - 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;
|
col = dc->col.col;
|
||||||
for (i = 0; i <= 0xf; i++)
|
if (dst_image->cache_entry.space == EVAS_COLORSPACE_GRY8)
|
||||||
{
|
{
|
||||||
v = (i << 4) | i;
|
// FIXME: Font draw not optimized for Alpha targets! SLOW!
|
||||||
coltab[i] = MUL_SYM(v, col);
|
// This is not pretty :)
|
||||||
tmp = (coltab[i] >> 24);
|
|
||||||
mtab[i] = 256 - (tmp + (tmp >> 7));
|
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
|
else
|
||||||
if (evas_common_cpu_has_feature(CPU_FEATURE_MMX))
|
|
||||||
{
|
{
|
||||||
|
// 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
|
#define MMX 1
|
||||||
#include "evas_font_compress_draw.c"
|
#include "evas_font_compress_draw.c"
|
||||||
#undef MMX
|
#undef MMX
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef BUILD_NEON
|
#ifdef BUILD_NEON
|
||||||
if (evas_common_cpu_has_feature(CPU_FEATURE_NEON))
|
if (evas_common_cpu_has_feature(CPU_FEATURE_NEON))
|
||||||
{
|
{
|
||||||
#define NEON 1
|
#define NEON 1
|
||||||
#include "evas_font_compress_draw.c"
|
#include "evas_font_compress_draw.c"
|
||||||
#undef NEON
|
#undef NEON
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
{
|
// Plain C
|
||||||
|
{
|
||||||
#include "evas_font_compress_draw.c"
|
#include "evas_font_compress_draw.c"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -328,20 +328,21 @@ evas_gl_common_image_new_from_copied_data(Evas_Engine_GL_Context *gc, unsigned i
|
||||||
switch (cspace)
|
switch (cspace)
|
||||||
{
|
{
|
||||||
case EVAS_COLORSPACE_ARGB8888:
|
case EVAS_COLORSPACE_ARGB8888:
|
||||||
break;
|
case EVAS_COLORSPACE_GRY8:
|
||||||
|
break;
|
||||||
case EVAS_COLORSPACE_YCBCR422P601_PL:
|
case EVAS_COLORSPACE_YCBCR422P601_PL:
|
||||||
case EVAS_COLORSPACE_YCBCR422P709_PL:
|
case EVAS_COLORSPACE_YCBCR422P709_PL:
|
||||||
if (im->tex) evas_gl_common_texture_free(im->tex, EINA_TRUE);
|
if (im->tex) evas_gl_common_texture_free(im->tex, EINA_TRUE);
|
||||||
im->tex = NULL;
|
im->tex = NULL;
|
||||||
im->cs.no_free = 0;
|
im->cs.no_free = 0;
|
||||||
if (im->im->cache_entry.h > 0)
|
if (im->im->cache_entry.h > 0)
|
||||||
im->cs.data = calloc(1, im->im->cache_entry.h * sizeof(unsigned char *) * 2);
|
im->cs.data = calloc(1, im->im->cache_entry.h * sizeof(unsigned char *) * 2);
|
||||||
if ((data) && (im->cs.data))
|
if ((data) && (im->cs.data))
|
||||||
memcpy(im->cs.data, data, im->im->cache_entry.h * sizeof(unsigned char *) * 2);
|
memcpy(im->cs.data, data, im->im->cache_entry.h * sizeof(unsigned char *) * 2);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
abort();
|
abort();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return im;
|
return im;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue