jpeg pushed a commit to branch master.

http://git.enlightenment.org/core/efl.git/commit/?id=f3e16bc4854268cecc19a38671c6f68071b7955b

commit f3e16bc4854268cecc19a38671c6f68071b7955b
Author: Jean-Philippe Andre <jp.an...@samsung.com>
Date:   Thu Jun 4 19:42:38 2015 +0900

    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.
---
 src/Makefile_Evas.am                       |   8 +
 src/lib/evas/file/evas_module.c            |  10 +
 src/lib/evas/filters/evas_filter.c         |   1 +
 src/lib/evas/filters/evas_filter_parser.c  | 581 ++++++++++++++++-------------
 src/lib/evas/filters/evas_filter_private.h |   2 +
 src/lib/evas/filters/lua/color.lua         | 302 +++++++++++++++
 src/lib/evas/include/evas_private.h        |   1 +
 7 files changed, 638 insertions(+), 267 deletions(-)

diff --git a/src/Makefile_Evas.am b/src/Makefile_Evas.am
index 941e1d2..ef59a2f 100644
--- a/src/Makefile_Evas.am
+++ b/src/Makefile_Evas.am
@@ -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)
diff --git a/src/lib/evas/file/evas_module.c b/src/lib/evas/file/evas_module.c
index ead2612..bafc6e7 100644
--- a/src/lib/evas/file/evas_module.c
+++ b/src/lib/evas/file/evas_module.c
@@ -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)
 {
diff --git a/src/lib/evas/filters/evas_filter.c 
b/src/lib/evas/filters/evas_filter.c
index 1e369f0..d420929 100644
--- a/src/lib/evas/filters/evas_filter.c
+++ b/src/lib/evas/filters/evas_filter.c
@@ -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;
 }
diff --git a/src/lib/evas/filters/evas_filter_parser.c 
b/src/lib/evas/filters/evas_filter_parser.c
index c5f409d..8992dcb 100644
--- a/src/lib/evas/filters/evas_filter_parser.c
+++ b/src/lib/evas/filters/evas_filter_parser.c
@@ -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
-     {
-        DBG("Unknown index '%s' for a buffer", key);
-        return 0;
-     }
-}
+     return luaL_error(L, "Unknown index '%s' for a buffer", key);
 
-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;
+   return 0;
 }
 
-static int
-_lua_buffer_name(lua_State *L)
+// remove metatable from first argument if this is a __call metafunction
+static inline int
+_lua_implicit_metatable_drop(lua_State *L, const char *name)
 {
-   Buffer *buf, **pbuf;
-   pbuf = lua_touserdata(L, 1);
-   buf = pbuf ? *pbuf : NULL;
-   if (!buf) return 0;
-   lua_pushstring(L, buf->name);
-   return 1;
+   int ret = 0;
+   if (lua_istable(L, 1) && lua_getmetatable(L, 1))
+     {
+        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_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)
-{
-   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;
+// [-1, +1, e] -- converts the top of the stack to a valid 'color' object
+static Eina_Bool
+_lua_convert_color(lua_State *L)
+{
+   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;
+}
diff --git a/src/lib/evas/filters/evas_filter_private.h 
b/src/lib/evas/filters/evas_filter_private.h
index 002b2da..7a4ba85 100644
--- a/src/lib/evas/filters/evas_filter_private.h
+++ b/src/lib/evas/filters/evas_filter_private.h
@@ -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
diff --git a/src/lib/evas/filters/lua/color.lua 
b/src/lib/evas/filters/lua/color.lua
new file mode 100644
index 0000000..3fb1e9b
--- /dev/null
+++ b/src/lib/evas/filters/lua/color.lua
@@ -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
diff --git a/src/lib/evas/include/evas_private.h 
b/src/lib/evas/include/evas_private.h
index 163ed0e..215e996 100644
--- a/src/lib/evas/include/evas_private.h
+++ b/src/lib/evas/include/evas_private.h
@@ -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,

-- 


Reply via email to