forked from enlightenment/efl
2366 lines
72 KiB
C
2366 lines
72 KiB
C
#include "evas_filter_private.h"
|
|
#include <stdarg.h>
|
|
|
|
#define EVAS_FILTER_MODE_GROW (EVAS_FILTER_MODE_LAST+1)
|
|
|
|
/* Note on the documentation:
|
|
* To keep it simple, I'm not using any fancy features, only <ul>/<li> lists
|
|
* and @a, @b, @c flags from Doxygen.
|
|
* Let's keep it that way.
|
|
*
|
|
* This is a REFERENCE documentation, not supposed to contain tons of examples,
|
|
* but each filter command should have one simple copy and pasteable example.
|
|
*/
|
|
|
|
/**
|
|
@page evasfiltersref Evas filters reference
|
|
|
|
The Evas filters are a combination of filters used to apply specific effects
|
|
to an @ref Evas_Object "Evas Object". For the moment, these effects are
|
|
specific to the @ref Evas_Object_Text "Text Objects".
|
|
|
|
The filters can be applied to an object using a simple script language
|
|
specifically designed for these effects. A script will contain a series
|
|
of buffer declarations and filter commands to apply to these buffers.
|
|
|
|
Basically, when applying an effect to a @ref Evas_Object_Text "Text Object",
|
|
an alpha-only @c input buffer is created, where the text is rendered, and
|
|
an RGBA @c output buffer is created, where the text with effects shall be
|
|
finally rendered.
|
|
|
|
The script language is case insensitive, except for the buffer names.
|
|
All spaces will be discarded during parsing.
|
|
|
|
Here are the available commands:
|
|
<ul>
|
|
<li> @ref sec_syntax "Syntax" </li>
|
|
<li> @ref sec_buffers "Buffer management" </li>
|
|
<ul>
|
|
<li> @ref sec_buffers_cspace "Colorspaces" </li>
|
|
<li> @ref sec_buffers_auto "Automatic buffers" </li>
|
|
<li> @ref sec_buffers_cmd "BUFFER command" </li>
|
|
</ul>
|
|
<li> @ref sec_commands "Commands" </li>
|
|
<ul>
|
|
<li> @ref sec_commands_blend "BLEND command"</li>
|
|
<li> @ref sec_commands_blur "BLUR command"</li>
|
|
<li> @ref sec_commands_grow "GROW command"</li>
|
|
<li> @ref sec_commands_curve "CURVE command"</li>
|
|
<li> @ref sec_commands_fill "FILL command"</li>
|
|
<li> @ref sec_commands_mask "MASK command"</li>
|
|
<li> @ref sec_commands_bump "BUMP command"</li>
|
|
<li> @ref sec_commands_displace "DISPLACE command"</li>
|
|
<li> @ref sec_commands_transform "TRANSFORM command"</li>
|
|
</ul>
|
|
</ul>
|
|
|
|
All the examples in this page can (should) be directly used in
|
|
@ref evas_obj_text_filter_program_set.
|
|
|
|
Note that most of the text effects work better with larger font sizes (> 50px),
|
|
and so do the examples in this page.
|
|
*/
|
|
|
|
/**
|
|
@page evasfiltersref
|
|
@section sec_syntax Syntax
|
|
|
|
Here is a simple example illustrating the syntax:
|
|
|
|
@include filter_example_1.txt
|
|
|
|
This example will display a cyan and dark blue glow surrounding the
|
|
main text (its color depends on the object's theme).
|
|
|
|
<center>
|
|
@image html filter_example_1.png
|
|
</center>
|
|
|
|
The syntax is pretty simple and follows a small set of rules:
|
|
<ul>
|
|
<li>All whitespaces are discarded</li>
|
|
<li>All commands are case-insensitive, except for the buffer and source names</li>
|
|
<li>All dimensions are in pixels</li>
|
|
<li>The commands will be executed in sequential order</li>
|
|
<li>All commands must be terminated by a semicolon ';'</li>
|
|
<li>Most commands have default values</li>
|
|
<li>A command argument can either be set by name, or sequentially omitting the name (similarily to Python)</li>
|
|
<li>Boolean values can be either 1/0, on/off, yes/no, enabled/disabled, true/false</li>
|
|
</ul>
|
|
|
|
Since the spaces are discarded, the above code is equivalent to:
|
|
@code
|
|
buffer:fat(alpha);grow(5,dst=fat);blur(8,src=fat,color=darkblue);blur(4,color=cyan);blend();
|
|
@endcode
|
|
|
|
<h3>Special keywords and their values</h3>
|
|
|
|
Some options accept a certain set of values (like enums):
|
|
<ul>
|
|
<li>Booleans</li>
|
|
<ul>
|
|
<li>1/0, on/off, yes/no, enabled/disabled, true/false</li>
|
|
</ul>
|
|
@anchor evasfilters_color
|
|
<li>Color</li>
|
|
<ul>
|
|
<li>Hexademical values: @c #RRGGBB, @c #RRGGBBAA, @c #RGB, @c #RGBA</li>
|
|
<li>white: @c #FFFFFF</li>
|
|
<li>black: @c #000000</li>
|
|
<li>red: @c #FF0000</li>
|
|
<li>green: @c #008000</li>
|
|
<li>blue: @c #0000FF</li>
|
|
<li>darkblue: @c #0000A0</li>
|
|
<li>yellow: @c #FFFF00</li>
|
|
<li>magenta: @c #FF00FF</li>
|
|
<li>cyan: @c #00FFFF</li>
|
|
<li>orange: @c #FFA500</li>
|
|
<li>purple: @c #800080</li>
|
|
<li>brown: @c #A52A2A</li>
|
|
<li>maroon: @c #800000</li>
|
|
<li>lime: @c #00FF00</li>
|
|
<li>gray: @c #808080</li>
|
|
<li>grey: @c #808080</li>
|
|
<li>silver: @c #C0C0C0</li>
|
|
<li>olive: @c #808000</li>
|
|
<li>invisible, transparent: @c #0000 (alpha is zero)</li>
|
|
</ul>
|
|
<li>Fillmode</li>
|
|
<ul>
|
|
<li>none</li>
|
|
<li>stretch_x</li>
|
|
<li>stretch_y</li>
|
|
<li>repeat_x</li>
|
|
<li>repeat_y</li>
|
|
<li>repeat_x_stretch_y, stretch_y_repeat_x</li>
|
|
<li>repeat_y_stretch_x, stretch_x_repeat_y</li>
|
|
<li>repeat, repeat_xy</li>
|
|
<li>stretch, stretch_xy</li>
|
|
</ul>
|
|
</ul>
|
|
*/
|
|
|
|
/**
|
|
@page evasfiltersref
|
|
@section sec_buffers Buffer management
|
|
|
|
The Evas filters subsystem is based on the concept of using various
|
|
buffers as image layers and drawing or applying filters to these buffers.
|
|
|
|
Most of the buffers are allocated automatically at runtime, depending on the
|
|
various inputs and commands used (eg. 2-D blur will require a temporary
|
|
intermediate buffer).
|
|
|
|
@subsection sec_buffers_cspace Colorspaces and size
|
|
|
|
The buffers' size will be automatically defined at runtime, based on the
|
|
content of the input and the series of operations to apply (eg. blur adds
|
|
some necessary margins).
|
|
|
|
The buffers can be either ALPHA (1 color channel only) or RGBA (full color).
|
|
Some operations might require specifically an ALPHA buffer, some others RGBA.
|
|
|
|
Most buffers will have the same size, except those specified by an external
|
|
source.
|
|
|
|
|
|
@subsection sec_buffers_auto Automatic buffers
|
|
|
|
The two most important buffers, input and output, are statically defined and
|
|
always present when running a filter. input is an ALPHA buffer, containing
|
|
the @ref Evas_Object_Text "Text Object"'s rendered text, and output is the
|
|
final target on which to render as RGBA.
|
|
|
|
Some operations, like 2-D blur might require temporary intermediate buffers,
|
|
that will be allocated automatically. Those buffers are internal only and
|
|
can't be used from the script.
|
|
|
|
Finally, if a buffer is created using another Evas Object as source (see
|
|
@ref sec_buffers_cmd "buffer" for more details), its pixel data will be filled
|
|
by rendering the Evas Object into this buffer. This is how it will be
|
|
possible to load external images, textures and even animations into a buffer.
|
|
|
|
|
|
@subsection sec_buffers_cmd Buffer command
|
|
|
|
@code
|
|
buffer : name;
|
|
buffer : name (alpha);
|
|
buffer : name (rgba);
|
|
buffer : name (src = partname);
|
|
@endcode
|
|
|
|
The "buffer" instruction is a @a special command used to declare a new buffer
|
|
in the filters context. This buffer can be either ALPHA, RGBA or based on
|
|
an other Evas Object (proxy source).
|
|
If no option is given, an RGBA buffer will be created.
|
|
|
|
@param name An alpha-numerical name, starting with a letter (a-z, A-Z).
|
|
Can not be @c input or @c output, as these are reserved names.
|
|
Must be unique.
|
|
|
|
@param (args) [alpha] OR [rgba] OR [src = partname] <br>
|
|
Create a new named buffer, specify its colorspace or source. Possible options:
|
|
@li @c alpha: Create an alpha-only buffer (1 channel, no color)
|
|
@li @c rgba: Create an RGBA buffer (4 channels, full color)
|
|
@li <tt>src = partname</tt>: Use another <tt>Evas Object</tt> as source for this
|
|
buffer's pixels. The name can either be an Edje part name or the one
|
|
specified in evas_obj_text_filter_source_set.
|
|
|
|
@see evas_obj_text_filter_source_set
|
|
|
|
@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;
|
|
Evas_Filter_Fill_Mode value;
|
|
} fill_modes[] =
|
|
{
|
|
{ "none", EVAS_FILTER_FILL_MODE_NONE },
|
|
{ "stretch_x", EVAS_FILTER_FILL_MODE_STRETCH_X },
|
|
{ "stretch_y", EVAS_FILTER_FILL_MODE_STRETCH_Y },
|
|
{ "repeat_x", EVAS_FILTER_FILL_MODE_REPEAT_X },
|
|
{ "repeat_y", EVAS_FILTER_FILL_MODE_REPEAT_Y },
|
|
{ "repeat_x_stretch_y", EVAS_FILTER_FILL_MODE_REPEAT_X_STRETCH_Y },
|
|
{ "repeat_y_stretch_x", EVAS_FILTER_FILL_MODE_REPEAT_Y_STRETCH_X },
|
|
{ "stretch_y_repeat_x", EVAS_FILTER_FILL_MODE_REPEAT_X_STRETCH_Y }, // alias
|
|
{ "stretch_x_repeat_y", EVAS_FILTER_FILL_MODE_REPEAT_Y_STRETCH_X }, // alias
|
|
{ "repeat", EVAS_FILTER_FILL_MODE_REPEAT_XY }, // alias
|
|
{ "repeat_xy", EVAS_FILTER_FILL_MODE_REPEAT_XY },
|
|
{ "stretch", EVAS_FILTER_FILL_MODE_STRETCH_XY }, // alias
|
|
{ "stretch_xy", EVAS_FILTER_FILL_MODE_STRETCH_XY }
|
|
};
|
|
|
|
typedef enum
|
|
{
|
|
VT_NONE,
|
|
VT_BOOL,
|
|
VT_INT,
|
|
VT_REAL,
|
|
VT_STRING,
|
|
VT_COLOR,
|
|
VT_BUFFER
|
|
} Value_Type;
|
|
|
|
typedef struct _Buffer
|
|
{
|
|
EINA_INLIST;
|
|
Eina_Stringshare *name;
|
|
Eina_Stringshare *proxy;
|
|
int cid; // Transient value
|
|
struct {
|
|
int l, r, t, b; // Used for padding calculation. Can change over time.
|
|
} pad;
|
|
Eina_Bool alpha : 1;
|
|
} Buffer;
|
|
|
|
typedef struct _Instruction_Param
|
|
{
|
|
EINA_INLIST;
|
|
Eina_Stringshare *name;
|
|
Value_Type type;
|
|
Eina_Value *value;
|
|
Eina_Bool set : 1;
|
|
Eina_Bool allow_seq : 1;
|
|
Eina_Bool allow_any_string : 1;
|
|
} Instruction_Param;
|
|
|
|
struct _Evas_Filter_Instruction
|
|
{
|
|
EINA_INLIST;
|
|
Eina_Stringshare *name;
|
|
int /* Evas_Filter_Mode */ type;
|
|
Eina_Inlist /* Instruction_Param */ *params;
|
|
struct
|
|
{
|
|
void (* update) (Evas_Filter_Program *, Evas_Filter_Instruction *, int *, int *, int *, int *);
|
|
} pad;
|
|
Eina_Bool valid : 1;
|
|
};
|
|
|
|
struct _Evas_Filter_Program
|
|
{
|
|
Eina_Stringshare *name; // Optional for now
|
|
Eina_Hash /* const char * : Evas_Filter_Proxy_Binding */ *proxies;
|
|
Eina_Inlist /* Evas_Filter_Instruction */ *instructions;
|
|
Eina_Inlist /* Buffer */ *buffers;
|
|
Eina_Bool valid : 1;
|
|
};
|
|
|
|
/* Instructions */
|
|
static Evas_Filter_Instruction *
|
|
_instruction_new(const char *name)
|
|
{
|
|
Evas_Filter_Instruction *instr;
|
|
|
|
instr = calloc(1, sizeof(Evas_Filter_Instruction));
|
|
instr->name = eina_stringshare_add(name);
|
|
|
|
return instr;
|
|
}
|
|
|
|
static Eina_Bool
|
|
_instruction_param_addv(Evas_Filter_Instruction *instr, const char *name,
|
|
Value_Type format, Eina_Bool sequential, va_list args)
|
|
{
|
|
const Eina_Value_Type *type = NULL;
|
|
Instruction_Param *param;
|
|
|
|
switch (format)
|
|
{
|
|
case VT_BOOL:
|
|
case VT_INT:
|
|
type = EINA_VALUE_TYPE_INT;
|
|
break;
|
|
case VT_REAL:
|
|
type = EINA_VALUE_TYPE_DOUBLE;
|
|
break;
|
|
case VT_STRING:
|
|
case VT_BUFFER:
|
|
type = EINA_VALUE_TYPE_STRING;
|
|
break;
|
|
case VT_COLOR:
|
|
type = EINA_VALUE_TYPE_UINT;
|
|
break;
|
|
case VT_NONE:
|
|
default:
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
param = calloc(1, sizeof(Instruction_Param));
|
|
param->name = eina_stringshare_add(name);
|
|
param->type = format;
|
|
param->value = eina_value_new(type);
|
|
param->allow_seq = sequential;
|
|
eina_value_vset(param->value, args);
|
|
instr->params = eina_inlist_append(instr->params, EINA_INLIST_GET(param));
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
static Eina_Bool
|
|
_instruction_param_adda(Evas_Filter_Instruction *instr, const char *name,
|
|
Value_Type format, Eina_Bool sequential,
|
|
/* default value */ ...)
|
|
{
|
|
Eina_Bool ok;
|
|
va_list args;
|
|
|
|
va_start(args, sequential);
|
|
ok = _instruction_param_addv(instr, name, format, sequential, args);
|
|
va_end(args);
|
|
|
|
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))
|
|
|
|
static void
|
|
_instruction_del(Evas_Filter_Instruction *instr)
|
|
{
|
|
Instruction_Param *param;
|
|
|
|
if (!instr) return;
|
|
EINA_INLIST_FREE(instr->params, param)
|
|
{
|
|
eina_value_free(param->value);
|
|
eina_stringshare_del(param->name);
|
|
instr->params = eina_inlist_remove(instr->params, EINA_INLIST_GET(param));
|
|
free(param);
|
|
}
|
|
eina_stringshare_del(instr->name);
|
|
free(instr);
|
|
}
|
|
|
|
static int
|
|
_instruction_param_geti(Evas_Filter_Instruction *instr, const char *name,
|
|
Eina_Bool *isset)
|
|
{
|
|
Instruction_Param *param;
|
|
int i = 0;
|
|
|
|
EINA_INLIST_FOREACH(instr->params, param)
|
|
if (!strcasecmp(name, param->name))
|
|
{
|
|
if (eina_value_get(param->value, &i))
|
|
{
|
|
if (isset) *isset = param->set;
|
|
return i;
|
|
}
|
|
else return -1;
|
|
}
|
|
|
|
if (isset) *isset = EINA_FALSE;
|
|
return -1;
|
|
}
|
|
|
|
static double
|
|
_instruction_param_getd(Evas_Filter_Instruction *instr, const char *name,
|
|
Eina_Bool *isset)
|
|
{
|
|
Instruction_Param *param;
|
|
double i = 0;
|
|
|
|
EINA_INLIST_FOREACH(instr->params, param)
|
|
if (!strcasecmp(name, param->name))
|
|
{
|
|
if (eina_value_get(param->value, &i))
|
|
{
|
|
if (isset) *isset = param->set;
|
|
return i;
|
|
}
|
|
else return 0.0;
|
|
}
|
|
|
|
if (isset) *isset = EINA_FALSE;
|
|
return 0.0;
|
|
}
|
|
|
|
static DATA32
|
|
_instruction_param_getc(Evas_Filter_Instruction *instr, const char *name,
|
|
Eina_Bool *isset)
|
|
{
|
|
Instruction_Param *param;
|
|
DATA32 i = 0;
|
|
|
|
EINA_INLIST_FOREACH(instr->params, param)
|
|
if (!strcasecmp(name, param->name))
|
|
{
|
|
if (eina_value_get(param->value, &i))
|
|
{
|
|
if (isset) *isset = param->set;
|
|
return i;
|
|
}
|
|
else return 0;
|
|
}
|
|
|
|
if (isset) *isset = EINA_FALSE;
|
|
return 0;
|
|
}
|
|
|
|
static const char *
|
|
_instruction_param_gets(Evas_Filter_Instruction *instr, const char *name,
|
|
Eina_Bool *isset)
|
|
{
|
|
Instruction_Param *param;
|
|
const char *str = NULL;
|
|
|
|
EINA_INLIST_FOREACH(instr->params, param)
|
|
if (!strcasecmp(name, param->name))
|
|
{
|
|
if (eina_value_get(param->value, &str))
|
|
{
|
|
if (isset) *isset = param->set;
|
|
return str;
|
|
}
|
|
else return NULL;
|
|
}
|
|
|
|
if (isset) *isset = EINA_FALSE;
|
|
return NULL;
|
|
}
|
|
|
|
/* Parsing format: func ( arg , arg2 , argname=val1, argname2 = val2 ) */
|
|
|
|
#define CHARS_ALPHABET "abcdefghijklmnopqrstuvwxyzABCDEFGHJIKLMNOPQRSTUVWXYZ"
|
|
#define CHARS_NUMS "0123456789"
|
|
#define CHARS_DELIMS "=-(),;#.:_"
|
|
static const char *allowed_chars = CHARS_ALPHABET CHARS_NUMS "_";
|
|
static const char *allowed_delim = CHARS_DELIMS;
|
|
|
|
static char *
|
|
_whitespace_ignore_strdup(const char *str)
|
|
{
|
|
Eina_Bool inword = EINA_FALSE, wasword = EINA_FALSE;
|
|
char *dst, *ptr, *next;
|
|
int len;
|
|
|
|
if (!str) return NULL;
|
|
len = strlen(str);
|
|
dst = calloc(len + 1, 1);
|
|
|
|
// TODO: Support quoted strings ("string" or 'string')
|
|
|
|
ptr = dst;
|
|
for (; *str; str++)
|
|
{
|
|
if (isspace(*str))
|
|
{
|
|
wasword = inword;
|
|
inword = EINA_FALSE;
|
|
}
|
|
else if (isalpha(*str) || isdigit(*str))
|
|
{
|
|
if (wasword)
|
|
{
|
|
ERR("Invalid space found in program code");
|
|
goto invalid;
|
|
}
|
|
inword = EINA_TRUE;
|
|
*ptr++ = *str;
|
|
}
|
|
else if (*str == '/')
|
|
{
|
|
if (str[1] == '*')
|
|
{
|
|
next = strstr(str + 2, "*/");
|
|
if (!next)
|
|
{
|
|
ERR("Unterminated comment section, \"*/\" was not found");
|
|
goto invalid;
|
|
}
|
|
str = next + 1;
|
|
}
|
|
else if (str[1] == '/')
|
|
{
|
|
next = strchr(str + 2, '\n');
|
|
if (!next) break;
|
|
str = next;
|
|
}
|
|
else
|
|
{
|
|
ERR("Character '/' not followed by '/' or '*' is invalid");
|
|
goto invalid;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!strchr(allowed_delim, *str))
|
|
{
|
|
ERR("Character '%1.1s' is not allowed", str);
|
|
goto invalid;
|
|
}
|
|
wasword = inword = EINA_FALSE;
|
|
*ptr++ = *str;
|
|
}
|
|
}
|
|
|
|
*ptr = 0;
|
|
return dst;
|
|
|
|
invalid:
|
|
free(dst);
|
|
return NULL;
|
|
}
|
|
|
|
// "key", alphanumeric chars only, starting with a letter
|
|
static Eina_Bool
|
|
_is_valid_string(const char *str)
|
|
{
|
|
if (!str)
|
|
return EINA_FALSE;
|
|
if (!isalpha(*str++))
|
|
return EINA_FALSE;
|
|
for (; *str; str++)
|
|
if (!isalpha(*str) && !isdigit(*str) && (*str != '_'))
|
|
return EINA_FALSE;
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
// valid number:
|
|
static Eina_Bool
|
|
_is_valid_number(const char *str)
|
|
{
|
|
Eina_Bool dot = EINA_FALSE;
|
|
if (!str || !*str) return EINA_FALSE;
|
|
for (; *str; str++)
|
|
{
|
|
if (!isdigit(*str))
|
|
{
|
|
if (dot) return EINA_FALSE;
|
|
if (*str == '.') dot = EINA_TRUE;
|
|
}
|
|
}
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
// FIXME/TODO: Add support for strings with "" AND/OR ''
|
|
|
|
// "key=val"
|
|
static Eina_Bool
|
|
_is_valid_keyval(const char *str)
|
|
{
|
|
char *equal;
|
|
Eina_Bool ok = EINA_TRUE;
|
|
|
|
if (!str) return EINA_FALSE;
|
|
equal = strchr(str, '=');
|
|
if (!equal) return EINA_FALSE;
|
|
*equal = 0;
|
|
if (!_is_valid_string(str))
|
|
ok = EINA_FALSE;
|
|
else if (!_is_valid_string(equal + 1))
|
|
{
|
|
if (!_is_valid_number(equal + 1))
|
|
ok = EINA_FALSE;
|
|
}
|
|
*equal = '=';
|
|
return ok;
|
|
}
|
|
|
|
static Eina_Bool
|
|
_bool_parse(const char *str, Eina_Bool *b)
|
|
{
|
|
if (!str || !*str) return EINA_FALSE;
|
|
if (!strcmp(str, "1") ||
|
|
!strcasecmp(str, "yes") ||
|
|
!strcasecmp(str, "on") ||
|
|
!strcasecmp(str, "enable") ||
|
|
!strcasecmp(str, "enabled") ||
|
|
!strcasecmp(str, "true"))
|
|
{
|
|
if (b) *b = EINA_TRUE;
|
|
return EINA_TRUE;
|
|
}
|
|
else if (!strcmp(str, "0") ||
|
|
!strcasecmp(str, "no") ||
|
|
!strcasecmp(str, "off") ||
|
|
!strcasecmp(str, "disable") ||
|
|
!strcasecmp(str, "disabled") ||
|
|
!strcasecmp(str, "false"))
|
|
{
|
|
if (b) *b = EINA_FALSE;
|
|
return EINA_TRUE;
|
|
}
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
#define PARSE_ABORT() do {} while (0)
|
|
//#define PARSE_ABORT() abort()
|
|
|
|
#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)
|
|
{
|
|
unsigned long 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 = (DATA32) value;
|
|
success = EINA_TRUE;
|
|
|
|
end:
|
|
return success;
|
|
}
|
|
|
|
static Eina_Bool
|
|
_value_parse(Instruction_Param *param, const char *value)
|
|
{
|
|
Eina_Bool b;
|
|
DATA32 color;
|
|
double d;
|
|
int i;
|
|
|
|
switch (param->type)
|
|
{
|
|
case VT_BOOL:
|
|
PARSE_CHECK(_bool_parse(value, &b));
|
|
eina_value_set(param->value, b ? 1 : 0);
|
|
return EINA_TRUE;
|
|
case VT_INT:
|
|
PARSE_CHECK(sscanf(value, "%d", &i) == 1);
|
|
eina_value_set(param->value, i);
|
|
return EINA_TRUE;
|
|
case VT_REAL:
|
|
PARSE_CHECK(sscanf(value, "%lf", &d) == 1);
|
|
eina_value_set(param->value, d);
|
|
return EINA_TRUE;
|
|
case VT_STRING:
|
|
case VT_BUFFER:
|
|
if (!param->allow_any_string) PARSE_CHECK(_is_valid_string(value));
|
|
eina_value_set(param->value, value);
|
|
return EINA_TRUE;
|
|
case VT_COLOR:
|
|
PARSE_CHECK(_color_parse(value, &color));
|
|
eina_value_set(param->value, color);
|
|
return EINA_TRUE;
|
|
case VT_NONE:
|
|
default:
|
|
PARSE_CHECK(!"invalid value type");
|
|
}
|
|
|
|
end:
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
static Eina_Bool
|
|
_instruction_parse(Evas_Filter_Instruction *instr, const char *string)
|
|
{
|
|
Instruction_Param *param = NULL;
|
|
char *str = NULL, *token = NULL, *next = NULL, *optval = NULL, *optname = NULL;
|
|
Eina_Bool last = EINA_FALSE, namedargs = EINA_FALSE, success = EINA_FALSE;
|
|
int seqorder = 0;
|
|
|
|
instr->valid = EINA_FALSE;
|
|
PARSE_CHECK(string);
|
|
|
|
EINA_INLIST_FOREACH(instr->params, param)
|
|
param->set = EINA_FALSE;
|
|
|
|
// Copy and remove whitespaces now
|
|
str = _whitespace_ignore_strdup(string);
|
|
PARSE_CHECK(str);
|
|
token = str;
|
|
|
|
// Check instruction matches function name
|
|
next = strchr(token, '(');
|
|
PARSE_CHECK(next);
|
|
*next++ = 0;
|
|
PARSE_CHECK(!strcasecmp(token, instr->name));
|
|
|
|
// Read arguments
|
|
while (((token = strsep(&next, ",")) != NULL) && (!last))
|
|
{
|
|
Eina_Bool found = EINA_FALSE;
|
|
|
|
// Last argument
|
|
if (!next)
|
|
{
|
|
// ',' was not found, find ')'
|
|
next = strchr(token, ')');
|
|
PARSE_CHECK(next);
|
|
last = EINA_TRUE;
|
|
*next++ = 0;
|
|
}
|
|
|
|
// Named arguments
|
|
if (_is_valid_keyval(token))
|
|
{
|
|
namedargs = EINA_TRUE;
|
|
|
|
optval = strchr(token, '=');
|
|
PARSE_CHECK(optval); // assert
|
|
*optval++ = 0;
|
|
|
|
optname = token;
|
|
EINA_INLIST_FOREACH(instr->params, param)
|
|
{
|
|
if (!strcasecmp(param->name, optname))
|
|
{
|
|
found = EINA_TRUE;
|
|
PARSE_CHECK(!param->set);
|
|
PARSE_CHECK(_value_parse(param, optval));
|
|
param->set = EINA_TRUE;
|
|
}
|
|
}
|
|
PARSE_CHECK(found);
|
|
}
|
|
// Sequential arguments
|
|
else if (!namedargs &&
|
|
(_is_valid_string(token) || _is_valid_number(token)))
|
|
{
|
|
int order = 0;
|
|
|
|
// Go to the nth argument
|
|
EINA_INLIST_FOREACH(instr->params, param)
|
|
{
|
|
if (order < seqorder)
|
|
order++;
|
|
else
|
|
{
|
|
found = EINA_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
PARSE_CHECK(found);
|
|
PARSE_CHECK(param->allow_seq);
|
|
PARSE_CHECK(_value_parse(param, token));
|
|
param->set = EINA_TRUE;
|
|
seqorder++;
|
|
}
|
|
else if (!last)
|
|
PARSE_CHECK(!"invalid argument list");
|
|
}
|
|
PARSE_CHECK(last);
|
|
success = EINA_TRUE;
|
|
|
|
end:
|
|
free(str);
|
|
instr->valid = success;
|
|
return success;
|
|
}
|
|
|
|
/* Buffers */
|
|
static Buffer *
|
|
_buffer_get(Evas_Filter_Program *pgm, const char *name)
|
|
{
|
|
Buffer *buf;
|
|
Eo *source;
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(pgm, NULL);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(name, NULL);
|
|
|
|
EINA_INLIST_FOREACH(pgm->buffers, buf)
|
|
if (!strcmp(buf->name, name))
|
|
return buf;
|
|
|
|
// Auto proxies
|
|
if (pgm->proxies)
|
|
{
|
|
source = eina_hash_find(pgm->proxies, name);
|
|
if (!source) return NULL;
|
|
|
|
buf = calloc(1, sizeof(Buffer));
|
|
if (!buf) return NULL;
|
|
|
|
buf->name = eina_stringshare_add(name);
|
|
buf->proxy = eina_stringshare_add(name);
|
|
buf->alpha = EINA_FALSE;
|
|
pgm->buffers = eina_inlist_append(pgm->buffers, EINA_INLIST_GET(buf));
|
|
return buf;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static Eina_Bool
|
|
_buffer_add(Evas_Filter_Program *pgm, const char *name, Eina_Bool alpha,
|
|
const char *src)
|
|
{
|
|
Buffer *buf;
|
|
|
|
if (_buffer_get(pgm, name))
|
|
{
|
|
ERR("Buffer '%s' already exists", name);
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
if (alpha && src)
|
|
{
|
|
ERR("Can not set proxy buffer as alpha!");
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
buf = calloc(1, sizeof(Buffer));
|
|
if (!buf) return EINA_FALSE;
|
|
|
|
buf->name = eina_stringshare_add(name);
|
|
buf->proxy = eina_stringshare_add(src);
|
|
buf->alpha = alpha;
|
|
pgm->buffers = eina_inlist_append(pgm->buffers, EINA_INLIST_GET(buf));
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
static void
|
|
_buffer_del(Buffer *buf)
|
|
{
|
|
if (!buf) return;
|
|
eina_stringshare_del(buf->name);
|
|
eina_stringshare_del(buf->proxy);
|
|
free(buf);
|
|
}
|
|
|
|
/* Instruction definitions */
|
|
|
|
static void
|
|
_blend_padding_update(Evas_Filter_Program *pgm, Evas_Filter_Instruction *instr,
|
|
int *padl, int *padr, int *padt, int *padb)
|
|
{
|
|
const char *outbuf;
|
|
Buffer *out;
|
|
int ox, oy, l = 0, r = 0, t = 0, b = 0;
|
|
|
|
ox = _instruction_param_geti(instr, "ox", NULL);
|
|
oy = _instruction_param_geti(instr, "oy", NULL);
|
|
|
|
outbuf = _instruction_param_gets(instr, "dst", NULL);
|
|
out = _buffer_get(pgm, outbuf);
|
|
EINA_SAFETY_ON_NULL_RETURN(out);
|
|
|
|
if (ox < 0) l = (-ox);
|
|
else r = ox;
|
|
|
|
if (oy < 0) t = (-oy);
|
|
else b = oy;
|
|
|
|
if (out->pad.l < l) out->pad.l = l;
|
|
if (out->pad.r < r) out->pad.r = r;
|
|
if (out->pad.t < t) out->pad.t = t;
|
|
if (out->pad.b < b) out->pad.b = b;
|
|
|
|
if (padl) *padl = l;
|
|
if (padr) *padr = r;
|
|
if (padt) *padt = t;
|
|
if (padb) *padb = b;
|
|
}
|
|
|
|
/**
|
|
@page evasfiltersref
|
|
@section sec_commands Filter commands
|
|
@page evasfiltersref
|
|
|
|
This section will present the various filter instructions, their syntax
|
|
and their effects.
|
|
*/
|
|
|
|
/**
|
|
@page evasfiltersref
|
|
|
|
@subsection sec_commands_blend Blend
|
|
|
|
Blend a buffer onto another. This is the simplest filter, as it just
|
|
renders one buffer on another, potentially using a color, an
|
|
offset and fill options.
|
|
|
|
@code
|
|
blend (src = input, dst = output, ox = 0, oy = 0, color = white, fillmode = none);
|
|
@endcode
|
|
|
|
@param src Source buffer to blend.
|
|
@param dst Destination buffer for blending.
|
|
@param ox X offset. Moves the buffer to the right (ox > 0) or to the left (ox < 0) by N pixels.
|
|
@param oy Y offset. Moves the buffer to the bottom (oy > 0) or to the top (oy < 0) by N pixels.
|
|
@param color A color to use as multiplier. See @ref evasfilters_color "colors". <br>
|
|
If the input is an alpha buffer and the output is RGBA, this will
|
|
draw the buffer in this color.
|
|
@param fillmode @c NONE, @c STRETCH, @c REPEAT <br>
|
|
Map the input onto the whole surface of the output by stretching or
|
|
repeating it.
|
|
|
|
If @a src is an alpha buffer and @a dst is an RGBA buffer, then the @a color option should be set.
|
|
|
|
@include filter_blend.txt
|
|
|
|
<center>
|
|
@image html filter_blend.png
|
|
</center>
|
|
|
|
@since 1.9
|
|
*/
|
|
|
|
static Eina_Bool
|
|
_blend_instruction_prepare(Evas_Filter_Instruction *instr)
|
|
{
|
|
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, "blend"), EINA_FALSE);
|
|
|
|
instr->type = EVAS_FILTER_MODE_BLEND;
|
|
instr->pad.update = _blend_padding_update;
|
|
_instruction_param_seq_add(instr, "src", VT_BUFFER, "input");
|
|
_instruction_param_seq_add(instr, "dst", VT_BUFFER, "output");
|
|
_instruction_param_seq_add(instr, "ox", VT_INT, 0);
|
|
_instruction_param_seq_add(instr, "oy", VT_INT, 0);
|
|
_instruction_param_name_add(instr, "color", VT_COLOR, 0xFFFFFFFF);
|
|
_instruction_param_name_add(instr, "fillmode", VT_STRING, "none");
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
static void
|
|
_blur_padding_update(Evas_Filter_Program *pgm, Evas_Filter_Instruction *instr,
|
|
int *padl, int *padr, int *padt, int *padb)
|
|
{
|
|
Eina_Bool yset;
|
|
int rx, ry, ox, oy, l, r, t, b;
|
|
const char *typestr, *inbuf, *outbuf;
|
|
Buffer *in, *out;
|
|
|
|
rx = _instruction_param_geti(instr, "rx", NULL);
|
|
ry = _instruction_param_geti(instr, "ry", &yset);
|
|
ox = _instruction_param_geti(instr, "ox", NULL);
|
|
oy = _instruction_param_geti(instr, "oy", NULL);
|
|
typestr = _instruction_param_gets(instr, "type", NULL);
|
|
inbuf = _instruction_param_gets(instr, "src", NULL);
|
|
outbuf = _instruction_param_gets(instr, "dst", NULL);
|
|
|
|
in = _buffer_get(pgm, inbuf);
|
|
out = _buffer_get(pgm, outbuf);
|
|
EINA_SAFETY_ON_NULL_RETURN(in);
|
|
EINA_SAFETY_ON_NULL_RETURN(out);
|
|
|
|
if (typestr && !strcasecmp(typestr, "motion"))
|
|
{
|
|
CRI("Motion blur not implemented yet!");
|
|
/*
|
|
instr->pad.l = (rx < 0) ? (-rx) : 0;
|
|
instr->pad.r = (rx > 0) ? (rx) : 0;
|
|
instr->pad.t = (ry < 0) ? (-ry) : 0;
|
|
instr->pad.b = (ry > 0) ? (ry) : 0;
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
if (!yset) ry = rx;
|
|
if (rx < 0) rx = 0;
|
|
if (ry < 0) ry = 0;
|
|
|
|
l = rx + in->pad.l - ox;
|
|
r = rx + in->pad.r + ox;
|
|
t = ry + in->pad.t - oy;
|
|
b = ry + in->pad.b + oy;
|
|
|
|
if (out->pad.l < l) out->pad.l = l;
|
|
if (out->pad.r < r) out->pad.r = r;
|
|
if (out->pad.t < t) out->pad.t = t;
|
|
if (out->pad.b < b) out->pad.b = b;
|
|
|
|
if (padl) *padl = rx - ox;
|
|
if (padr) *padr = rx + ox;
|
|
if (padt) *padt = ry - oy;
|
|
if (padb) *padb = ry + oy;
|
|
}
|
|
}
|
|
|
|
/**
|
|
@page evasfiltersref
|
|
|
|
@subsection sec_commands_blur Blur
|
|
|
|
Apply blur effect on a buffer (box or gaussian).
|
|
|
|
@code
|
|
blur (rx = 3, ry = -1, type = default, ox = 0, oy = 0, color = white, src = input, dst = output);
|
|
@endcode
|
|
|
|
@param rx X radius. Specifies the radius of the blurring kernel (X direction).
|
|
@param ry Y radius. Specifies the radius of the blurring kernel (Y direction). If -1 is used, then @a ry = @a rx.
|
|
@param type Blur type to apply. One of @c default, @c box or @c gaussian. @c default is an alias for @c gaussian.
|
|
@param ox X offset. Moves the buffer to the right (@a ox > 0) or to the left (@a ox < 0) by N pixels.
|
|
@param oy Y offset. Moves the buffer to the bottom (@a oy > 0) or to the top (@a oy < 0) by N pixels.
|
|
@param color A color to use as multiplier. See @ref evasfilters_color "colors". <br>
|
|
If the input is an alpha buffer and the output is RGBA, this will
|
|
draw the buffer in this color.
|
|
@param src Source buffer to blur.
|
|
@param dst Destination buffer for blending.
|
|
|
|
If @a src is an alpha buffer and @a dst is an RGBA buffer, then the color option should be set.
|
|
|
|
@a ox and @a oy can be used to move the blurry output by a few pixels, like a drop shadow. Example:
|
|
@include filter_blur.txt
|
|
|
|
<center>
|
|
@image html filter_blur.png
|
|
</center>
|
|
|
|
@since 1.9
|
|
*/
|
|
|
|
static Eina_Bool
|
|
_blur_instruction_prepare(Evas_Filter_Instruction *instr)
|
|
{
|
|
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, "blur"), EINA_FALSE);
|
|
|
|
instr->type = EVAS_FILTER_MODE_BLUR;
|
|
instr->pad.update = _blur_padding_update;
|
|
_instruction_param_seq_add(instr, "rx", VT_INT, 3);
|
|
_instruction_param_seq_add(instr, "ry", VT_INT, -1);
|
|
_instruction_param_seq_add(instr, "type", VT_STRING, "default");
|
|
_instruction_param_seq_add(instr, "ox", VT_INT, 0);
|
|
_instruction_param_seq_add(instr, "oy", VT_INT, 0);
|
|
_instruction_param_name_add(instr, "color", VT_COLOR, 0xFFFFFFFF);
|
|
_instruction_param_name_add(instr, "src", VT_BUFFER, "input");
|
|
_instruction_param_name_add(instr, "dst", VT_BUFFER, "output");
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
/**
|
|
@page evasfiltersref
|
|
|
|
@subsection sec_commands_bump Bump
|
|
|
|
Apply a light effect (ambient light, specular reflection and shadows) based on a bump map.
|
|
|
|
This can be used to give a relief effect on the object.
|
|
|
|
@code
|
|
bump (map, azimuth = 135.0, elevation = 45.0, depth = 8.0, specular = 0.0,
|
|
color = white, compensate = false, src = input, dst = output,
|
|
black = black, white = white, fillmode = repeat);
|
|
@endcode
|
|
|
|
@param map An alpha buffer treated like a Z map for the light effect (bump map). Must be specified.
|
|
@param azimuth The angle between the light vector and the X axis in the XY plane (Z = 0). 135.0 means 45 degrees from the top-left. Counter-clockwise notation.
|
|
@param elevation The angle between the light vector and the Z axis. 45.0 means 45 degrees to the screen's plane. Ranges from 0 to 90 only.
|
|
@param depth The depth of the object in an arbitrary unit. More depth means the shadows will be stronger. Default is 8.0.
|
|
@param specular An arbitrary unit for the specular light effect. Default is 0.0, but a common value would be 40.0.
|
|
@param color The main color of the object if src is an alpha buffer. This represents the light's normal color. See @ref evasfilters_color "colors".
|
|
@param compensate If set to true, compensate for whitening or darkening on flat surfaces. Default is false but it is recommended if specular light is wanted.
|
|
@param src Source buffer. This should be an alpha buffer.
|
|
@param dst Destination buffer. This should be an RGBA buffer (although alpha is supported). Must be of the same size as @a src.
|
|
@param black The shadows' color. Usually this will be black (@c #000).
|
|
@param white The specular light's color. Usually this will be white (@c #FFF).
|
|
@param fillmode This specifies how to handle @a map when its dimensions don't match those of @a src and @a dst. Default is to @c repeat.
|
|
|
|
@note As of 2014/02/11, the ALPHA to RGBA support is of much better quality than ALPHA only, but @b very slow. RGBA sources are not supported yet.
|
|
|
|
Here is a full example for a size 100 font, of a very simple bevel effect:
|
|
@include filter_bump.txt
|
|
|
|
<center>
|
|
@image html filter_bump.png
|
|
</center>
|
|
|
|
@since 1.9
|
|
*/
|
|
|
|
static Eina_Bool
|
|
_bump_instruction_prepare(Evas_Filter_Instruction *instr)
|
|
{
|
|
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, "bump"), EINA_FALSE);
|
|
|
|
instr->type = EVAS_FILTER_MODE_BUMP;
|
|
_instruction_param_seq_add(instr, "map", VT_BUFFER, NULL);
|
|
_instruction_param_seq_add(instr, "azimuth", VT_REAL, 135.0);
|
|
_instruction_param_seq_add(instr, "elevation", VT_REAL, 45.0);
|
|
_instruction_param_seq_add(instr, "depth", VT_REAL, 8.0);
|
|
_instruction_param_seq_add(instr, "specular", VT_REAL, 0.0);
|
|
_instruction_param_name_add(instr, "color", VT_COLOR, 0xFFFFFFFF);
|
|
_instruction_param_name_add(instr, "compensate", VT_BOOL, EINA_FALSE);
|
|
_instruction_param_name_add(instr, "src", VT_BUFFER, "input");
|
|
_instruction_param_name_add(instr, "dst", VT_BUFFER, "output");
|
|
_instruction_param_name_add(instr, "black", VT_COLOR, 0xFF000000);
|
|
_instruction_param_name_add(instr, "white", VT_COLOR, 0xFFFFFFFF);
|
|
_instruction_param_name_add(instr, "fillmode", VT_STRING, "repeat");
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
/**
|
|
@page evasfiltersref
|
|
|
|
@subsection sec_commands_curve Curve
|
|
|
|
Apply a color curve to a specific channel in a buffer.
|
|
|
|
@code
|
|
curve (points, interpolation = linear, channel = rgb, src = input, dst = output);
|
|
@endcode
|
|
|
|
Modify the colors of a buffer. This applies a color curve y = f(x) to every pixel.
|
|
|
|
@param points The color curve to apply. See below for the syntax.
|
|
@param interpolation How to interpolate between points. One of @c linear (y = ax + b) or @c none (y = Yk).
|
|
@param channel Target channel for the color modification. One of @c R(ed), @c G(reen), @c B(lue), @c A(lpha), @c RGB and @c RGBA. If @a src is an alpha buffer, this parameter will be ignored.
|
|
@param src Source buffer.
|
|
@param dst Destination buffer, must be of same dimensions and color space as @a src.
|
|
|
|
The @a points argument contains a list of (X,Y) points in the range 0..255,
|
|
describing a function <tt>f(x) = y</tt> to apply to all pixel values.
|
|
|
|
The syntax of this @a points string is <tt>x1:y1 - x2:y2 - x3:y3 - ... - xn:yn</tt>
|
|
(remember that all spaces are discarded).
|
|
The points @c xn are in @a increasing order: <tt>x1 < x2 < x3 < ... < xn</tt>,
|
|
and all values @c xn or @c yn are within the range 0..255.
|
|
|
|
The identity curve is then described as <tt>0:0-255:255</tt>, with linear interpolation:
|
|
@code
|
|
curve(points = 0:0 - 255:255, interpolation = linear);
|
|
@endcode
|
|
If ignored, y(x = 0) is 0 and y(x = 255) is 255.
|
|
|
|
The following example will generate a 4px thick stroke around text letters:
|
|
@include filter_curve.txt
|
|
|
|
<center>
|
|
@image html filter_curve.png
|
|
</center>
|
|
|
|
The curve command can be used to alter the output of a blur operation.
|
|
|
|
@since 1.9
|
|
*/
|
|
|
|
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);
|
|
|
|
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;
|
|
}
|
|
|
|
static void
|
|
_displace_padding_update(Evas_Filter_Program *pgm,
|
|
Evas_Filter_Instruction *instr,
|
|
int *padl, int *padr, int *padt, int *padb)
|
|
{
|
|
int intensity = 0;
|
|
int l, r, t, b;
|
|
const char *inbuf, *outbuf;
|
|
Buffer *in, *out;
|
|
|
|
intensity = _instruction_param_geti(instr, "intensity", NULL);
|
|
inbuf = _instruction_param_gets(instr, "src", NULL);
|
|
outbuf = _instruction_param_gets(instr, "dst", NULL);
|
|
|
|
in = _buffer_get(pgm, inbuf);
|
|
out = _buffer_get(pgm, outbuf);
|
|
EINA_SAFETY_ON_NULL_RETURN(in);
|
|
EINA_SAFETY_ON_NULL_RETURN(out);
|
|
|
|
l = intensity + in->pad.l;
|
|
r = intensity + in->pad.r;
|
|
t = intensity + in->pad.t;
|
|
b = intensity + in->pad.b;
|
|
|
|
if (out->pad.l < l) out->pad.l = l;
|
|
if (out->pad.r < r) out->pad.r = r;
|
|
if (out->pad.t < t) out->pad.t = t;
|
|
if (out->pad.b < b) out->pad.b = b;
|
|
|
|
if (padl) *padl = l;
|
|
if (padr) *padr = r;
|
|
if (padt) *padt = t;
|
|
if (padb) *padb = b;
|
|
}
|
|
|
|
/**
|
|
@page evasfiltersref
|
|
|
|
@subsection sec_commands_displace Displace
|
|
|
|
Apply a displacement map on a buffer.
|
|
|
|
@code
|
|
displace (map, intensity = 10, flags = 0, src = input, dst = output, fillmode = repeat);
|
|
@endcode
|
|
|
|
@param map An RGBA buffer containing a displacement map. See below for more details.
|
|
@param intensity Maximum distance for the displacement.
|
|
This means 0 and 255 will represent a displacement of @c intensity pixels.
|
|
@param flags One of @c default, @c nearest, @c smooth, @c nearest_stretch or @c smooth_stretch.
|
|
This defines how pixels should be treated when going out of the @a src image bounds.
|
|
@c default is equivalent to @c smooth_stretch.
|
|
@param src Source buffer
|
|
@param dst Destination buffer. Must be of same color format and size as @a src.
|
|
@param fillmode Defines how to handle cases where the map has a different size from @a src and @a dst. It should most likely be @c stretch or @c repeat.
|
|
|
|
<h3>Displacement map</h3>
|
|
|
|
The @a map buffer is an RGBA image containing displacement and alpha values.
|
|
Its size can be different from @c src or @c dst.
|
|
|
|
The @b red channel is used for X displacements while the @b green channel is
|
|
used for Y displacements. All subpixel values are in the range 0..255.
|
|
A value of 128 means 0 displacement, lower means displace to the top/left
|
|
and higher than 128 displace to the bottom/right.
|
|
|
|
If <tt>signed char</tt> is used instead of <tt>unsigned char</tt> to represent
|
|
these R and G values, then < 0 means displace top/left while > 0 means bottom/right.
|
|
|
|
The @c alpha channel is used as an alpha multiplier for blending.
|
|
|
|
Considering <tt>I(x, y)</tt> represents the pixel at position (x, y) in the
|
|
image I, then here is how the displacement is applied to @a dst:
|
|
@code
|
|
D = map (x, y)
|
|
dst (x, y) += D.alpha * src (D.red * intensity / 128, D.green * intensity / 128)
|
|
@endcode
|
|
Of course, the real algorithm takes into account interpolation between pixels as well.
|
|
|
|
@since 1.9
|
|
*/
|
|
|
|
static Eina_Bool
|
|
_displace_instruction_prepare(Evas_Filter_Instruction *instr)
|
|
{
|
|
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, "displace"), EINA_FALSE);
|
|
|
|
instr->type = EVAS_FILTER_MODE_DISPLACE;
|
|
instr->pad.update = _displace_padding_update;
|
|
_instruction_param_seq_add(instr, "map", VT_BUFFER, NULL);
|
|
_instruction_param_seq_add(instr, "intensity", VT_INT, 10);
|
|
_instruction_param_seq_add(instr, "flags", VT_STRING, "default");
|
|
_instruction_param_name_add(instr, "src", VT_BUFFER, "input");
|
|
_instruction_param_name_add(instr, "dst", VT_BUFFER, "output");
|
|
_instruction_param_name_add(instr, "fillmode", VT_STRING, "repeat");
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
/**
|
|
@page evasfiltersref
|
|
|
|
@subsection sec_commands_fill Fill
|
|
|
|
Fill a buffer with a specific color.
|
|
Not blending, can be used to clear a buffer.
|
|
|
|
@code
|
|
fill (dst = output, color = transparent, l = 0, r = 0, t = 0, b = 0);
|
|
@endcode
|
|
|
|
@param dst Target buffer to fill with @a color.
|
|
@param color The color used to fill the buffer. All pixels within the fill area will be reset to this value. See @ref evasfilters_color "colors".
|
|
@param l Left padding: skip @a l pixels from the left border of the buffer
|
|
@param r Right padding: skip @a r pixels from the right border of the buffer
|
|
@param t Top padding: skip @a t pixels from the top border of the buffer
|
|
@param b Bottom padding: skip @a b pixels from the bottom border of the buffer
|
|
|
|
This function should generally not be used, except for:
|
|
<ul>
|
|
<li>@a Testing an effect over a specific background color</li>
|
|
<li>Clearing out a buffer with either white or transparent color</li>
|
|
</ul>
|
|
|
|
@since 1.9
|
|
*/
|
|
|
|
static Eina_Bool
|
|
_fill_instruction_prepare(Evas_Filter_Instruction *instr)
|
|
{
|
|
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, "fill"), EINA_FALSE);
|
|
|
|
instr->type = EVAS_FILTER_MODE_FILL;
|
|
_instruction_param_seq_add(instr, "dst", VT_BUFFER, "output");
|
|
_instruction_param_seq_add(instr, "color", VT_COLOR, 0x0);
|
|
_instruction_param_seq_add(instr, "l", VT_INT, 0);
|
|
_instruction_param_seq_add(instr, "r", VT_INT, 0);
|
|
_instruction_param_seq_add(instr, "t", VT_INT, 0);
|
|
_instruction_param_seq_add(instr, "b", VT_INT, 0);
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
static void
|
|
_grow_padding_update(Evas_Filter_Program *pgm, Evas_Filter_Instruction *instr,
|
|
int *padl, int *padr, int *padt, int *padb)
|
|
{
|
|
const char *inbuf, *outbuf;
|
|
Buffer *in, *out;
|
|
int l, r, t, b;
|
|
int radius;
|
|
|
|
radius = _instruction_param_geti(instr, "radius", NULL);
|
|
inbuf = _instruction_param_gets(instr, "src", NULL);
|
|
outbuf = _instruction_param_gets(instr, "dst", NULL);
|
|
|
|
in = _buffer_get(pgm, inbuf);
|
|
out = _buffer_get(pgm, outbuf);
|
|
EINA_SAFETY_ON_NULL_RETURN(in);
|
|
EINA_SAFETY_ON_NULL_RETURN(out);
|
|
|
|
if (radius < 0) radius = 0;
|
|
|
|
l = radius + in->pad.l;
|
|
r = radius + in->pad.r;
|
|
t = radius + in->pad.t;
|
|
b = radius + in->pad.b;
|
|
|
|
if (padl) *padl = l;
|
|
if (padr) *padr = r;
|
|
if (padt) *padt = t;
|
|
if (padb) *padb = b;
|
|
|
|
if (out->pad.l < l) out->pad.l = l;
|
|
if (out->pad.r < r) out->pad.r = r;
|
|
if (out->pad.t < t) out->pad.t = t;
|
|
if (out->pad.b < b) out->pad.b = b;
|
|
}
|
|
|
|
/**
|
|
@page evasfiltersref
|
|
|
|
@subsection sec_commands_grow Grow
|
|
|
|
Grow or shrink a buffer's contents. This is not a zoom effect.
|
|
|
|
@code
|
|
grow (radius, smooth = true, src = input, dst = output);
|
|
@endcode
|
|
|
|
@param radius The radius of the grow kernel.
|
|
If a negative value is specified, the contents will shrink rather than grow.
|
|
@param smooth If @c true, use a smooth transitions between black and white (smooth blur and smoother curve).
|
|
@param src Source buffer to blur.
|
|
@param dst Destination buffer for blending. This must be of same size and colorspace as @a src.
|
|
|
|
Example:
|
|
@include filter_grow.txt
|
|
|
|
This will first grow the letters in the buffer @c input by 4px, and then draw
|
|
this buffer in black in the background. Blending white on top of that will
|
|
give a simple impression of stroked text.
|
|
|
|
<center>
|
|
@image html filter_grow.png
|
|
</center>
|
|
|
|
@since 1.9
|
|
*/
|
|
|
|
static Eina_Bool
|
|
_grow_instruction_prepare(Evas_Filter_Instruction *instr)
|
|
{
|
|
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, "grow"), EINA_FALSE);
|
|
|
|
instr->type = EVAS_FILTER_MODE_GROW;
|
|
instr->pad.update = _grow_padding_update;
|
|
_instruction_param_seq_add(instr, "radius", VT_INT, 0);
|
|
_instruction_param_name_add(instr, "smooth", VT_BOOL, EINA_TRUE);
|
|
_instruction_param_name_add(instr, "src", VT_BUFFER, "input");
|
|
_instruction_param_name_add(instr, "dst", VT_BUFFER, "output");
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
/**
|
|
@page evasfiltersref
|
|
|
|
@subsection sec_commands_mask Mask
|
|
|
|
Blend two buffers into a destination.
|
|
|
|
@code
|
|
mask (mask, src = input, dst = output, color = white, fillmode = none);
|
|
@endcode
|
|
|
|
@param mask A mask or texture to blend with the input @a src into the target @a dst.
|
|
@param src Source buffer. This can also be thought of a mask if @a src is alpha and @a mask is RGBA.
|
|
@param dst Destination buffer for blending. This must be of same size and colorspace as @a src.
|
|
@param color A color to use as multiplier for the blend operation. White means no change. See @ref evasfilters_color "colors".
|
|
@param fillmode Defines whether to stretch or repeat the @a mask if its size that of @src. Should be set when masking with external textures. Default is none.
|
|
|
|
Note that @a src and @a mask are interchangeable, if they have the same dimensions.
|
|
|
|
Example:
|
|
@include filter_mask.txt
|
|
|
|
This will create a simple cyan inner glow effect on black text.
|
|
|
|
<center>
|
|
@image html filter_mask.png
|
|
</center>
|
|
|
|
@since 1.9
|
|
*/
|
|
|
|
static Eina_Bool
|
|
_mask_instruction_prepare(Evas_Filter_Instruction *instr)
|
|
{
|
|
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, "mask"), EINA_FALSE);
|
|
|
|
instr->type = EVAS_FILTER_MODE_MASK;
|
|
_instruction_param_seq_add(instr, "mask", VT_BUFFER, NULL);
|
|
_instruction_param_seq_add(instr, "src", VT_BUFFER, "input");
|
|
_instruction_param_seq_add(instr, "dst", VT_BUFFER, "output");
|
|
_instruction_param_name_add(instr, "color", VT_COLOR, 0xFFFFFFFF);
|
|
_instruction_param_name_add(instr, "fillmode", VT_STRING, "none");
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
static void
|
|
_transform_padding_update(Evas_Filter_Program *pgm,
|
|
Evas_Filter_Instruction *instr,
|
|
int *padl, int *padr, int *padt, int *padb)
|
|
{
|
|
const char *outbuf;
|
|
Buffer *out;
|
|
int ox, oy, l = 0, r = 0, t = 0, b = 0;
|
|
|
|
//ox = _instruction_param_geti(instr, "ox", NULL);
|
|
ox = 0;
|
|
oy = _instruction_param_geti(instr, "oy", NULL);
|
|
|
|
outbuf = _instruction_param_gets(instr, "dst", NULL);
|
|
out = _buffer_get(pgm, outbuf);
|
|
EINA_SAFETY_ON_NULL_RETURN(out);
|
|
|
|
if (ox < 0) l = (-ox) * 2;
|
|
else r = ox * 2;
|
|
|
|
if (oy < 0) t = (-oy) * 2;
|
|
else b = oy * 2;
|
|
|
|
if (out->pad.l < l) out->pad.l = l;
|
|
if (out->pad.r < r) out->pad.r = r;
|
|
if (out->pad.t < t) out->pad.t = t;
|
|
if (out->pad.b < b) out->pad.b = b;
|
|
|
|
if (padl) *padl = l;
|
|
if (padr) *padr = r;
|
|
if (padt) *padt = t;
|
|
if (padb) *padb = b;
|
|
}
|
|
|
|
/**
|
|
@page evasfiltersref
|
|
|
|
@subsection sec_commands_transform Transform
|
|
|
|
Apply a geometrical transformation to a buffer.
|
|
|
|
Right now, only <b>vertical flip</b> is implemented and available.
|
|
|
|
@code
|
|
transform (dst, op = vflip, src = input, oy = 0);
|
|
@endcode
|
|
|
|
@param dst Destination buffer. Must be of the same colorspace as @a src. Must be specified.
|
|
@param op Must be @c vflip. There is no other operation yet.
|
|
@param src Source buffer to transform.
|
|
@param oy Y offset.
|
|
|
|
Example:
|
|
@include filter_transform.txt
|
|
|
|
This will create a mirrored text effect, for a font of 50px.
|
|
|
|
<center>
|
|
@image html filter_transform.png
|
|
</center>
|
|
|
|
@note Because of the meaning of @a oy, this effect probably needs to be
|
|
customized for a single font size (FIXME).
|
|
|
|
@since 1.9
|
|
*/
|
|
|
|
static Eina_Bool
|
|
_transform_instruction_prepare(Evas_Filter_Instruction *instr)
|
|
{
|
|
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, "transform"), EINA_FALSE);
|
|
|
|
instr->type = EVAS_FILTER_MODE_TRANSFORM;
|
|
instr->pad.update = _transform_padding_update;
|
|
_instruction_param_seq_add(instr, "dst", VT_BUFFER, NULL);
|
|
_instruction_param_seq_add(instr, "op", VT_STRING, "vflip");
|
|
_instruction_param_seq_add(instr, "src", VT_BUFFER, "input");
|
|
//_instruction_param_name_add(instr, "ox", VT_INT, 0);
|
|
_instruction_param_name_add(instr, "oy", VT_INT, 0);
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
static Evas_Filter_Instruction *
|
|
_instruction_create(const char *name)
|
|
{
|
|
Evas_Filter_Instruction *instr;
|
|
Eina_Bool (* prepare) (Evas_Filter_Instruction *) = NULL;
|
|
|
|
EINA_SAFETY_ON_FALSE_RETURN_VAL(name && *name, EINA_FALSE);
|
|
|
|
if (!strcasecmp(name, "blend"))
|
|
prepare = _blend_instruction_prepare;
|
|
else if (!strcasecmp(name, "blur"))
|
|
prepare = _blur_instruction_prepare;
|
|
else if (!strcasecmp(name, "bump"))
|
|
prepare = _bump_instruction_prepare;
|
|
else if (!strcasecmp(name, "curve"))
|
|
prepare = _curve_instruction_prepare;
|
|
else if (!strcasecmp(name, "displace"))
|
|
prepare = _displace_instruction_prepare;
|
|
else if (!strcasecmp(name, "fill"))
|
|
prepare = _fill_instruction_prepare;
|
|
else if (!strcasecmp(name, "grow"))
|
|
prepare = _grow_instruction_prepare;
|
|
else if (!strcasecmp(name, "mask"))
|
|
prepare = _mask_instruction_prepare;
|
|
else if (!strcasecmp(name, "transform"))
|
|
prepare = _transform_instruction_prepare;
|
|
|
|
if (!prepare)
|
|
{
|
|
ERR("Invalid instruction name '%s'", name);
|
|
return NULL;
|
|
}
|
|
|
|
instr = _instruction_new(name);
|
|
if (!instr) return NULL;
|
|
|
|
prepare(instr);
|
|
return instr;
|
|
}
|
|
|
|
/* Evas_Filter_Parser entry points */
|
|
|
|
#undef PARSE_CHECK
|
|
#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)
|
|
|
|
void
|
|
evas_filter_program_del(Evas_Filter_Program *pgm)
|
|
{
|
|
Evas_Filter_Instruction *instr;
|
|
Buffer *buf;
|
|
|
|
if (!pgm) return;
|
|
|
|
EINA_INLIST_FREE(pgm->buffers, buf)
|
|
{
|
|
pgm->buffers = eina_inlist_remove(pgm->buffers, EINA_INLIST_GET(buf));
|
|
_buffer_del(buf);
|
|
}
|
|
|
|
EINA_INLIST_FREE(pgm->instructions, instr)
|
|
{
|
|
pgm->instructions = eina_inlist_remove(pgm->instructions, EINA_INLIST_GET(instr));
|
|
_instruction_del(instr);
|
|
}
|
|
|
|
eina_stringshare_del(pgm->name);
|
|
free(pgm);
|
|
}
|
|
|
|
static Eina_Bool
|
|
_instruction_buffer_parse(Evas_Filter_Program *pgm, char *command)
|
|
{
|
|
Eina_Bool success = EINA_FALSE;
|
|
char *bufname = NULL, *src = NULL, *tok, *tok2;
|
|
Eina_Bool alpha = EINA_FALSE;
|
|
size_t sz;
|
|
|
|
tok = strchr(command, ':');
|
|
PARSE_CHECK(tok);
|
|
PARSE_CHECK(!strncasecmp("buffer:", command, tok - command));
|
|
|
|
tok++;
|
|
tok2 = strchr(tok, '(');
|
|
if (!tok2)
|
|
bufname = tok;
|
|
else
|
|
{
|
|
*tok2++ = 0;
|
|
bufname = tok;
|
|
tok = strchr(tok2, ')');
|
|
PARSE_CHECK(tok);
|
|
*tok = 0;
|
|
if (!*tok2)
|
|
alpha = EINA_FALSE;
|
|
else if (!strcasecmp(tok2, "rgba"))
|
|
alpha = EINA_FALSE;
|
|
else if (!strcasecmp(tok2, "alpha"))
|
|
alpha = EINA_TRUE;
|
|
else if (!strncasecmp("src=", tok2, 4))
|
|
{
|
|
src = tok2 + 4;
|
|
alpha = EINA_FALSE;
|
|
}
|
|
else
|
|
PARSE_CHECK(!"Invalid buffer type");
|
|
}
|
|
|
|
sz = strspn(bufname, allowed_chars);
|
|
PARSE_CHECK(sz == strlen(bufname));
|
|
PARSE_CHECK(_buffer_add(pgm, bufname, (alpha != 0), src));
|
|
success = EINA_TRUE;
|
|
|
|
end:
|
|
return success;
|
|
}
|
|
|
|
/** Parse a style program */
|
|
|
|
Eina_Bool
|
|
evas_filter_program_parse(Evas_Filter_Program *pgm, const char *str)
|
|
{
|
|
Evas_Filter_Instruction *instr = NULL;
|
|
Instruction_Param *param;
|
|
Eina_Bool success = EINA_FALSE, ok;
|
|
char *token, *next, *code, *instrname;
|
|
int count = 0;
|
|
size_t spn;
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(pgm, EINA_FALSE);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(str, EINA_FALSE);
|
|
EINA_SAFETY_ON_FALSE_RETURN_VAL(*str != 0, EINA_FALSE);
|
|
|
|
code = _whitespace_ignore_strdup(str);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(code, EINA_FALSE);
|
|
|
|
// NOTE: '' or "" strings will be broken by strsep if they contain ';'.
|
|
// But we don't support them anyway :)
|
|
|
|
next = code;
|
|
while ((token = strsep(&next, ";")) != NULL)
|
|
{
|
|
if (!next)
|
|
{
|
|
// Semicolon is mandatory.
|
|
DBG("End of processing");
|
|
PARSE_CHECK(!*token);
|
|
break;
|
|
}
|
|
|
|
// Empty command
|
|
if (next == token + 1) continue;
|
|
|
|
// Parse "instrname(options)" or "buffer:a(options)"
|
|
spn = strcspn(token, "(:");
|
|
PARSE_CHECK(spn);
|
|
if (token[spn] == ':')
|
|
PARSE_CHECK(_instruction_buffer_parse(pgm, token));
|
|
else if (token[spn] == '(')
|
|
{
|
|
instrname = token;
|
|
instrname[spn] = 0;
|
|
|
|
instr = _instruction_create(instrname);
|
|
PARSE_CHECK(instr);
|
|
|
|
instrname[spn] = '(';
|
|
ok = _instruction_parse(instr, token);
|
|
PARSE_CHECK(ok);
|
|
|
|
// Check buffers validity
|
|
EINA_INLIST_FOREACH(instr->params, param)
|
|
{
|
|
const char *bufname = NULL;
|
|
|
|
if (param->type != VT_BUFFER) continue;
|
|
PARSE_CHECK(eina_value_get(param->value, &bufname));
|
|
if (!_buffer_get(pgm, bufname))
|
|
{
|
|
ERR("Buffer '%s' does not exist!", bufname);
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
// Add to the queue
|
|
pgm->instructions = eina_inlist_append(pgm->instructions, EINA_INLIST_GET(instr));
|
|
instr = NULL;
|
|
count++;
|
|
}
|
|
else PARSE_CHECK(!"invalid command");
|
|
}
|
|
success = EINA_TRUE;
|
|
|
|
DBG("Program successfully compiled with %d instruction(s)", count);
|
|
|
|
end:
|
|
if (!success)
|
|
{
|
|
ERR("Failed to parse program");
|
|
_instruction_del(instr);
|
|
}
|
|
free(code);
|
|
|
|
pgm->valid = success;
|
|
return success;
|
|
}
|
|
|
|
/** Evaluate required padding to correctly apply an effect */
|
|
|
|
Eina_Bool
|
|
evas_filter_program_padding_get(Evas_Filter_Program *pgm,
|
|
int *l, int *r, int *t, int *b)
|
|
{
|
|
Evas_Filter_Instruction *instr;
|
|
int pl = 0, pr = 0, pt = 0, pb = 0;
|
|
int maxl = 0, maxr = 0, maxt = 0, maxb = 0;
|
|
Buffer *buf;
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(pgm, EINA_FALSE);
|
|
|
|
// Reset all paddings
|
|
EINA_INLIST_FOREACH(pgm->buffers, buf)
|
|
buf->pad.l = buf->pad.r = buf->pad.t = buf->pad.b = 0;
|
|
|
|
// Accumulate paddings
|
|
EINA_INLIST_FOREACH(pgm->instructions, instr)
|
|
if (instr->pad.update)
|
|
{
|
|
instr->pad.update(pgm, instr, &pl, &pr, &pt, &pb);
|
|
if (pl > maxl) maxl = pl;
|
|
if (pr > maxr) maxr = pr;
|
|
if (pt > maxt) maxt = pt;
|
|
if (pb > maxb) maxb = pb;
|
|
}
|
|
|
|
if (l) *l = maxl;
|
|
if (r) *r = maxr;
|
|
if (t) *t = maxt;
|
|
if (b) *b = maxb;
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
/** Create an empty filter program for style parsing */
|
|
|
|
Evas_Filter_Program *
|
|
evas_filter_program_new(const char *name)
|
|
{
|
|
Evas_Filter_Program *pgm;
|
|
|
|
pgm = calloc(1, sizeof(Evas_Filter_Program));
|
|
if (!pgm) return NULL;
|
|
pgm->name = eina_stringshare_add(name);
|
|
_buffer_add(pgm, "input", EINA_TRUE, NULL);
|
|
_buffer_add(pgm, "output", EINA_FALSE, NULL);
|
|
|
|
return pgm;
|
|
}
|
|
|
|
/** Bind objects for proxy rendering */
|
|
void
|
|
evas_filter_program_source_set_all(Evas_Filter_Program *pgm,
|
|
Eina_Hash *proxies)
|
|
{
|
|
if (!pgm) return;
|
|
pgm->proxies = proxies;
|
|
}
|
|
|
|
/** Glue with Evas' filters */
|
|
|
|
#define CA(color) ((color >> 24) & 0xFF)
|
|
#define CR(color) ((color >> 16) & 0xFF)
|
|
#define CG(color) ((color >> 8) & 0xFF)
|
|
#define CB(color) ((color) & 0xFF)
|
|
|
|
#define SETCOLOR(c) do { ENFN->context_color_get(ENDT, dc, &R, &G, &B, &A); \
|
|
ENFN->context_color_set(ENDT, dc, CR(c), CG(c), CB(c), CA(c)); } while (0)
|
|
#define RESETCOLOR() do { ENFN->context_color_set(ENDT, dc, R, G, B, A); } while (0)
|
|
|
|
#define SETCLIP(l, r, t, b) int _l = 0, _r = 0, _t = 0, _b = 0; \
|
|
do { ENFN->context_clip_get(ENDT, dc, &_l, &_r, &_t, &_b); \
|
|
ENFN->context_clip_set(ENDT, dc, l, r, t, b); } while (0)
|
|
#define RESETCLIP() do { ENFN->context_clip_set(ENDT, dc, _l, _r, _t, _b); } while (0)
|
|
|
|
static Evas_Filter_Fill_Mode
|
|
_fill_mode_get(Evas_Filter_Instruction *instr)
|
|
{
|
|
const char *fill;
|
|
unsigned k;
|
|
|
|
if (!instr) return EVAS_FILTER_FILL_MODE_NONE;
|
|
fill = _instruction_param_gets(instr, "fillmode", NULL);
|
|
|
|
for (k = 0; k < sizeof(fill_modes) / sizeof(fill_modes[0]); k++)
|
|
{
|
|
if (!strcasecmp(fill_modes[k].name, fill))
|
|
return fill_modes[k].value;
|
|
}
|
|
|
|
return EVAS_FILTER_FILL_MODE_NONE;
|
|
}
|
|
|
|
static int
|
|
_instr2cmd_blend(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm,
|
|
Evas_Filter_Instruction *instr, void *dc)
|
|
{
|
|
Eina_Bool isset = EINA_FALSE;
|
|
const char *src, *dst;
|
|
DATA32 color;
|
|
Buffer *in, *out;
|
|
Evas_Filter_Fill_Mode fillmode;
|
|
int cmdid, ox, oy, A, R, G, B;
|
|
|
|
src = _instruction_param_gets(instr, "src", NULL);
|
|
dst = _instruction_param_gets(instr, "dst", NULL);
|
|
ox = _instruction_param_geti(instr, "ox", NULL);
|
|
oy = _instruction_param_geti(instr, "oy", NULL);
|
|
color = _instruction_param_getc(instr, "color", &isset);
|
|
fillmode = _fill_mode_get(instr);
|
|
in = _buffer_get(pgm, src);
|
|
out = _buffer_get(pgm, dst);
|
|
|
|
if (isset) SETCOLOR(color);
|
|
cmdid = evas_filter_command_blend_add(ctx, dc, in->cid, out->cid, ox, oy,
|
|
fillmode);
|
|
if (isset) RESETCOLOR();
|
|
if (cmdid < 0) return cmdid;
|
|
|
|
return cmdid;
|
|
}
|
|
|
|
static int
|
|
_instr2cmd_blur(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm,
|
|
Evas_Filter_Instruction *instr, void *dc)
|
|
{
|
|
Eina_Bool isset = EINA_FALSE, yset = EINA_FALSE;
|
|
Evas_Filter_Blur_Type type = EVAS_FILTER_BLUR_DEFAULT;
|
|
const char *src, *dst, *typestr;
|
|
DATA32 color;
|
|
Buffer *in, *out;
|
|
int cmdid, ox, oy, rx, ry, A, R, G, B;
|
|
|
|
src = _instruction_param_gets(instr, "src", NULL);
|
|
dst = _instruction_param_gets(instr, "dst", NULL);
|
|
ox = _instruction_param_geti(instr, "ox", NULL);
|
|
oy = _instruction_param_geti(instr, "oy", NULL);
|
|
rx = _instruction_param_geti(instr, "rx", NULL);
|
|
ry = _instruction_param_geti(instr, "ry", &yset);
|
|
color = _instruction_param_getc(instr, "color", &isset);
|
|
typestr = _instruction_param_gets(instr, "type", NULL);
|
|
in = _buffer_get(pgm, src);
|
|
out = _buffer_get(pgm, dst);
|
|
|
|
if (typestr)
|
|
{
|
|
if (!strcasecmp(typestr, "gaussian"))
|
|
type = EVAS_FILTER_BLUR_GAUSSIAN;
|
|
else if (!strcasecmp(typestr, "box"))
|
|
type = EVAS_FILTER_BLUR_BOX;
|
|
else if (!strcasecmp(typestr, "default"))
|
|
type = EVAS_FILTER_BLUR_DEFAULT;
|
|
else
|
|
ERR("Unknown blur type '%s'. Using default blur.", typestr);
|
|
}
|
|
|
|
if (!yset) ry = rx;
|
|
if (isset) SETCOLOR(color);
|
|
cmdid = evas_filter_command_blur_add(ctx, dc, in->cid, out->cid, type,
|
|
rx, ry, ox, oy);
|
|
if (isset) RESETCOLOR();
|
|
|
|
return cmdid;
|
|
}
|
|
|
|
static int
|
|
_instr2cmd_bump(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm,
|
|
Evas_Filter_Instruction *instr, void *dc)
|
|
{
|
|
Evas_Filter_Bump_Flags flags = EVAS_FILTER_BUMP_NORMAL;
|
|
Evas_Filter_Fill_Mode fillmode;
|
|
const char *src, *dst, *map;
|
|
DATA32 color, black, white;
|
|
Buffer *in, *out, *bump;
|
|
double azimuth, elevation, depth, specular;
|
|
int cmdid, compensate;
|
|
|
|
src = _instruction_param_gets(instr, "src", NULL);
|
|
dst = _instruction_param_gets(instr, "dst", NULL);
|
|
map = _instruction_param_gets(instr, "map", NULL);
|
|
color = _instruction_param_getc(instr, "color", NULL);
|
|
white = _instruction_param_getc(instr, "white", NULL);
|
|
black = _instruction_param_getc(instr, "black", NULL);
|
|
azimuth = _instruction_param_getd(instr, "azimuth", NULL);
|
|
elevation = _instruction_param_getd(instr, "elevation", NULL);
|
|
depth = _instruction_param_getd(instr, "depth", NULL);
|
|
specular = _instruction_param_getd(instr, "specular", NULL);
|
|
compensate = _instruction_param_geti(instr, "compensate", NULL);
|
|
fillmode = _fill_mode_get(instr);
|
|
if (compensate) flags |= EVAS_FILTER_BUMP_COMPENSATE;
|
|
|
|
in = _buffer_get(pgm, src);
|
|
out = _buffer_get(pgm, dst);
|
|
bump = _buffer_get(pgm, map);
|
|
|
|
cmdid = evas_filter_command_bump_map_add(ctx, dc, in->cid, bump->cid, out->cid,
|
|
azimuth, elevation, depth, specular,
|
|
black, color, white, flags,
|
|
fillmode);
|
|
|
|
return cmdid;
|
|
}
|
|
|
|
static int
|
|
_instr2cmd_displace(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm,
|
|
Evas_Filter_Instruction *instr, void *dc)
|
|
{
|
|
Evas_Filter_Fill_Mode fillmode;
|
|
Evas_Filter_Displacement_Flags flags =
|
|
EVAS_FILTER_DISPLACE_STRETCH | EVAS_FILTER_DISPLACE_LINEAR;
|
|
const char *src, *dst, *map, *flagsstr;
|
|
Buffer *in, *out, *mask;
|
|
int cmdid, intensity;
|
|
Eina_Bool isset = EINA_FALSE;
|
|
|
|
src = _instruction_param_gets(instr, "src", NULL);
|
|
dst = _instruction_param_gets(instr, "dst", NULL);
|
|
map = _instruction_param_gets(instr, "map", NULL);
|
|
intensity = _instruction_param_geti(instr, "intensity", NULL);
|
|
flagsstr = _instruction_param_gets(instr, "flags", &isset);
|
|
fillmode = _fill_mode_get(instr);
|
|
|
|
if (!flagsstr) flagsstr = "default";
|
|
if (!strcasecmp(flagsstr, "nearest"))
|
|
flags = EVAS_FILTER_DISPLACE_NEAREST;
|
|
else if (!strcasecmp(flagsstr, "smooth"))
|
|
flags = EVAS_FILTER_DISPLACE_LINEAR;
|
|
else if (!strcasecmp(flagsstr, "nearest_stretch"))
|
|
flags = EVAS_FILTER_DISPLACE_NEAREST | EVAS_FILTER_DISPLACE_STRETCH;
|
|
else if (!strcasecmp(flagsstr, "default") || !strcasecmp(flagsstr, "smooth_stretch"))
|
|
flags = EVAS_FILTER_DISPLACE_STRETCH | EVAS_FILTER_DISPLACE_LINEAR;
|
|
else if (isset)
|
|
WRN("Invalid flags '%s' in displace operation. Using default instead", flagsstr);
|
|
|
|
in = _buffer_get(pgm, src);
|
|
out = _buffer_get(pgm, dst);
|
|
mask = _buffer_get(pgm, map);
|
|
|
|
cmdid = evas_filter_command_displacement_map_add(ctx, dc, in->cid, out->cid,
|
|
mask->cid, flags, intensity,
|
|
fillmode);
|
|
|
|
return cmdid;
|
|
}
|
|
|
|
static int
|
|
_instr2cmd_fill(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm,
|
|
Evas_Filter_Instruction *instr, void *dc)
|
|
{
|
|
const char *bufname;
|
|
Buffer *buf;
|
|
int R, G, B, A, l, r, t, b;
|
|
Evas_Filter_Command *cmd;
|
|
DATA32 color;
|
|
int cmdid;
|
|
|
|
bufname = _instruction_param_gets(instr, "dst", NULL);
|
|
color = _instruction_param_getc(instr, "color", NULL);
|
|
l = _instruction_param_geti(instr, "l", NULL);
|
|
r = _instruction_param_geti(instr, "r", NULL);
|
|
t = _instruction_param_geti(instr, "t", NULL);
|
|
b = _instruction_param_geti(instr, "b", NULL);
|
|
|
|
buf = _buffer_get(pgm, bufname);
|
|
|
|
SETCOLOR(color);
|
|
cmdid = evas_filter_command_fill_add(ctx, dc, buf->cid);
|
|
RESETCOLOR();
|
|
|
|
cmd = EINA_INLIST_CONTAINER_GET(eina_inlist_last(ctx->commands), Evas_Filter_Command);
|
|
cmd->draw.clip.l = l;
|
|
cmd->draw.clip.r = r;
|
|
cmd->draw.clip.t = t;
|
|
cmd->draw.clip.b = b;
|
|
cmd->draw.clip_mode_lrtb = EINA_TRUE;
|
|
|
|
return cmdid;
|
|
}
|
|
|
|
static int
|
|
_instr2cmd_grow(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm,
|
|
Evas_Filter_Instruction *instr, void *dc)
|
|
{
|
|
const char *src, *dst;
|
|
Buffer *in, *out;
|
|
Eina_Bool smooth;
|
|
int cmdid, radius;
|
|
|
|
src = _instruction_param_gets(instr, "src", NULL);
|
|
dst = _instruction_param_gets(instr, "dst", NULL);
|
|
radius = _instruction_param_geti(instr, "radius", NULL);
|
|
smooth = _instruction_param_geti(instr, "smooth", NULL);
|
|
|
|
in = _buffer_get(pgm, src);
|
|
out = _buffer_get(pgm, dst);
|
|
|
|
cmdid = evas_filter_command_grow_add(ctx, dc, in->cid, out->cid,
|
|
radius, smooth);
|
|
|
|
return cmdid;
|
|
}
|
|
|
|
static int
|
|
_instr2cmd_mask(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm,
|
|
Evas_Filter_Instruction *instr, void *dc)
|
|
{
|
|
Evas_Filter_Fill_Mode fillmode;
|
|
const char *src, *dst, *msk;
|
|
Buffer *in, *out, *mask;
|
|
DATA32 color;
|
|
int R, G, B, A, cmdid;
|
|
|
|
src = _instruction_param_gets(instr, "src", NULL);
|
|
dst = _instruction_param_gets(instr, "dst", NULL);
|
|
msk = _instruction_param_gets(instr, "mask", NULL);
|
|
color = _instruction_param_getc(instr, "color", NULL);
|
|
fillmode = _fill_mode_get(instr);
|
|
|
|
in = _buffer_get(pgm, src);
|
|
out = _buffer_get(pgm, dst);
|
|
mask = _buffer_get(pgm, msk);
|
|
|
|
SETCOLOR(color);
|
|
cmdid = evas_filter_command_mask_add(ctx, dc, in->cid, mask->cid, out->cid, fillmode);
|
|
RESETCOLOR();
|
|
if (cmdid < 0) return cmdid;
|
|
|
|
if (!in->alpha && !mask->alpha && !out->alpha)
|
|
{
|
|
Evas_Filter_Command *cmd;
|
|
|
|
cmd = _evas_filter_command_get(ctx, cmdid);
|
|
cmd->draw.need_temp_buffer = EINA_TRUE;
|
|
}
|
|
|
|
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 = NULL, *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 && !strcasecmp(interpolation, "none"))
|
|
mode = EVAS_FILTER_INTERPOLATION_MODE_NONE;
|
|
|
|
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;
|
|
}
|
|
|
|
static int
|
|
_instr2cmd_transform(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm,
|
|
Evas_Filter_Instruction *instr, void *dc)
|
|
{
|
|
Evas_Filter_Transform_Flags flags;
|
|
const char *src, *dst, *op;
|
|
Buffer *in, *out;
|
|
int ox = 0, oy;
|
|
|
|
op = _instruction_param_gets(instr, "op", NULL);
|
|
src = _instruction_param_gets(instr, "src", NULL);
|
|
dst = _instruction_param_gets(instr, "dst", NULL);
|
|
// ox = _instruction_param_geti(instr, "ox", NULL);
|
|
oy = _instruction_param_geti(instr, "oy", NULL);
|
|
|
|
if (!strcasecmp(op, "vflip"))
|
|
flags = EVAS_FILTER_TRANSFORM_VFLIP;
|
|
else
|
|
{
|
|
ERR("Invalid transform '%s'", op);
|
|
return -1;
|
|
}
|
|
|
|
in = _buffer_get(pgm, src);
|
|
out = _buffer_get(pgm, dst);
|
|
return evas_filter_command_transform_add(ctx, dc, in->cid, out->cid, flags, ox, oy);
|
|
}
|
|
|
|
static int
|
|
_command_from_instruction(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm,
|
|
Evas_Filter_Instruction *instr, void *dc)
|
|
{
|
|
int (* instr2cmd) (Evas_Filter_Context *, Evas_Filter_Program *,
|
|
Evas_Filter_Instruction *, void *);
|
|
|
|
switch (instr->type)
|
|
{
|
|
case EVAS_FILTER_MODE_BLEND:
|
|
instr2cmd = _instr2cmd_blend;
|
|
break;
|
|
case EVAS_FILTER_MODE_BLUR:
|
|
instr2cmd = _instr2cmd_blur;
|
|
break;
|
|
case EVAS_FILTER_MODE_BUMP:
|
|
instr2cmd = _instr2cmd_bump;
|
|
break;
|
|
case EVAS_FILTER_MODE_DISPLACE:
|
|
instr2cmd = _instr2cmd_displace;
|
|
break;
|
|
case EVAS_FILTER_MODE_FILL:
|
|
instr2cmd = _instr2cmd_fill;
|
|
break;
|
|
case EVAS_FILTER_MODE_GROW:
|
|
instr2cmd = _instr2cmd_grow;
|
|
break;
|
|
case EVAS_FILTER_MODE_MASK:
|
|
instr2cmd = _instr2cmd_mask;
|
|
break;
|
|
case EVAS_FILTER_MODE_CURVE:
|
|
instr2cmd = _instr2cmd_curve;
|
|
break;
|
|
case EVAS_FILTER_MODE_TRANSFORM:
|
|
instr2cmd = _instr2cmd_transform;
|
|
break;
|
|
default:
|
|
CRI("Invalid instruction type: %d", instr->type);
|
|
return -1;
|
|
}
|
|
|
|
return instr2cmd(ctx, pgm, instr, dc);
|
|
}
|
|
|
|
Eina_Bool
|
|
evas_filter_context_program_use(Evas_Filter_Context *ctx,
|
|
Evas_Filter_Program *pgm)
|
|
{
|
|
Buffer *buf;
|
|
Evas_Filter_Instruction *instr;
|
|
Eina_Bool success = EINA_FALSE;
|
|
void *dc = NULL;
|
|
int cmdid;
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, EINA_FALSE);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(pgm, EINA_FALSE);
|
|
EINA_SAFETY_ON_FALSE_RETURN_VAL(pgm->valid, EINA_FALSE);
|
|
|
|
DBG("Using program '%s' for context %p", pgm->name, ctx);
|
|
|
|
// Create empty context with all required buffers
|
|
evas_filter_context_clear(ctx);
|
|
EINA_INLIST_FOREACH(pgm->buffers, buf)
|
|
{
|
|
buf->cid = evas_filter_buffer_empty_new(ctx, buf->alpha);
|
|
if (buf->proxy)
|
|
{
|
|
Evas_Filter_Proxy_Binding *pb;
|
|
Evas_Filter_Buffer *fb;
|
|
|
|
pb = eina_hash_find(pgm->proxies, buf->proxy);
|
|
if (!pb) continue;
|
|
|
|
fb = _filter_buffer_get(ctx, buf->cid);
|
|
fb->proxy = pb->eo_proxy;
|
|
fb->source = pb->eo_source;
|
|
fb->source_name = eina_stringshare_ref(pb->name);
|
|
}
|
|
}
|
|
|
|
// Compute and save padding info
|
|
evas_filter_program_padding_get(pgm, &ctx->padl, &ctx->padr, &ctx->padt, &ctx->padb);
|
|
|
|
dc = ENFN->context_new(ENDT);
|
|
ENFN->context_color_set(ENDT, dc, 255, 255, 255, 255);
|
|
|
|
// Apply all commands
|
|
EINA_INLIST_FOREACH(pgm->instructions, instr)
|
|
{
|
|
cmdid = _command_from_instruction(ctx, pgm, instr, dc);
|
|
if (cmdid <= 0)
|
|
goto end;
|
|
}
|
|
|
|
success = EINA_TRUE;
|
|
|
|
end:
|
|
if (!success) evas_filter_context_clear(ctx);
|
|
if (dc) ENFN->context_free(ENDT, dc);
|
|
return success;
|
|
}
|