From 82032d494f9c5ecfa40119b95e3ad61fc83e968d Mon Sep 17 00:00:00 2001 From: Jean-Philippe Andre Date: Thu, 2 Jan 2014 15:57:44 +0900 Subject: [PATCH] Evas filters: Implement curve script API Currently supports interpolation modes "none" and "linear". Cubic interpolation is not implemented yet. --- src/lib/evas/filters/evas_filter_parser.c | 133 +++++++++++++++++---- src/lib/evas/filters/evas_filter_private.h | 10 ++ src/lib/evas/filters/evas_filter_utils.c | 67 +++++++++++ src/lib/evas/include/evas_filter.h | 11 ++ 4 files changed, 200 insertions(+), 21 deletions(-) diff --git a/src/lib/evas/filters/evas_filter_parser.c b/src/lib/evas/filters/evas_filter_parser.c index 8992f397c0..b7f7825b59 100644 --- a/src/lib/evas/filters/evas_filter_parser.c +++ b/src/lib/evas/filters/evas_filter_parser.c @@ -1,8 +1,7 @@ #include "evas_filter_private.h" #include -#define EVAS_FILTER_MODE_BUFFER (EVAS_FILTER_MODE_LAST+1) -#define EVAS_FILTER_MODE_GROW (EVAS_FILTER_MODE_LAST+2) +#define EVAS_FILTER_MODE_GROW (EVAS_FILTER_MODE_LAST+1) // Map of the most common HTML color names static struct @@ -83,6 +82,7 @@ typedef struct _Instruction_Param Eina_Value *value; Eina_Bool set : 1; Eina_Bool allow_seq : 1; + Eina_Bool allow_any_string : 1; } Instruction_Param; struct _Evas_Filter_Instruction @@ -514,7 +514,7 @@ _value_parse(Instruction_Param *param, const char *value) return EINA_TRUE; case VT_STRING: case VT_BUFFER: - PARSE_CHECK(_is_valid_string(value)); + if (!param->allow_any_string) PARSE_CHECK(_is_valid_string(value)); eina_value_set(param->value, value); return EINA_TRUE; case VT_COLOR: @@ -819,18 +819,37 @@ _bump_instruction_prepare(Evas_Filter_Instruction *instr) static Eina_Bool _curve_instruction_prepare(Evas_Filter_Instruction *instr) { + Instruction_Param *param; + 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(!strcasecmp(instr->name, "curve"), EINA_FALSE); /* - * curve TODO - * This one is a bit trickier: need interpolation functions to describe - * the curve. + * curve [points=]INTERPSTR [interpolation=]STRING (src=BUFFER) (dst=BUFFER) + * + * INTERPSTR is a STRING of the following format: + * "x1:y1-x2:y2-x3:y3...xn:yn" + * Where xk and yk are all numbers in [0-255] and xk are in increasing order + * + * The interpolation STRING can be one of: + * - none: y = yk in [xk,xk+1] (staircase effect) + * - linear: linear interpolation y = ax + b + * - cubic: cubic interpolation y = ax^3 + bx^2 + cx + d + * Invalid values default to linear */ - //instr->type = EVAS_FILTER_MODE_CURVE; - CRI("Not implemented yet"); + instr->type = EVAS_FILTER_MODE_CURVE; + + _instruction_param_seq_add(instr, "points", VT_STRING, NULL); + param = EINA_INLIST_CONTAINER_GET(eina_inlist_last(instr->params), Instruction_Param); + param->allow_any_string = EINA_TRUE; + + _instruction_param_seq_add(instr, "interpolation", VT_STRING, "linear"); + _instruction_param_seq_add(instr, "channel", VT_STRING, "rgb"); + _instruction_param_name_add(instr, "src", VT_BUFFER, "input"); + _instruction_param_name_add(instr, "dst", VT_BUFFER, "output"); + return EINA_FALSE; } @@ -910,7 +929,7 @@ _fill_instruction_prepare(Evas_Filter_Instruction *instr) * Works with both Alpha and RGBA. */ - instr->type = EVAS_FILTER_MODE_BUFFER; + instr->type = EVAS_FILTER_MODE_FILL; _instruction_param_seq_add(instr, "dst", VT_BUFFER, NULL); _instruction_param_seq_add(instr, "color", VT_COLOR, 0x0); @@ -1463,10 +1482,9 @@ _instr2cmd_fill(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm, buf = _buffer_get(pgm, bufname); - ENFN->context_color_get(ENDT, dc, &R, &G, &B, &A); - ENFN->context_color_set(ENDT, dc, R_VAL(&color), G_VAL(&color), B_VAL(&color), A_VAL(&color)); + SETCOLOR(color); cmdid = evas_filter_command_fill_add(ctx, dc, buf->cid); - ENFN->context_color_set(ENDT, dc, R, G, B, A); + RESETCOLOR(); return cmdid; } @@ -1515,10 +1533,89 @@ _instr2cmd_mask(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm, out = _buffer_get(pgm, dst); mask = _buffer_get(pgm, msk); - ENFN->context_color_get(ENDT, dc, &R, &G, &B, &A); - ENFN->context_color_set(ENDT, dc, R_VAL(&color), G_VAL(&color), B_VAL(&color), A_VAL(&color)); + SETCOLOR(color); cmdid = evas_filter_command_mask_add(ctx, dc, in->cid, mask->cid, out->cid, fillmode); - ENFN->context_color_set(ENDT, dc, R, G, B, A); + RESETCOLOR(); + + return cmdid; +} + +static int +_instr2cmd_curve(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm, + Evas_Filter_Instruction *instr, void *dc) +{ + Evas_Filter_Interpolation_Mode mode = EVAS_FILTER_INTERPOLATION_MODE_LINEAR; + Evas_Filter_Channel channel = EVAS_FILTER_CHANNEL_RGB; + const char *src, *dst, *points_str, *interpolation, *channel_name; + DATA8 values[256] = {0}, points[512]; + int cmdid, point_count = 0; + char *token, *copy, *saveptr; + Buffer *in, *out; + Eina_Bool parse_ok = EINA_FALSE; + + src = _instruction_param_gets(instr, "src", NULL); + dst = _instruction_param_gets(instr, "dst", NULL); + points_str = _instruction_param_gets(instr, "points", NULL); + interpolation = _instruction_param_gets(instr, "interpolation", NULL); + channel_name = _instruction_param_gets(instr, "channel", NULL); + + if (channel_name) + { + if (tolower(*channel_name) == 'r') + { + if (!strcasecmp(channel_name, "rgb")) + channel = EVAS_FILTER_CHANNEL_RGB; + else + channel = EVAS_FILTER_CHANNEL_RED; + } + else if (tolower(*channel_name) == 'g') + channel = EVAS_FILTER_CHANNEL_GREEN; + else if (tolower(*channel_name) == 'b') + channel = EVAS_FILTER_CHANNEL_BLUE; + else if (tolower(*channel_name) == 'a') + channel = EVAS_FILTER_CHANNEL_ALPHA; + } + + if (interpolation) + { + if (!strcasecmp(interpolation, "none")) + mode = EVAS_FILTER_INTERPOLATION_MODE_NONE; + else if (!strcasecmp(interpolation, "cubic")) + mode = EVAS_FILTER_INTERPOLATION_MODE_CUBIC; + } + + if (!points_str) goto interpolated; + copy = strdup(points_str); + token = strtok_r(copy, "-", &saveptr); + 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_r(NULL, "-", &saveptr); + } + + parse_ok = evas_filter_interpolate(values, points, point_count, mode); + +interpolated: + free(copy); + if (!parse_ok) + { + int x; + ERR("Failed to parse the interpolation chain"); + for (x = 0; x < 256; x++) + values[x] = x; + } + + in = _buffer_get(pgm, src); + out = _buffer_get(pgm, dst); + cmdid = evas_filter_command_curve_add(ctx, dc, in->cid, out->cid, values, channel); return cmdid; } @@ -1553,15 +1650,9 @@ _command_from_instruction(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm, case EVAS_FILTER_MODE_MASK: instr2cmd = _instr2cmd_mask; break; -#if 0 case EVAS_FILTER_MODE_CURVE: instr2cmd = _instr2cmd_curve; break; -#endif - case EVAS_FILTER_MODE_CURVE: - CRI("Not implemented yet"); - return -1; - case EVAS_FILTER_MODE_BUFFER: default: CRI("Invalid instruction type: %d", instr->type); return -1; diff --git a/src/lib/evas/filters/evas_filter_private.h b/src/lib/evas/filters/evas_filter_private.h index 07dcf20399..0a0c85e3c4 100644 --- a/src/lib/evas/filters/evas_filter_private.h +++ b/src/lib/evas/filters/evas_filter_private.h @@ -36,6 +36,8 @@ #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; @@ -126,6 +128,13 @@ struct _Evas_Filter_Buffer Eina_Bool locked : 1; // internal flag }; +enum _Evas_Filter_Interpolation_Mode +{ + EVAS_FILTER_INTERPOLATION_MODE_NONE, + EVAS_FILTER_INTERPOLATION_MODE_LINEAR, + EVAS_FILTER_INTERPOLATION_MODE_CUBIC +}; + void evas_filter_context_clear(Evas_Filter_Context *ctx); void evas_filter_context_proxy_bind(Evas_Filter_Context *ctx, Evas_Object *eo_proxy, Evas_Object *eo_source, int bufid); @@ -144,5 +153,6 @@ 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); 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, int w, int h); +Eina_Bool evas_filter_interpolate(DATA8* output /* 256 values */, DATA8* points /* pairs x + y */, int point_count, Evas_Filter_Interpolation_Mode mode); #endif // EVAS_FILTER_PRIVATE_H diff --git a/src/lib/evas/filters/evas_filter_utils.c b/src/lib/evas/filters/evas_filter_utils.c index f07d69c250..340c7d272f 100644 --- a/src/lib/evas/filters/evas_filter_utils.c +++ b/src/lib/evas/filters/evas_filter_utils.c @@ -50,3 +50,70 @@ evas_filter_buffer_scaled_get(Evas_Filter_Context *ctx, 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; +} + +static Eina_Bool +_interpolate_cubic(DATA8 *output, DATA8 *points, int point_count) +{ + CRI("Not implemented yet"); + return EINA_FALSE; +} + +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_CUBIC: + return _interpolate_cubic(output, points, point_count); + case EVAS_FILTER_INTERPOLATION_MODE_LINEAR: + default: + return _interpolate_linear(output, points, point_count); + } +} diff --git a/src/lib/evas/include/evas_filter.h b/src/lib/evas/include/evas_filter.h index 4fe04f7a30..59b430ba50 100644 --- a/src/lib/evas/include/evas_filter.h +++ b/src/lib/evas/include/evas_filter.h @@ -150,6 +150,17 @@ int evas_filter_command_blur_add(Evas_Filter_Context *ctx, * @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); /**