Evas filters: Implement curve script API

Currently supports interpolation modes "none" and "linear".
Cubic interpolation is not implemented yet.
This commit is contained in:
Jean-Philippe Andre 2014-01-02 15:57:44 +09:00
parent 7000907cd3
commit 82032d494f
4 changed files with 200 additions and 21 deletions

View File

@ -1,8 +1,7 @@
#include "evas_filter_private.h"
#include <stdarg.h>
#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;

View File

@ -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

View File

@ -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);
}
}

View File

@ -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);
/**