Evas filters: Implement Lua classes for colors & buffer

Reuse previous code for buffer. Keeps API stability.

The new class "color" is here for a more convenient color
representation. This way, colors can be represented in more
natural ways like: {r,g,b[,a]}, 0xaarrggbb, "red", "#rrggbb"

Class color is implemented in pure Lua, and adds a .lua file
to Evas' share folder.
This commit is contained in:
Jean-Philippe Andre 2015-06-04 19:42:38 +09:00
parent 93797e3b0d
commit f3e16bc485
7 changed files with 639 additions and 268 deletions

View File

@ -2204,3 +2204,11 @@ installed_evasluadir = $(datadir)/elua/modules/evas
nodist_installed_evaslua_DATA = $(generated_evas_lua_all)
endif
# Evas filters Lua stuff
evas_filters_lua = \
lib/evas/filters/lua/color.lua \
$(NULL)
installed_evasfiltersdir = $(datadir)/evas/filters/lua
dist_installed_evasfilters_DATA = $(evas_filters_lua)

View File

@ -680,6 +680,16 @@ _evas_module_libdir_get(void)
return eina_prefix_lib_get(pfx);
}
const char *
_evas_module_datadir_get(void)
{
if (!pfx) pfx = eina_prefix_new
(NULL, _evas_module_libdir_get, "EVAS", "evas", "checkme",
PACKAGE_BIN_DIR, PACKAGE_LIB_DIR, PACKAGE_DATA_DIR, PACKAGE_DATA_DIR);
if (!pfx) return NULL;
return eina_prefix_data_get(pfx);
}
EAPI const char *
evas_cserve_path_get(void)
{

View File

@ -2002,6 +2002,7 @@ void
evas_filter_shutdown()
{
if ((--init_cnt) > 0) return;
evas_filter_parser_shutdown();
eina_log_domain_unregister(_evas_filter_log_dom);
_evas_filter_log_dom = 0;
}

View File

@ -215,35 +215,6 @@
@since 1.9
*/
// Map of the most common HTML color names
static struct
{
const char *name;
DATA32 value;
} color_map[] =
{
{ "white", 0xFFFFFFFF },
{ "black", 0xFF000000 },
{ "red", 0xFFFF0000 },
{ "green", 0xFF008000 },
{ "blue", 0xFF0000FF },
{ "darkblue", 0xFF0000A0 },
{ "yellow", 0xFFFFFF00 },
{ "magenta", 0xFFFF00FF },
{ "cyan", 0xFF00FFFF },
{ "orange", 0xFFFFA500 },
{ "purple", 0xFF800080 },
{ "brown", 0xFFA52A2A },
{ "maroon", 0xFF800000 },
{ "lime", 0xFF00FF00 },
{ "gray", 0xFF808080 },
{ "grey", 0xFF808080 },
{ "silver", 0xFFC0C0C0 },
{ "olive", 0xFF808000 },
{ "invisible", 0x00000000 },
{ "transparent", 0x00000000 }
};
static struct
{
const char *name;
@ -265,9 +236,15 @@ static struct
{ "stretch_xy", EVAS_FILTER_FILL_MODE_STRETCH_XY }
};
static const char *_lua_buffer_meta = "Filter.buffer";
static const char *_lua_buffer_meta = "buffer";
static const char *_lua_color_meta = "color";
#define _lua_methods_table "__methods"
#define _lua_register_func "__register"
#define _lua_errfunc_name "__backtrace"
static Evas_Filter_Fill_Mode _fill_mode_get(Evas_Filter_Instruction *instr);
static Eina_Bool _lua_instruction_run(lua_State *L, Evas_Filter_Instruction *instr);
static int _lua_backtrace(lua_State *L);
typedef enum
{
@ -618,46 +595,6 @@ _bool_parse(const char *str, Eina_Bool *b)
#define PARSE_CHECK(a) do { if (!(a)) { ERR("Parsing failed because '%s' is false at %s:%d", #a, __FUNCTION__, __LINE__); PARSE_ABORT(); goto end; } } while (0)
static Eina_Bool
_color_parse(const char *word, DATA32 *color)
{
DATA32 value;
Eina_Bool success = EINA_FALSE;
PARSE_CHECK(word && *word);
errno = 0;
if (*word == '#')
{
unsigned char a, r, g, b;
int slen = strlen(word);
PARSE_CHECK(evas_common_format_color_parse(word, slen, &r, &g, &b, &a));
value = ARGB_JOIN(a, r, g, b);
}
else
{
unsigned int k;
for (k = 0; k < (sizeof(color_map) / sizeof(color_map[0])); k++)
{
if (!strcasecmp(word, color_map[k].name))
{
if (color) *color = color_map[k].value;
return EINA_TRUE;
}
}
PARSE_CHECK(!"color name not found");
}
if ((value & 0xFF000000) == 0 && (value != 0))
value |= 0xFF000000;
if (color) *color = value;
success = EINA_TRUE;
end:
return success;
}
/* Buffers */
static Buffer *
_buffer_get(Evas_Filter_Program *pgm, const char *name)
@ -679,16 +616,19 @@ _lua_buffer_push(lua_State *L, Buffer *buf)
{
Buffer **ptr;
lua_getglobal(L, buf->name);
ptr = lua_newuserdata(L, sizeof(Buffer **));
lua_getglobal(L, buf->name);//+1
ptr = lua_newuserdata(L, sizeof(Buffer **));//+1
*ptr = buf;
luaL_getmetatable(L, _lua_buffer_meta);
lua_setmetatable(L, -2);
lua_setglobal(L, buf->name);
luaL_getmetatable(L, _lua_buffer_meta);//+1
lua_setmetatable(L, -2);//-1
lua_setglobal(L, buf->name);//-1
lua_pop(L, 1);
return EINA_TRUE;
}
// Begin of Lua metamethods and stuff
static int
_lua_buffer_tostring(lua_State *L)
{
@ -717,12 +657,12 @@ _lua_buffer_index(lua_State *L)
key = lua_tostring(L, 2);
if (!key) return 0;
if (!strcmp(key, "width"))
if (!strcmp(key, "w") || !strcmp(key, "width"))
{
lua_pushinteger(L, buf->w);
return 1;
}
else if (!strcmp(key, "height"))
else if (!strcmp(key, "h") || !strcmp(key, "height"))
{
lua_pushinteger(L, buf->h);
return 1;
@ -754,69 +694,30 @@ _lua_buffer_index(lua_State *L)
return 1;
}
else
return luaL_error(L, "Unknown index '%s' for a buffer", key);
return 0;
}
// remove metatable from first argument if this is a __call metafunction
static inline int
_lua_implicit_metatable_drop(lua_State *L, const char *name)
{
int ret = 0;
if (lua_istable(L, 1) && lua_getmetatable(L, 1))
{
DBG("Unknown index '%s' for a buffer", key);
return 0;
luaL_getmetatable(L, name);
if (lua_equal(L, -1, -2))
{
lua_remove(L, 1);
ret = 1;
}
lua_pop(L, 2);
}
return ret;
}
static int
_lua_buffer_width(lua_State *L)
{
Buffer *buf, **pbuf;
pbuf = lua_touserdata(L, 1);
buf = pbuf ? *pbuf : NULL;
if (!buf) return 0;
lua_pushnumber(L, buf->w);
return 1;
}
static int
_lua_buffer_height(lua_State *L)
{
Buffer *buf, **pbuf;
pbuf = lua_touserdata(L, 1);
buf = pbuf ? *pbuf : NULL;
if (!buf) return 0;
lua_pushnumber(L, buf->h);
return 1;
}
static int
_lua_buffer_type(lua_State *L)
{
Buffer *buf, **pbuf;
pbuf = lua_touserdata(L, 1);
buf = pbuf ? *pbuf : NULL;
if (!buf) return 0;
lua_pushstring(L, buf->alpha ? "alpha" : "rgba");
return 1;
}
static int
_lua_buffer_name(lua_State *L)
{
Buffer *buf, **pbuf;
pbuf = lua_touserdata(L, 1);
buf = pbuf ? *pbuf : NULL;
if (!buf) return 0;
lua_pushstring(L, buf->name);
return 1;
}
static int
_lua_buffer_source(lua_State *L)
{
Buffer *buf, **pbuf;
pbuf = lua_touserdata(L, 1);
buf = pbuf ? *pbuf : NULL;
if (!buf) return 0;
if (!buf->proxy)
lua_pushnil(L);
else
lua_pushstring(L, buf->proxy);
return 1;
}
// End of all lua metamethods and stuff
static Buffer *
_buffer_add(Evas_Filter_Program *pgm, const char *name, Eina_Bool alpha,
@ -868,6 +769,19 @@ _buffer_del(Buffer *buf)
free(buf);
}
static const int this_is_not_a_cat = 42;
static Evas_Filter_Program *
_lua_program_get(lua_State *L)
{
Evas_Filter_Program *pgm;
lua_pushlightuserdata(L, (void *) &this_is_not_a_cat);
lua_gettable(L, LUA_REGISTRYINDEX);
pgm = lua_touserdata(L, -1);
lua_pop(L, 1);
return pgm;
}
/* Instruction definitions */
/**
@ -935,20 +849,33 @@ _buffer_instruction_parse_run(lua_State *L,
return ok;
}
static Eina_Bool
_buffer_instruction_prepare(Evas_Filter_Program *pgm EINA_UNUSED,
Evas_Filter_Instruction *instr)
static int
_lua_buffer_new(lua_State *L)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(instr, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(instr->name, EINA_FALSE);
EINA_SAFETY_ON_FALSE_RETURN_VAL(!strcmp(instr->name, "buffer"), EINA_FALSE);
// Reuse old "buffer" instruction code
Evas_Filter_Program *pgm = _lua_program_get(L);
Evas_Filter_Instruction *instr;
instr = _instruction_new(_lua_buffer_meta);
instr->type = EVAS_FILTER_MODE_BUFFER;
instr->parse_run = _buffer_instruction_parse_run;
_instruction_param_seq_add(instr, "type", VT_STRING, "rgba");
_instruction_param_seq_add(instr, "src", VT_BUFFER, NULL);
return EINA_TRUE;
// drop "buffer" metatable
_lua_implicit_metatable_drop(L, _lua_buffer_meta);
if (!_lua_instruction_run(L, instr))
{
_instruction_del(instr);
return luaL_error(L, "buffer instanciation failed");
}
else
{
pgm->instructions = eina_inlist_append(pgm->instructions, EINA_INLIST_GET(instr));
}
return instr->return_count;
}
static int
@ -1329,9 +1256,10 @@ _lua_curve_points_func(lua_State *L, int i, Evas_Filter_Program *pgm EINA_UNUSED
case LUA_TFUNCTION:
for (k = 0; k < 256; k++)
{
lua_getglobal(L, _lua_errfunc_name);
lua_pushvalue(L, i);
lua_pushinteger(L, k);
if (!lua_pcall(L, 1, 1, 0))
if (!lua_pcall(L, 1, 1, -3))
{
if (!lua_isnumber(L, -1))
{
@ -1932,17 +1860,21 @@ evas_filter_program_del(Evas_Filter_Program *pgm)
free(pgm);
}
static const int this_is_not_a_cat = 42;
static Evas_Filter_Program *
_lua_program_get(lua_State *L)
// [-1, +1, e] -- converts the top of the stack to a valid 'color' object
static Eina_Bool
_lua_convert_color(lua_State *L)
{
Evas_Filter_Program *pgm;
lua_pushlightuserdata(L, (void *) &this_is_not_a_cat);
lua_gettable(L, LUA_REGISTRYINDEX);
pgm = lua_touserdata(L, -1);
lua_pop(L, 1);
return pgm;
int top = lua_gettop(L);
lua_getglobal(L, _lua_errfunc_name); //+1
lua_getglobal(L, _lua_color_meta); //+1 (mt)
lua_getfield(L, -1, "__call"); //+1 (func)
lua_pushvalue(L, -2); //+1 (mt)
lua_pushvalue(L, top); //+1 (argument)
if (lua_pcall(L, 2, 1, top + 1) != 0)
return EINA_FALSE;
lua_insert(L, top);
lua_settop(L, top);
return EINA_TRUE;
}
static Eina_Bool
@ -1954,10 +1886,12 @@ _lua_parameter_parse(Evas_Filter_Program *pgm, lua_State *L,
if (param->set)
{
ERR("Parameter %s has already been set", param->name);
luaL_error(L, "Parameter %s has already been set", param->name);
return 0;
return luaL_error(L, "Parameter %s has already been set", param->name);
}
if (i < 0)
i = lua_gettop(L) + i + 1;
switch (param->type)
{
case VT_BOOL:
@ -1992,35 +1926,45 @@ _lua_parameter_parse(Evas_Filter_Program *pgm, lua_State *L,
param->value.s = strdup(lua_tostring(L, i));
break;
case VT_COLOR:
if ((lua_isnumber(L, i)) || (lua_type(L, i) == LUA_TSTRING))
{
int A, R, G, B;
DATA32 color;
if (lua_isnumber(L, i))
color = (DATA32) lua_tonumber(L, i);
else
{
if (!_color_parse(lua_tostring(L, i), &color))
goto fail;
}
A = A_VAL(&color);
R = R_VAL(&color);
G = G_VAL(&color);
B = B_VAL(&color);
if (!A && (R || G || B)) A = 0xFF;
if ((A < R) || (A < G) || (A < B))
{
ERR("Argument '%s' of function '%s' is not a valid premultiplied RGBA value!",
param->name, instr->name);
evas_color_argb_premul(A, &R, &G, &B);
//goto fail;
}
param->value.c = ARGB_JOIN(A, R, G, B);
}
else
goto fail;
{
// Auto convert values to a color() if they aren't one already
int cid = 0, pop = 0, A, R, G, B;
if (lua_istable(L, i))
{
luaL_getmetatable(L, _lua_color_meta);
lua_getmetatable(L, i);
if (!lua_isnil(L, -1) && lua_equal(L, -2, -1))
{
// this is a color already
cid = i;
}
lua_pop(L, 2);
}
if (!cid)
{
lua_pushvalue(L, i); //+1 (arg)
if (!_lua_convert_color(L)) //-1/+1
{
ERR("Failed to convert color: %s", lua_tostring(L, -1));
goto fail;
}
cid = lua_gettop(L);
pop = 1;
}
if (!lua_istable(L, cid))
goto fail;
lua_getfield(L, cid, "a");
A = lua_tointeger(L, -1);
lua_getfield(L, cid, "r");
R = lua_tointeger(L, -1);
lua_getfield(L, cid, "g");
G = lua_tointeger(L, -1);
lua_getfield(L, cid, "b");
B = lua_tointeger(L, -1);
lua_pop(L, pop + 4);
evas_color_argb_premul(A, &R, &G, &B);
param->value.c = ARGB_JOIN(A, R, G, B);
}
break;
case VT_BUFFER:
{
@ -2051,13 +1995,15 @@ _lua_parameter_parse(Evas_Filter_Program *pgm, lua_State *L,
goto fail;
}
if (i != lua_gettop(L))
ERR("something is wrong");
param->set = EINA_TRUE;
return EINA_TRUE;
fail:
ERR("Invalid value for parameter %s", param->name);
luaL_error(L, "Invalid value for parameter %s", param->name);
return EINA_FALSE;
return luaL_error(L, "Invalid value for parameter %s", param->name);
}
static Instruction_Param *
@ -2089,7 +2035,7 @@ _lua_instruction_run(lua_State *L, Evas_Filter_Instruction *instr)
if (eina_inlist_count(instr->params) < argc)
{
ERR("Too many arguments passed to the instruction %s", instr->name);
goto fail;
return EINA_FALSE;
}
if (lua_istable(L, 1))
@ -2097,7 +2043,7 @@ _lua_instruction_run(lua_State *L, Evas_Filter_Instruction *instr)
if (argc > 1)
{
ERR("Too many arguments passed to the instruction %s (in table mode)", instr->name);
goto fail;
return EINA_FALSE;
}
lua_pushnil(L);
@ -2110,7 +2056,7 @@ _lua_instruction_run(lua_State *L, Evas_Filter_Instruction *instr)
if (!param)
{
ERR("Parameter %s does not exist", name);
goto fail;
return EINA_FALSE;
}
}
else if (lua_isnumber(L, -2))
@ -2120,24 +2066,24 @@ _lua_instruction_run(lua_State *L, Evas_Filter_Instruction *instr)
if (!param)
{
ERR("Too many parameters for the function %s", instr->name);
goto fail;
return EINA_FALSE;
}
if (!param->allow_seq)
{
ERR("The parameter %s must be referred to by name in function %s",
param->name, instr->name);
goto fail;
return EINA_FALSE;
}
}
else
{
ERR("Invalid type for the parameter key in function %s", instr->name);
goto fail;
return EINA_FALSE;
}
if (!_lua_parameter_parse(pgm, L, instr, param, -1))
goto fail;
return EINA_FALSE;
lua_pop(L, 1);
}
}
@ -2147,7 +2093,7 @@ _lua_instruction_run(lua_State *L, Evas_Filter_Instruction *instr)
{
if ((++i) > argc) break;
if (!_lua_parameter_parse(pgm, L, instr, param, i))
goto fail;
return EINA_FALSE;
}
}
@ -2161,11 +2107,6 @@ _lua_instruction_run(lua_State *L, Evas_Filter_Instruction *instr)
}
return EINA_TRUE;
fail:
ERR("Invalid parameters for instruction %s", instr->name);
luaL_error(L, "Invalid parameters for instruction %s", instr->name);
return EINA_FALSE;
}
static int
@ -2174,18 +2115,14 @@ _lua_generic_function(lua_State *L, const char *name,
{
Evas_Filter_Program *pgm = _lua_program_get(L);
Evas_Filter_Instruction *instr;
Eina_Bool ok;
instr = _instruction_new(name);
prepare(pgm, instr);
ok = _lua_instruction_run(L, instr);
if (!ok)
if (!_lua_instruction_run(L, instr))
{
ERR("Instruction parsing failed");
_instruction_del(instr);
lua_error(L);
return 0;
return luaL_error(L, "Instruction parsing failed");
}
else
{
@ -2212,33 +2149,14 @@ _lua_print(lua_State *L)
for (i = 1; i <= nargs; i++)
{
if (lua_isstring(L, i))
eina_strbuf_append(s, lua_tostring(L, i));
else if (lua_isnumber(L, i))
{
double d = lua_tonumber(L, i);
if (fabs(d - floor(d)) < 0.000001)
eina_strbuf_append_printf(s, "%d", (int) d);
else
eina_strbuf_append_printf(s, "%f", d);
}
else if (luaL_checkudata(L, i, _lua_buffer_meta))
{
Buffer *buf, **pbuf;
pbuf = lua_touserdata(L, i);
buf = pbuf ? *pbuf : NULL;
if (!buf)
eina_strbuf_append(s, "Buffer[null]");
else
eina_strbuf_append_printf(s, "Buffer[#%d %dx%d %s%s%s]",
buf->cid, buf->w, buf->h,
buf->alpha ? "alpha" : "rgba",
buf->proxy ? " src: " : "",
buf->proxy ? buf->proxy : "");
}
else
eina_strbuf_append(s, "<>");
const char *str;
lua_getglobal(L, _lua_errfunc_name);
lua_getglobal(L, "tostring"); //+1
lua_pushvalue(L, i); //+1
lua_pcall(L, 1, 1, -3); //-2/+1
str = lua_tostring(L, -1);
eina_strbuf_append(s, str ? str : "(nil)");
lua_pop(L, 2);
eina_strbuf_append_char(s, ' ');
}
@ -2264,7 +2182,6 @@ _lua_##name(lua_State *L) \
lua_pushcfunction(L, _lua_##name); \
lua_setglobal(L, #name);
LUA_GENERIC_FUNCTION(buffer)
LUA_GENERIC_FUNCTION(blend)
LUA_GENERIC_FUNCTION(blur)
LUA_GENERIC_FUNCTION(bump)
@ -2276,21 +2193,98 @@ LUA_GENERIC_FUNCTION(mask)
LUA_GENERIC_FUNCTION(padding_set)
LUA_GENERIC_FUNCTION(transform)
static const luaL_Reg buffer_methods[] = {
{ "width", _lua_buffer_width },
{ "height", _lua_buffer_height },
{ "type", _lua_buffer_type },
{ "name", _lua_buffer_name },
{ "source", _lua_buffer_source },
{ NULL, NULL }
};
static const luaL_Reg buffer_meta[] = {
static const luaL_Reg _lua_buffer_metamethods[] = {
{ "__call", _lua_buffer_new },
{ "__tostring", _lua_buffer_tostring },
{ "__index", _lua_buffer_index },
{ NULL, NULL }
};
static char *_lua_color_code = NULL;
static inline void
_lua_import_path_get(char *path, size_t len, const char *name)
{
const char *pfx = _evas_module_datadir_get();
struct stat st;
size_t r;
//#ifdef FILTERS_DEBUG
// This is a hack to fetch the most recent file from source
if (stat(path, &st) == -1)
{
char *sep = evas_file_path_join("", "");
char *src = strdup(__FILE__);
char *slash = strrchr(src, *sep);
if (slash)
{
*slash = '\0';
if (*src == '/')
r = snprintf(path, len - 1, "%s/lua/%s.lua", src, name);
else // abs_srcdir is unknown here
r = snprintf(path, len - 1, "%s/src/%s/lua/%s.lua", PACKAGE_BUILD_DIR, src, name);
if (r >= len) path[len - 1] = '\0';
}
free(sep);
free(src);
if (!stat(path, &st)) return;
}
//#endif
r = snprintf(path, len - 1, "%s/filters/lua/%s.lua", pfx ? pfx : ".", name);
if (r >= len) path[len - 1] = '\0';
}
static Eina_Bool
_lua_import_class(lua_State *L, const char *name, char **code)
{
// Load code from file
if (!*code)
{
char path[PATH_MAX];
Eina_File *f;
void *map;
size_t sz;
_lua_import_path_get(path, PATH_MAX, name);
f = eina_file_open(path, EINA_FALSE);
if (!f) return EINA_FALSE;
sz = eina_file_size_get(f);
*code = malloc(sz);
if (!*code) return EINA_FALSE;
map = eina_file_map_all(f, EINA_FILE_SEQUENTIAL);
if (!map) return EINA_FALSE;
memcpy(*code, map, sz);
eina_file_map_free(f, map);
eina_file_close(f);
}
if (!luaL_dostring(L, *code)) //+1
{
lua_getglobal(L, _lua_errfunc_name); //+1
lua_pushliteral(L, _lua_register_func); //+1
lua_rawget(L, -3); //-1/+1
if (lua_isfunction(L, -1))
{
lua_getglobal(L, "_G"); //+1
if (lua_pcall(L, 1, 0, -3) != 0) //-2
{
ERR("Failed to register globals for '%s': %s", name, lua_tostring(L, -1));
lua_pop(L, 1);
}
}
else lua_pop(L, 1);
lua_pop(L, 1); // -1 (errfunc)
lua_setglobal(L, name); //-1
}
else
{
ERR("Lua class '%s' could not be loaded: %s", name, lua_tostring(L, -1));
return EINA_FALSE;
}
return EINA_TRUE;
}
static void
_filter_program_buffers_set(Evas_Filter_Program *pgm)
{
@ -2311,6 +2305,27 @@ _filter_program_buffers_set(Evas_Filter_Program *pgm)
}
}
static inline void
_lua_class_create(lua_State *L, const char *name,
const luaL_Reg *meta, const luaL_Reg *methods)
{
luaL_newmetatable(L, name);
luaL_register(L, NULL, meta);
lua_pushliteral(L, "__metatable");
lua_pushvalue(L, -2);
lua_rawset(L, -3);
if (methods)
{
lua_pushliteral(L, _lua_methods_table);
lua_newtable(L);
luaL_register(L, NULL, methods);
lua_rawset(L, -3);
}
lua_pushvalue(L, -1);
lua_setmetatable(L, -2);
lua_setglobal(L, name);
}
static lua_State *
_lua_state_create(Evas_Filter_Program *pgm)
{
@ -2327,19 +2342,24 @@ _lua_state_create(Evas_Filter_Program *pgm)
luaopen_table(L);
luaopen_string(L);
luaopen_math(L);
luaopen_debug(L);
lua_settop(L, 0);
// Implement print
lua_getglobal(L, "_G");
luaL_register(L, NULL, printlib);
lua_pop(L, 1);
// Add backtrace error function
lua_pushcfunction(L, _lua_backtrace);
lua_setglobal(L, _lua_errfunc_name);
// Store program
lua_pushlightuserdata(L, (void *) &this_is_not_a_cat);
lua_pushlightuserdata(L, pgm);
lua_settable(L, LUA_REGISTRYINDEX);
// Register functions
PUSH_LUA_FUNCTION(buffer)
PUSH_LUA_FUNCTION(blend)
PUSH_LUA_FUNCTION(blur)
PUSH_LUA_FUNCTION(bump)
@ -2351,13 +2371,6 @@ _lua_state_create(Evas_Filter_Program *pgm)
PUSH_LUA_FUNCTION(padding_set)
PUSH_LUA_FUNCTION(transform)
// Register special variables
for (unsigned k = 0; k < (sizeof(color_map) / sizeof(color_map[0])); k++)
{
lua_pushnumber(L, color_map[k].value);
lua_setglobal(L, color_map[k].name);
}
for (unsigned k = 0; k < (sizeof(fill_modes) / sizeof(fill_modes[0])); k++)
{
if (strcmp("repeat", fill_modes[k].name))
@ -2391,18 +2404,40 @@ _lua_state_create(Evas_Filter_Program *pgm)
lua_setglobal(L, booleans[k].name);
}
// Register buffer meta stuff
luaL_openlib(L, _lua_buffer_meta, buffer_methods, 0);
luaL_newmetatable(L, _lua_buffer_meta);
luaL_openlib(L, NULL, buffer_meta, 0);
lua_pushliteral(L, "__metatable");
lua_pushvalue(L, -3);
lua_rawset(L, -3);
lua_pop(L, 1);
// Create buffer class based on userdata
_lua_class_create(L, _lua_buffer_meta, _lua_buffer_metamethods, NULL);
// Load color class
if (!_lua_import_class(L, _lua_color_meta, &_lua_color_code))
ERR("Could not load color class!");
return L;
}
static int
_lua_backtrace(lua_State *L)
{
if (!lua_isstring(L, 1)) /* 'message' not a string? */
return 1; /* keep it intact */
ERR("Lua error: %s", lua_tolstring(L, 1, NULL));
lua_getfield(L, LUA_GLOBALSINDEX, "debug");
if (!lua_istable(L, -1))
{
lua_pop(L, 1);
return 1;
}
lua_getfield(L, -1, "traceback");
if (!lua_isfunction(L, -1))
{
lua_pop(L, 2);
return 1;
}
lua_pushvalue(L, 1); /* pass error message */
lua_pushinteger(L, 2); /* skip this function and traceback */
lua_call(L, 2, 1); /* call debug.traceback */
return 1;
}
#ifdef FILTERS_LEGACY_COMPAT
// This function is here to avoid breaking the ABI too much.
// It should not stay here long, only until all client apps have changed the filters' code to Lua.
@ -2545,8 +2580,10 @@ _filter_program_state_set(Evas_Filter_Program *pgm)
* }
*/
#define JOINC(k) ARGB_JOIN(pgm->state.k.a, pgm->state.k.r, pgm->state.k.g, pgm->state.k.b)
#define JOINC(k) (double) ({ DATA32 d; int A = pgm->state.k.a, R = pgm->state.k.r, G = pgm->state.k.g, B = pgm->state.k.b; \
evas_color_argb_unpremul(A, &R, &G, &B); d = ARGB_JOIN(A, R, G, B); d; })
#define SETFIELD(name, val) do { lua_pushnumber(L, val); lua_setfield(L, -2, name); } while(0)
#define SETCOLOR(name, val) do { lua_pushnumber(L, val); _lua_convert_color(L); lua_setfield(L, -2, name); } while(0)
// TODO: Mark program as dependent on some values so we can improve
// the changed flag (ie. re-run the filter only when required)
@ -2555,7 +2592,7 @@ _filter_program_state_set(Evas_Filter_Program *pgm)
lua_newtable(L); // "state"
{
SETFIELD("color", JOINC(color));
SETCOLOR("color", JOINC(color));
SETFIELD("scale", pgm->state.scale);
SETFIELD("pos", pgm->state.pos);
lua_newtable(L); // "cur"
@ -2577,10 +2614,10 @@ _filter_program_state_set(Evas_Filter_Program *pgm)
}
lua_newtable(L); // "text"
{
SETFIELD("outline", JOINC(text.outline));
SETFIELD("shadow", JOINC(text.shadow));
SETFIELD("glow", JOINC(text.glow));
SETFIELD("glow2", JOINC(text.glow2));
SETCOLOR("outline", JOINC(text.outline));
SETCOLOR("shadow", JOINC(text.shadow));
SETCOLOR("glow", JOINC(text.glow));
SETCOLOR("glow2", JOINC(text.glow2));
lua_setfield(L, -2, "text");
}
}
@ -2588,6 +2625,7 @@ _filter_program_state_set(Evas_Filter_Program *pgm)
#undef JOINC
#undef SETFIELD
#undef SETCOLOR
}
static void
@ -2656,8 +2694,9 @@ evas_filter_program_parse(Evas_Filter_Program *pgm, const char *str)
{
pgm->lua_func = luaL_ref(L, LUA_REGISTRYINDEX);
_filter_program_reset(pgm);
lua_getglobal(L, _lua_errfunc_name);
lua_rawgeti(L, LUA_REGISTRYINDEX, pgm->lua_func);
ok = !lua_pcall(L, 0, LUA_MULTRET, 0);
ok = !lua_pcall(L, 0, LUA_MULTRET, -2);
}
if (!ok)
@ -3382,8 +3421,9 @@ evas_filter_context_program_use(Evas_Filter_Context *ctx,
{
pgm->changed = EINA_FALSE;
_filter_program_reset(pgm);
lua_getglobal(pgm->L, _lua_errfunc_name);
lua_rawgeti(pgm->L, LUA_REGISTRYINDEX, pgm->lua_func);
success = !lua_pcall(pgm->L, 0, LUA_MULTRET, 0);
success = !lua_pcall(pgm->L, 0, LUA_MULTRET, -2);
if (!success)
{
const char *msg = lua_tostring(pgm->L, -1);
@ -3416,3 +3456,10 @@ end:
if (dc) ENFN->context_free(ENDT, dc);
return success;
}
void
evas_filter_parser_shutdown(void)
{
free(_lua_color_code);
_lua_color_code = NULL;
}

View File

@ -268,4 +268,6 @@ Eina_Bool evas_filter_interpolate(DATA8* output /* 256 values */, int
Evas_Filter_Command *_evas_filter_command_get(Evas_Filter_Context *ctx, int cmdid);
int evas_filter_smallest_pow2_larger_than(int val);
void evas_filter_parser_shutdown(void);
#endif // EVAS_FILTER_PRIVATE_H

View File

@ -0,0 +1,302 @@
--[[
A simple 'color' class for Evas filters.
The default alpha value will be 255 unless specified,
which means the default color is opaque black.
r,g,b,a values range from 0 to 255 and are straight
(ie. NOT pre-multiplied).
--]]
local __color, __inrange, __color_parse
--[[Checks that a number is valid and in the range 0-255]]
__inrange = function(a)
if ((not tonumber(a)) or (tonumber(a) < 0) or (tonumber(a) > 255)) then
return false
else
return true
end
end
--[[
Parses a string of one of the formats:
1. "#RRGGBB"
2. "#RRGGBBAA"
3. "#RGB"
4. "#RGBA"
To the rgba values.
Same as evas_common_format_color_parse, except we don't premultiply here.
--]]
__color_parse = function(str)
local r,g,b,a
if not str then return 0,0,0,0 end
if not string.match(str, "^#[%x]+$") then return 0,0,0,0 end
len = string.len(str)
if len == 7 then -- #rrggbb
r = tonumber(string.sub(str, 2, 3), 16)
g = tonumber(string.sub(str, 4, 5), 16)
b = tonumber(string.sub(str, 6, 7), 16)
a = 0xff
return r,g,b,a
end
if len == 9 then -- #rrggbbaa
r = tonumber(string.sub(str, 2, 3), 16)
g = tonumber(string.sub(str, 4, 5), 16)
b = tonumber(string.sub(str, 6, 7), 16)
a = tonumber(string.sub(str, 8, 9), 16)
return r,g,b,a
end
if len == 4 then -- #rgb
r = tonumber(string.sub(str, 2, 2), 16)
g = tonumber(string.sub(str, 3, 3), 16)
b = tonumber(string.sub(str, 4, 4), 16)
r = (r * 0x10) + r
g = (g * 0x10) + g
b = (b * 0x10) + b
a = 0xff
return r,g,b,a
end
if len == 5 then -- #rgba
r = tonumber(string.sub(str, 2, 2), 16)
g = tonumber(string.sub(str, 3, 3), 16)
b = tonumber(string.sub(str, 4, 4), 16)
a = tonumber(string.sub(str, 5, 5), 16)
r = (r * 0x10) + r
g = (g * 0x10) + g
b = (b * 0x10) + b
a = (a * 0x10) + a
return r,g,b,a
end
return 0,0,0,255
end
__color = {
__names = {
white = 0xFFFFFFFF,
black = 0xFF000000,
red = 0xFFFF0000,
green = 0xFF008000,
blue = 0xFF0000FF,
darkblue = 0xFF0000A0,
yellow = 0xFFFFFF00,
magenta = 0xFFFF00FF,
cyan = 0xFF00FFFF,
orange = 0xFFFFA500,
purple = 0xFF800080,
brown = 0xFFA52A2A,
maroon = 0xFF800000,
lime = 0xFF00FF00,
gray = 0xFF808080,
grey = 0xFF808080,
silver = 0xFFC0C0C0,
olive = 0xFF808000,
invisible = 0x00000000,
transparent = 0x00000000
},
__methods = {
--[[
Assign a value to a color object.
Accepted formats include:
- 'colorname' (eg. 'red')
- another color object
- {r,g,b} or {r,g,b,a}
- a single integer value from 0x00000000 to 0xFFFFFFFF (0xAARRGGBB)
- 3 or 4 arguments (c:set(r,g,b) or c:set(r,g,b,a))
- a string like "#aarrggbb"
--]]
set = function (self, A, B, C, D)
-- nil
if not A then
return self:set(0xFF000000)
end
-- name or #value or 0xvalue
if (type(A) == 'string') then
if string.sub(A, 1, 1) == "#" then
return self:set(__color_parse(A))
end
if string.sub(A, 1, 2) == "0x" then
return self:set(tonumber(A))
end
return self:set(__color.__names[A])
end
-- another color
if (getmetatable(A) == getmetatable(self)) then
self.r = math.floor(A.r)
self.g = math.floor(A.g)
self.b = math.floor(A.b)
self.a = math.floor(A.a)
return self
end
-- input {r,g,b} or {r,g,b,a}
if (type(A) == 'table') then
if ((not __inrange(A[1])) or (not __inrange(A[2])) or (not __inrange(A[3]))) then
error('Invalid color value: ' .. tostring(A[1]) .. " , " .. tostring(A[2]) .. " , " .. tostring(A[3]))
end
self.r = math.floor(A[1])
self.g = math.floor(A[2])
self.b = math.floor(A[3])
if (__inrange(A[4])) then self.a = math.floor(A[4]) else self.a = 255 end
return self
end
-- input single value 0xAARRGGBB
if ((B == nil) and (type(A) == 'number')) then
A = math.floor(A) -- % 0x100000000
if ((A < 0) or (A > 0xFFFFFFFF)) then
error('Invalid color value: ' .. string.format("0x%x", A))
end
self.a = math.floor(A / 0x1000000)
self.r = math.floor((A / 0x10000) % 0x100)
self.g = math.floor((A / 0x100) % 0x100)
self.b = math.floor(A % 0x100)
return self
end
-- simplest method (r,g,b[,a])
if ((not __inrange(A)) or (not __inrange(B)) or (not __inrange(C))) then
error('Invalid color value: ' .. tostring(A) .. " , " .. tostring(B) .. " , " .. tostring(C))
end
if (__inrange(D)) then self.a = math.floor(D) else self.a = 255 end
self.r = math.floor(A)
self.g = math.floor(B)
self.b = math.floor(C)
return self
end,
--[[
Multiply a color by a value (another color or an alpha value).
Returns a new value.
--]]
mul = function (self, A)
local C = __color(self)
if tonumber(A) ~= nil then
C.a = C.a * tonumber(A) / 255
else
A = __color(A)
C.r = C.r * A.r / 255
C.g = C.g * A.g / 255
C.b = C.b * A.b / 255
C.a = C.a * A.a / 255
end
return C
end,
--[[
Add a color to another.
Returns a new value.
--]]
add = function (self, A)
local C = __color(self)
A = __color(A)
C.a = math.min(C.a + A.a, 255)
C.r = math.min(C.r + A.r, 255)
C.g = math.min(C.g + A.g, 255)
C.b = math.min(C.b + A.b, 255)
return C
end,
--[[
Alpha blending function: A:blend(B) returns A.a*A.rgb + B.a*(255-A.a)*B.rgb
This blends A on top of B.
Returns a new value.
--]]
blend = function (self, A)
local C = __color(self)
A = __color(A)
C.r = ((C.a * C.r) / 255) + ((255 - C.a) * A.a) * A.r / (255 * 255);
C.g = ((C.a * C.g) / 255) + ((255 - C.a) * A.a) * A.g / (255 * 255);
C.b = ((C.a * C.b) / 255) + ((255 - C.a) * A.a) * A.b / (255 * 255);
C.a = C.a + ((255 - C.a) * A.a) / 255;
return C
end
},
__index = function (self, key)
methods = getmetatable(self).__methods
if (rawget(methods, key)) then return rawget(methods, key) end
error('Invalid index \'' .. tostring(key) .. '\' for a color')
end,
__tostring = function (self)
return string.format('#%02x%02x%02x%02x', self.r, self.g, self.b, self.a)
end,
__call = function (mt, ...)
local C = {}
setmetatable(C, mt)
return C:set(...)
end,
__mul = function (self, ...)
return __color(self):mul(...)
end,
__add = function (self, ...)
return __color(self):add(...)
end,
-- Register all global values into global env (_G)
__register = function (tbl)
for k, v in pairs(__color.__names) do
rawset(tbl, k, __color(v))
end
end,
-- Test case
__test = function ()
local A, B, C
C = __color()
assert(tostring(C) == '#000000ff')
C:set({0xFE, 0xAB, 0x12})
assert(tostring(C) == '#feab12ff')
C:set(0xFFFEAB99)
assert(tostring(C) == '#feab99ff')
C:set()
assert(tostring(C) == '#000000ff')
C:set(0xfe, 0xab, 0x12, 0xff)
assert(tostring(C) == '#feab12ff')
C = __color{0xfe, 0xab, 0x12}
assert(tostring(C) == '#feab12ff')
B = __color(C)
assert(tostring(B) == '#feab12ff')
B = B * 128
assert(tostring(B) == '#feab1280')
A = B * C
assert(tostring(A) == '#fd720180')
A = B + C
assert(tostring(A) == '#ffff24ff')
A = __color(0xFF012345):blend(0xFFFFFFFF)
assert(tostring(A) == '#012345ff')
A = __color(0x00012345):blend(0xFFFFFFFF)
assert(tostring(A) == '#ffffffff')
A = __color(0x80102030):blend(0xFFFFFFFF)
assert(tostring(A) == '#878f97ff') -- check this
A = __color(0x80102030):blend("transparent")
assert(tostring(A) == '#08101880')
A = __color("#ff0000ff") * 255
assert(tostring(A) == '#ff0000ff')
A = A * 0x80
assert(tostring(A) == '#ff000080')
assert(tostring(__color('#123')) == '#112233ff')
assert(tostring(__color('#1234')) == '#11223344')
assert(tostring(__color('#123456')) == '#123456ff')
assert(tostring(__color('#12345678')) == '#12345678')
__color.__register(_G)
assert(tostring(white) == '#ffffffff')
assert(tostring(red) == '#ff0000ff')
print('All color tests passed')
return true
end
}
setmetatable(__color, __color)
if arg and arg[1] == "-t" then __color.__test() end
return __color

View File

@ -1840,6 +1840,7 @@ void _evas_unwalk(Evas_Public_Data *e_pd);
// expose for use in engines
EAPI int _evas_module_engine_inherit(Evas_Func *funcs, char *name);
EAPI const char *_evas_module_libdir_get(void);
const char *_evas_module_datadir_get(void);
Eina_Bool evas_render_mapped(Evas_Public_Data *e, Evas_Object *obj,
Evas_Object_Protected_Data *source_pd,