From 70efb699a5c53d2e5b17fcf669ae8852a1038ce3 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Andre Date: Thu, 28 May 2015 14:37:10 +0900 Subject: [PATCH] Evas filters: Implement table & function support for curve Now the points can be specified by passing a table or a proper Lua function. The previous functionality (parsing a string) is still valid. --- src/lib/evas/filters/evas_filter_parser.c | 222 +++++++++++++++++---- src/lib/evas/filters/evas_filter_private.h | 2 +- src/lib/evas/filters/evas_filter_utils.c | 52 ++--- 3 files changed, 211 insertions(+), 65 deletions(-) diff --git a/src/lib/evas/filters/evas_filter_parser.c b/src/lib/evas/filters/evas_filter_parser.c index 60a93fe8db..0972e7c934 100644 --- a/src/lib/evas/filters/evas_filter_parser.c +++ b/src/lib/evas/filters/evas_filter_parser.c @@ -276,7 +276,8 @@ typedef enum VT_REAL, VT_STRING, VT_COLOR, - VT_BUFFER + VT_BUFFER, + VT_SPECIAL } Value_Type; typedef struct _Buffer @@ -293,7 +294,8 @@ typedef struct _Buffer Eina_Bool manual : 1; // created by "buffer" instruction } Buffer; -typedef struct _Instruction_Param +typedef struct _Instruction_Param Instruction_Param; +struct _Instruction_Param { EINA_INLIST; Eina_Stringshare *name; @@ -305,11 +307,15 @@ typedef struct _Instruction_Param char *s; unsigned int c; Buffer *buf; + struct { + void *data; + Eina_Bool (*func)(lua_State *L, int i, Evas_Filter_Program *, Evas_Filter_Instruction *, Instruction_Param *); + } special; } value; Eina_Bool set : 1; Eina_Bool allow_seq : 1; Eina_Bool allow_any_string : 1; -} Instruction_Param; +}; struct _Evas_Filter_Instruction { @@ -402,6 +408,10 @@ _instruction_param_addv(Evas_Filter_Instruction *instr, const char *name, case VT_COLOR: param->value.c = va_arg(args, DATA32); break; + case VT_SPECIAL: + param->value.special.func = va_arg(args, typeof(param->value.special.func)); + param->value.special.data = va_arg(args, void *); + break; case VT_NONE: default: return EINA_FALSE; @@ -426,8 +436,8 @@ _instruction_param_adda(Evas_Filter_Instruction *instr, const char *name, return ok; } -#define _instruction_param_seq_add(a,b,c,d) _instruction_param_adda((a),(b),(c),1,(d)) -#define _instruction_param_name_add(a,b,c,d) _instruction_param_adda((a),(b),(c),0,(d)) +#define _instruction_param_seq_add(a,b,c,...) _instruction_param_adda((a),(b),(c),1,__VA_ARGS__) +#define _instruction_param_name_add(a,b,c,...) _instruction_param_adda((a),(b),(c),0,__VA_ARGS__) static void _instruction_del(Evas_Filter_Instruction *instr) @@ -439,6 +449,8 @@ _instruction_del(Evas_Filter_Instruction *instr) { if (param->type == VT_STRING) free(param->value.s); + else if (param->type == VT_SPECIAL) + free(param->value.special.data); eina_stringshare_del(param->name); instr->params = eina_inlist_remove(instr->params, EINA_INLIST_GET(param)); free(param); @@ -510,6 +522,23 @@ _instruction_param_getc(Evas_Filter_Instruction *instr, const char *name, return 0; } +static void * +_instruction_param_getspecial(Evas_Filter_Instruction *instr, const char *name, + Eina_Bool *isset) +{ + Instruction_Param *param; + + EINA_INLIST_FOREACH(instr->params, param) + if (!strcasecmp(name, param->name)) + { + if (isset) *isset = param->set; + return param->value.special.data; + } + + if (isset) *isset = EINA_FALSE; + return 0; +} + static const char * _instruction_param_gets(Evas_Filter_Instruction *instr, const char *name, Eina_Bool *isset) @@ -1200,6 +1229,138 @@ _bump_instruction_prepare(Evas_Filter_Program *pgm, Evas_Filter_Instruction *ins return EINA_TRUE; } +static Eina_Bool +_lua_curve_points_func(lua_State *L, int i, Evas_Filter_Program *pgm EINA_UNUSED, + Evas_Filter_Instruction *instr, Instruction_Param *param) +{ + int values[256]; + char *token, *copy = NULL; + const char *points_str; + lua_Number n; + int k; + + switch (lua_type(L, i)) + { + case LUA_TTABLE: + // FIXME: indices start from 0 here. Lua prefers starting with 1. + for (k = 0; k < 256; k++) + { + lua_rawgeti(L, i, k); + if (lua_isnil(L, -1)) + { + lua_pop(L, 1); + values[k] = -1; + } + else if (lua_isnumber(L, -1)) + { + n = lua_tonumber(L, -1); + if ((n < -1) || (n > 255)) + { + WRN("Value out of range in argument '%s' of function '%s' (got %d, expected 0-255)", + param->name, instr->name, (int) n); + if (n < -1) n = 0; + if (n > 255) n = 255; + } + lua_pop(L, 1); + values[k] = (int) n; + } + else + { + lua_pop(L, 1); + ERR("Invalid value type '%s' (expected number) in table for argument '%s' of function '%s'", + lua_typename(L, -1), param->name, instr->name); + return EINA_FALSE; + } + } + break; + + case LUA_TSTRING: + for (k = 0; k < 256; k++) + values[k] = -1; + points_str = lua_tostring(L, i); + copy = strdup(points_str); + if (!copy) return EINA_FALSE; + token = strtok(copy, "-"); + if (!token) + { + ERR("Invalid string format for argument '%s' of function '%s'", + param->name, instr->name); + free(copy); + return EINA_FALSE; + } + while (token) + { + int x, y, r, minx = 0; + r = sscanf(token, "%i:%i", &x, &y); + if ((r != 2) || (x < minx) || (x >= 256)) + { + ERR("Invalid string format for argument '%s' of function '%s'", + param->name, instr->name); + free(copy); + return EINA_FALSE; + } + minx = x + 1; + if ((y < -1) || (y > 255)) + { + WRN("Value out of range in argument '%s' of function '%s' (got %d, expected 0-255)", + param->name, instr->name, y); + if (y < -1) y = 0; + if (y > 255) y = 255; + } + values[x] = y; + token = strtok(NULL, "-"); + } + free(copy); + break; + + case LUA_TFUNCTION: + for (k = 0; k < 256; k++) + { + lua_pushvalue(L, i); + lua_pushinteger(L, k); + if (!lua_pcall(L, 1, 1, 0)) + { + if (!lua_isnumber(L, -1)) + { + ERR("Function returned an invalid type '%s' (expected number) " + "in argument '%s' of function '%s'", + lua_typename(L, -1), param->name, instr->name); + return EINA_FALSE; + } + n = lua_tonumber(L, -1); + if ((n < -1) || (n > 255)) + { + WRN("Value out of range in argument '%s' of function '%s' (got %d, expected 0-255)", + param->name, instr->name, (int) n); + if (n < -1) n = 0; + if (n > 255) n = 255; + } + lua_pop(L, 1); + values[k] = (int) n; + } + else + { + ERR("Failed to call function for argument '%s' of function '%s': %s", + param->name, instr->name, lua_tostring(L, -1)); + return EINA_FALSE; + } + } + break; + + default: + ERR("Invalid type '%s' for argument '%s' of function '%s'", + lua_typename(L, i), param->name, instr->name); + return EINA_FALSE; + } + + free(param->value.special.data); + param->value.special.data = malloc(sizeof(values)); + if (!param->value.special.data) return EINA_FALSE; + memcpy(param->value.special.data, values, sizeof(values)); + + return EINA_TRUE; +} + /** @page evasfiltersref @@ -1258,7 +1419,7 @@ _curve_instruction_prepare(Evas_Filter_Program *pgm, Evas_Filter_Instruction *in // TODO: Allow passing an array of 256 values as points. // It could be easily computed from another function in the script. - _instruction_param_seq_add(instr, "points", VT_STRING, NULL); + _instruction_param_seq_add(instr, "points", VT_SPECIAL, _lua_curve_points_func, NULL); param = EINA_INLIST_CONTAINER_GET(eina_inlist_last(instr->params), Instruction_Param); param->allow_any_string = EINA_TRUE; @@ -1865,6 +2026,11 @@ _lua_parameter_parse(Evas_Filter_Program *pgm, lua_State *L, } break; } + case VT_SPECIAL: + if (!param->value.special.func || + !param->value.special.func(L, i, pgm, instr, param)) + goto fail; + break; case VT_NONE: default: // This should not happen @@ -2369,6 +2535,11 @@ _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 SETFIELD(name, val) do { lua_pushinteger(L, val); 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) + // eg. edje state_val changed but it is not used by the filter --> no redraw + // --> this needs a metatable with __index + lua_newtable(L); // "state" { SETFIELD("color", JOINC(color)); @@ -2434,6 +2605,10 @@ evas_filter_program_parse(Evas_Filter_Program *pgm, const char *str) if (!L) return EINA_FALSE; ok = !luaL_loadstring(L, str); + if (!ok) + { + ERR("Failed to load Lua program: %s", lua_tostring(L, -1)); + } #ifdef FILTERS_LEGACY_COMPAT if (!ok) @@ -2954,18 +3129,18 @@ _instr2cmd_curve(Evas_Filter_Context *ctx, { Evas_Filter_Interpolation_Mode mode = EVAS_FILTER_INTERPOLATION_MODE_LINEAR; Evas_Filter_Channel channel = EVAS_FILTER_CHANNEL_RGB; - const char *points_str, *interpolation, *channel_name; - DATA8 values[256] = {0}, points[512]; - int cmdid, point_count = 0; - char *token, *copy = NULL; + const char *interpolation, *channel_name; Buffer *src, *dst; - Eina_Bool parse_ok = EINA_FALSE; + DATA8 values[256]; + int *points; + int cmdid; src = _instruction_param_getbuf(instr, "src", NULL); dst = _instruction_param_getbuf(instr, "dst", NULL); - points_str = _instruction_param_gets(instr, "points", NULL); + points = _instruction_param_getspecial(instr, "points", NULL); interpolation = _instruction_param_gets(instr, "interpolation", NULL); channel_name = _instruction_param_gets(instr, "channel", NULL); + INSTR_PARAM_CHECK(points); INSTR_PARAM_CHECK(src); INSTR_PARAM_CHECK(dst); @@ -2989,28 +3164,7 @@ _instr2cmd_curve(Evas_Filter_Context *ctx, if (interpolation && !strcasecmp(interpolation, "none")) mode = EVAS_FILTER_INTERPOLATION_MODE_NONE; - if (!points_str) goto interpolated; - copy = strdup(points_str); - token = strtok(copy, "-"); - if (!token) goto interpolated; - - while (token) - { - int x, y, r, maxx = 0; - r = sscanf(token, "%u:%u", &x, &y); - if (r != 2) goto interpolated; - if (x < maxx || x >= 256) goto interpolated; - points[point_count * 2 + 0] = x; - points[point_count * 2 + 1] = y; - point_count++; - token = strtok(NULL, "-"); - } - - parse_ok = evas_filter_interpolate(values, points, point_count, mode); - -interpolated: - free(copy); - if (!parse_ok) + if (!evas_filter_interpolate(values, points, mode)) { int x; ERR("Failed to parse the interpolation chain"); diff --git a/src/lib/evas/filters/evas_filter_private.h b/src/lib/evas/filters/evas_filter_private.h index c613be7980..9e49aa26ad 100644 --- a/src/lib/evas/filters/evas_filter_private.h +++ b/src/lib/evas/filters/evas_filter_private.h @@ -239,7 +239,7 @@ Evas_Filter_Buffer *_filter_buffer_data_new(Evas_Filter_Context *ctx, void *data #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); +Eina_Bool evas_filter_interpolate(DATA8* output /* 256 values */, int *points /* 256 values */, Evas_Filter_Interpolation_Mode mode); Evas_Filter_Command *_evas_filter_command_get(Evas_Filter_Context *ctx, int cmdid); int evas_filter_smallest_pow2_larger_than(int val); diff --git a/src/lib/evas/filters/evas_filter_utils.c b/src/lib/evas/filters/evas_filter_utils.c index 1e27895161..088cb53c14 100644 --- a/src/lib/evas/filters/evas_filter_utils.c +++ b/src/lib/evas/filters/evas_filter_utils.c @@ -84,60 +84,52 @@ evas_filter_buffer_scaled_get(Evas_Filter_Context *ctx, } static Eina_Bool -_interpolate_none(DATA8 *output, DATA8 *points, int point_count) +_interpolate_none(DATA8 *output, int *points) { - int j, k, val, x1, x2; - for (j = 0; j < point_count; j++) + DATA8 val = 0; + int j; + for (j = 0; j < 256; j++) { - x1 = points[j * 2]; - val = points[j * 2 + 1]; - if (j < (point_count - 1)) - x2 = points[(j + 1) * 2]; + if (points[j] == -1) + output[j] = val; else - x2 = 256; - if (x2 < x1) return EINA_FALSE; - for (k = x1; k < x2; k++) - output[k] = val; + val = output[j] = (DATA8) points[j]; } return EINA_TRUE; } static Eina_Bool -_interpolate_linear(DATA8 *output, DATA8 *points, int point_count) +_interpolate_linear(DATA8 *output, int *points) { - int j, k, val1, val2, x1, x2; - for (j = 0; j < point_count; j++) + DATA8 val = 0; + int j, k, last_idx = 0; + for (j = 0; j < 256; j++) { - x1 = points[j * 2]; - val1 = points[j * 2 + 1]; - if (j < (point_count - 1)) + if (points[j] != -1) { - x2 = points[(j + 1) * 2]; - val2 = points[(j + 1) * 2 + 1]; + output[j] = (DATA8) points[j]; + for (k = last_idx + 1; k < j; k++) + output[k] = (DATA8) (points[j] + ((k - last_idx) * (points[j] - points[last_idx]) / (j - last_idx))); + last_idx = j; } - 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); } + val = (DATA8) points[last_idx]; + for (j = last_idx + 1; j < 256; j++) + output[j] = val; return EINA_TRUE; } Eina_Bool -evas_filter_interpolate(DATA8 *output, DATA8 *points, int point_count, +evas_filter_interpolate(DATA8 *output, int *points, Evas_Filter_Interpolation_Mode mode) { switch (mode) { case EVAS_FILTER_INTERPOLATION_MODE_NONE: - return _interpolate_none(output, points, point_count); + return _interpolate_none(output, points); case EVAS_FILTER_INTERPOLATION_MODE_LINEAR: default: - return _interpolate_linear(output, points, point_count); + return _interpolate_linear(output, points); } }