evas filter: Implement blur filter in pure GL

Wait a second though, this implementation is not only incomplete
(no support for box vs. gaussian blur), it's also insanely bad in
terms of performance. Small radii may work fine, but at least blurs
render properly in GL with this patch (no more glReadPixels!).

The shader needs a lot of love, including in particular:
- support for 1D box blur single pass
- support for 1D gaussian (or sine) blur
- use linear interpolation and N-tap filters
- separation of 2D blur in two passes (high-level logic)
- potentially separation of large 1D blurs in 2 or more passes
  knowing that 2sigma == sigma + sigma when it comes to the gaussian
  bell curve.
This commit is contained in:
Jean-Philippe Andre 2017-01-25 18:06:29 +09:00
parent 125c7d956e
commit 5bce7120f1
12 changed files with 382 additions and 30 deletions

View File

@ -844,6 +844,7 @@ modules/evas/engines/gl_generic/evas_ector_gl_buffer.c \
modules/evas/engines/gl_generic/evas_ector_gl_image_buffer.c \
modules/evas/engines/gl_generic/filters/gl_engine_filter.h \
modules/evas/engines/gl_generic/filters/gl_filter_blend.c \
modules/evas/engines/gl_generic/filters/gl_filter_blur.c \
modules/evas/engines/gl_generic/filters/gl_filter_curve.c \
modules/evas/engines/gl_generic/filters/gl_filter_displace.c \
modules/evas/engines/gl_generic/filters/gl_filter_fill.c \

View File

@ -47,6 +47,8 @@ static const Filter_Image images_anim[] = {
/* builtin filter examples */
static const Filter templates[] = {
{ "Custom", NULL, NULL },
{ "BLUR",
"blur { 15, color = 'darkblue' }", NULL },
{ "Simple blend",
"blend { color = 'darkblue' }", NULL },
{ "Black shadow",

View File

@ -548,11 +548,6 @@ evas_filter_command_blur_add(Evas_Filter_Context *ctx, void *drawctx,
Eina_Bool override;
DATA32 color;
// Note (SW engine):
// The basic blur operation overrides the pixels in the target buffer,
// only supports one direction (X or Y) and no offset. As a consequence
// most cases require intermediate work buffers.
EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(drawctx, NULL);
@ -570,8 +565,6 @@ evas_filter_command_blur_add(Evas_Filter_Context *ctx, void *drawctx,
out = _filter_buffer_get(ctx, outbuf);
EINA_SAFETY_ON_FALSE_GOTO(out, fail);
if (in == out) out->dirty = EINA_FALSE;
ENFN->context_color_get(ENDT, drawctx, &R, &G, &B, &A);
color = ARGB_JOIN(A, R, G, B);
if (!color)
@ -580,6 +573,29 @@ evas_filter_command_blur_add(Evas_Filter_Context *ctx, void *drawctx,
return _command_new(ctx, EVAS_FILTER_MODE_SKIP, NULL, NULL, NULL);
}
if (ctx->gl)
{
// GL engine: single pass!
XDBG("Add GL blur %d -> %d (%dx%d px)", in->id, out->id, dx, dy);
cmd = _command_new(ctx, EVAS_FILTER_MODE_BLUR, in, NULL, out);
if (!cmd) goto fail;
cmd->blur.type = type;
cmd->blur.dx = dx;
cmd->blur.dy = dy;
cmd->blur.count = count;
cmd->draw.ox = ox;
cmd->draw.oy = oy;
DRAW_COLOR_SET(R, G, B, A);
return cmd;
}
// Note (SW engine):
// The basic blur operation overrides the pixels in the target buffer,
// only supports one direction (X or Y) and no offset. As a consequence
// most cases require intermediate work buffers.
if (in == out) out->dirty = EINA_FALSE;
render_op = ENFN->context_render_op_get(ENDT, drawctx);
override = (render_op == EVAS_RENDER_COPY);

View File

@ -108,7 +108,7 @@ struct _Evas_GL_Program
GLuint mvp, rotation_id;
} uniform;
struct {
GLuint loc_filter_data[3];
GLint loc_filter_data[3];
Eina_Bool known_locations;
} attribute;
GLuint prog;
@ -245,6 +245,7 @@ enum _Shader_Type {
SHD_MAP,
SHD_FILTER_DISPLACE,
SHD_FILTER_CURVE,
SHD_FILTER_BLUR,
SHD_TYPE_LAST
};
@ -650,6 +651,7 @@ void evas_gl_common_filter_displace_push(Evas_Engine_GL_Context *gc
int x, int y, int w, int h, double dx, double dy, Eina_Bool nearest);
void evas_gl_common_filter_curve_push(Evas_Engine_GL_Context *gc, Evas_GL_Texture *tex,
int x, int y, int w, int h, const uint8_t *points, int channel);
void evas_gl_common_filter_blur_push(Evas_Engine_GL_Context *gc, Evas_GL_Texture *tex, int x, int y, int w, int h, double dx, double dy);
int evas_gl_common_shader_program_init(Evas_GL_Shared *shared);
void evas_gl_common_shader_program_shutdown(Evas_GL_Shared *shared);

View File

@ -1205,6 +1205,9 @@ error:
#define PIPE_FREE(x) \
do { _pipebuf_free(x); (x) = NULL; } while (0)
#define FREE(x) \
do { free(x); (x) = NULL; } while (0)
typedef struct _Pipebuf
{
int skipped, alloc;
@ -1380,7 +1383,7 @@ evas_gl_common_context_free(Evas_Engine_GL_Context *gc)
PIPE_FREE(gc->pipe[i].array.texsam);
PIPE_FREE(gc->pipe[i].array.mask);
PIPE_FREE(gc->pipe[i].array.masksam);
PIPE_FREE(gc->pipe[i].array.filter_data);
FREE(gc->pipe[i].array.filter_data);
}
}
@ -3135,6 +3138,25 @@ _filter_data_flush(Evas_Engine_GL_Context *gc, Evas_GL_Program *prog)
}
}
static inline void
_filter_data_prepare(Evas_Engine_GL_Context *gc, int pn,
Evas_GL_Program *prog, int count)
{
gc->pipe[pn].array.filter_data_count = count;
gc->pipe[pn].array.filter_data = malloc(count * 2 * sizeof(GLfloat));
if (!prog->attribute.known_locations)
{
for (int k = 0; k < count; k++)
{
char name[32];
sprintf(name, "filter_data_%d", k);
prog->attribute.loc_filter_data[k] = glGetAttribLocation(prog->prog, name);
}
}
}
void
evas_gl_common_filter_displace_push(Evas_Engine_GL_Context *gc,
Evas_GL_Texture *tex, Evas_GL_Texture *map_tex,
@ -3199,19 +3221,7 @@ evas_gl_common_filter_displace_push(Evas_Engine_GL_Context *gc,
// displace properties
gc->pipe[pn].shader.filter.map_tex = map_tex->pt->texture;
gc->pipe[pn].shader.filter.map_nearest = nearest;
gc->pipe[pn].array.filter_data_count = 3;
gc->pipe[pn].array.filter_data = malloc(6 * sizeof(GLfloat));
if (!prog->attribute.known_locations)
{
for (int k = 0; k < gc->pipe[pn].array.filter_data_count; k++)
{
char name[32];
sprintf(name, "filter_data_%d", k);
prog->attribute.loc_filter_data[k] = glGetAttribLocation(prog->prog, name);
}
}
_filter_data_prepare(gc, pn, prog, 3);
sx = x;
sy = y;
@ -3415,6 +3425,111 @@ evas_gl_common_filter_curve_push(Evas_Engine_GL_Context *gc,
if (!nomul)
PUSH_6_COLORS(pn, r, g, b, a);
}
void
evas_gl_common_filter_blur_push(Evas_Engine_GL_Context *gc,
Evas_GL_Texture *tex,
int x, int y, int w, int h,
double dx, double dy)
{
double sx, sy, sw, sh, pw, ph;
double ox1, oy1, ox2, oy2, ox3, oy3, ox4, oy4;
GLfloat tx1, ty1, tx2, ty2, tx3, ty3, tx4, ty4;
GLfloat offsetx, offsety;
int r, g, b, a, nomul = 0, pn;
Evas_GL_Program *prog;
GLfloat *filter_data;
Eina_Bool blend = EINA_TRUE;
Eina_Bool smooth = EINA_TRUE;
r = R_VAL(&gc->dc->mul.col);
g = G_VAL(&gc->dc->mul.col);
b = B_VAL(&gc->dc->mul.col);
a = A_VAL(&gc->dc->mul.col);
if (gc->dc->render_op == EVAS_RENDER_COPY)
blend = EINA_FALSE;
prog = evas_gl_common_shader_program_get(gc, SHD_FILTER_BLUR, NULL, 0, r, g, b, a,
w, h, w, h, smooth, tex, EINA_FALSE,
NULL, EINA_FALSE, EINA_FALSE, 0, 0,
NULL, &nomul, NULL);
_filter_data_flush(gc, prog);
EINA_SAFETY_ON_NULL_RETURN(prog);
pn = _evas_gl_common_context_push(SHD_FILTER_BLUR, gc, tex, NULL, prog,
x, y, w, h, blend, smooth,
0, 0, 0, 0, 0, EINA_FALSE);
gc->pipe[pn].region.type = SHD_FILTER_BLUR;
gc->pipe[pn].shader.prog = prog;
gc->pipe[pn].shader.cur_tex = tex->pt->texture;
gc->pipe[pn].shader.cur_texm = 0;
gc->pipe[pn].shader.tex_target = GL_TEXTURE_2D;
gc->pipe[pn].shader.smooth = smooth;
gc->pipe[pn].shader.mask_smooth = 0;
gc->pipe[pn].shader.blend = blend;
gc->pipe[pn].shader.render_op = gc->dc->render_op;
gc->pipe[pn].shader.clip = 0;
gc->pipe[pn].shader.cx = 0;
gc->pipe[pn].shader.cy = 0;
gc->pipe[pn].shader.cw = 0;
gc->pipe[pn].shader.ch = 0;
gc->pipe[pn].array.line = 0;
gc->pipe[pn].array.use_vertex = 1;
gc->pipe[pn].array.use_color = !nomul;
gc->pipe[pn].array.use_texuv = 1;
gc->pipe[pn].array.use_texuv2 = 0;
gc->pipe[pn].array.use_texuv3 = 0;
gc->pipe[pn].array.use_texsam = 0;
gc->pipe[pn].array.use_mask = 0;
gc->pipe[pn].array.use_masksam = 0;
pipe_region_expand(gc, pn, x, y, w, h);
PIPE_GROW(gc, pn, 6);
// Set blur properties... TODO
_filter_data_prepare(gc, pn, prog, 2);
filter_data = gc->pipe[pn].array.filter_data;
filter_data[0] = dx;
filter_data[1] = dy;
filter_data[2] = w;
filter_data[3] = h;
sx = 0;
sy = 0;
sw = w;
sh = h;
pw = tex->pt->w;
ph = tex->pt->h;
ox1 = sx;
oy1 = sy;
ox2 = sx + sw;
oy2 = sy;
ox3 = sx + sw;
oy3 = sy + sh;
ox4 = sx;
oy4 = sy + sh;
offsetx = tex->x;
offsety = tex->y;
tx1 = ((double)(offsetx) + ox1) / pw;
ty1 = ((double)(offsety) + oy1) / ph;
tx2 = ((double)(offsetx) + ox2) / pw;
ty2 = ((double)(offsety) + oy2) / ph;
tx3 = ((double)(offsetx) + ox3) / pw;
ty3 = ((double)(offsety) + oy3) / ph;
tx4 = ((double)(offsetx) + ox4) / pw;
ty4 = ((double)(offsety) + oy4) / ph;
PUSH_6_VERTICES(pn, x, y, w, h);
PUSH_6_QUAD(pn, tx1, ty1, tx2, ty2, tx3, ty3, tx4, ty4);
if (!nomul)
PUSH_6_COLORS(pn, r, g, b, a);
shader_array_flush(gc);
}
@ -4180,7 +4295,7 @@ shader_array_flush(Evas_Engine_GL_Context *gc)
PIPE_FREE(gc->pipe[i].array.texsam);
PIPE_FREE(gc->pipe[i].array.mask);
PIPE_FREE(gc->pipe[i].array.masksam);
PIPE_FREE(gc->pipe[i].array.filter_data);
FREE(gc->pipe[i].array.filter_data);
gc->pipe[i].array.num = 0;
gc->pipe[i].array.alloc = 0;

View File

@ -42,8 +42,9 @@ typedef enum {
SHADER_FLAG_RGB_A_PAIR = (1 << 20),
SHADER_FLAG_FILTER_DISPLACE = (1 << 21),
SHADER_FLAG_FILTER_CURVE = (1 << 22),
SHADER_FLAG_FILTER_BLUR = (1 << 23),
} Shader_Flag;
#define SHADER_FLAG_COUNT 23
#define SHADER_FLAG_COUNT 24
static const char *_shader_flags[SHADER_FLAG_COUNT] = {
"TEX",
@ -68,7 +69,8 @@ static const char *_shader_flags[SHADER_FLAG_COUNT] = {
"ALPHA",
"RGB_A_PAIR",
"FILTER_DISPLACE",
"FILTER_CURVE"
"FILTER_CURVE",
"FILTER_BLUR"
};
static Eina_Bool compiler_released = EINA_FALSE;
@ -785,6 +787,9 @@ evas_gl_common_shader_flags_get(Evas_GL_Shared *shared, Shader_Type type,
case SHD_FILTER_CURVE:
flags |= SHADER_FLAG_FILTER_CURVE;
break;
case SHD_FILTER_BLUR:
flags |= SHADER_FLAG_FILTER_BLUR;
break;
default:
CRI("Impossible shader type.");
return 0;

View File

@ -20,6 +20,8 @@ static const char fragment_glsl[] =
"#else\n"
"# define SAMPLER_EXTERNAL_OES sampler2D\n"
"#endif\n"
"#define M_PI 3.141592653589793238462643383279502884\n"
"#define M_PI_2 1.570796326794896619231321691639751442\n"
"#ifndef SHD_NOMUL\n"
"varying vec4 col;\n"
"#endif\n"
@ -89,11 +91,22 @@ static const char fragment_glsl[] =
"#ifdef SHD_FILTER_CURVE\n"
"uniform sampler2D tex_filter;\n"
"#endif\n"
"#ifdef SHD_FILTER_BLUR\n"
"varying vec2 blur_radius;\n"
"varying vec2 blur_divider;\n"
"#endif\n"
"// ----------------------------------------------------------------------------\n"
"#ifndef SHD_FILTER_BLUR\n"
"void main()\n"
"{\n"
"#if defined(SHD_EXTERNAL) || defined(SHD_TEX)\n"
" vec2 coord = tex_c;\n"
"#endif\n"
"#else // SHD_FILTER_BLUR\n"
"vec4 fetch_pixel(float ox, float oy)\n"
"{\n"
" vec2 coord = tex_c + vec2(ox, oy);\n"
"#endif // SHD_FILTER_BLUR\n"
" vec4 c;\n"
"#ifdef SHD_FILTER_DISPLACE\n"
" vec2 dxy = texture2D(tex_filter, tex_c).rg * displace_vector;\n"
@ -195,8 +208,8 @@ static const char fragment_glsl[] =
" texture2D(tex_filter, vec2(c.g / old_alpha, 0.0)).g * new_alpha,\n"
" texture2D(tex_filter, vec2(c.b / old_alpha, 0.0)).b * new_alpha,\n"
" new_alpha);\n"
" //c = vec4(new_alpha, new_alpha, new_alpha, new_alpha);\n"
"#endif\n"
"#ifndef SHD_FILTER_BLUR\n"
" gl_FragColor =\n"
" c\n"
"#ifndef SHD_NOMUL\n"
@ -209,7 +222,50 @@ static const char fragment_glsl[] =
" * fa\n"
"#endif\n"
" ;\n"
"}\n";
"}\n"
"#else // SHD_FILTER_BLUR\n"
" return c;\n"
"}\n"
"void main()\n"
"{\n"
" float x, y, div_x, div_y;\n"
" float rx = blur_radius.x;\n"
" float ry = blur_radius.y;\n"
" vec4 acc = vec4(0.,0.,0.,0.);\n"
" vec4 c;\n"
" div_x = blur_divider.x;\n"
" div_y = blur_divider.y;\n"
" float diam_x = rx * 2.0 + 1.0;\n"
" float diam_y = ry * 2.0 + 1.0;\n"
" float div = 0.0;\n"
"#if 1\n"
" // This is completely insane... but renders properly :)\n"
" for (y = -ry; y <= ry; y += 8.0)\n"
" {\n"
" float wy = (y + ry) / (diam_y - 1.0) * 6.0 - 3.0;\n"
" wy = (sin(wy + M_PI_2) + 1.0) / 2.0;\n"
" for (x = -rx; x <= rx; x += 8.0)\n"
" {\n"
" float wx = (x + rx) / (diam_x - 1.0) * 6.0 - 3.0;\n"
" wx = (sin(wx + M_PI_2) + 1.0) / 2.0;\n"
" vec4 px = fetch_pixel(x / div_x, y / div_y);\n"
" acc += px * wx * wy;\n"
" div += wx * wy;\n"
" }\n"
" }\n"
"#else\n"
" vec4 px = fetch_pixel(0.0, 0.0);\n"
" div = 1.0;\n"
" acc += px;\n"
"#endif\n"
" c = acc / div;\n"
"#ifndef SHD_NOMUL\n"
" gl_FragColor = c * col;\n"
"#else\n"
" gl_FragColor = c;\n"
"#endif\n"
"}\n"
"#endif // SHD_FILTER_BLUR\n";
static const char vertex_glsl[] =
"/* General-purpose vertex shader for all operations in Evas.\n"
@ -282,6 +338,13 @@ static const char vertex_glsl[] =
"varying vec2 displace_min;\n"
"varying vec2 displace_max;\n"
"#endif\n"
"/* Gfx Filters: blur */\n"
"#ifdef SHD_FILTER_BLUR\n"
"attribute vec2 filter_data_0;\n"
"attribute vec2 filter_data_1;\n"
"varying vec2 blur_radius;\n"
"varying vec2 blur_divider;\n"
"#endif\n"
"void main()\n"
"{\n"
" gl_Position = mvp * vertex;\n"
@ -350,5 +413,9 @@ static const char vertex_glsl[] =
" displace_min = filter_data_1;\n"
" displace_max = filter_data_2;\n"
"#endif\n"
"#ifdef SHD_FILTER_BLUR\n"
" blur_radius = filter_data_0;\n"
" blur_divider = filter_data_1;\n"
"#endif\n"
"}\n";

View File

@ -6,6 +6,9 @@
FRAGMENT_SHADER
#define M_PI 3.141592653589793238462643383279502884
#define M_PI_2 1.570796326794896619231321691639751442
#ifndef SHD_NOMUL
varying vec4 col;
#endif
@ -85,12 +88,28 @@ varying vec2 displace_max;
uniform sampler2D tex_filter;
#endif
#ifdef SHD_FILTER_BLUR
varying vec2 blur_radius;
varying vec2 blur_divider;
#endif
// ----------------------------------------------------------------------------
#ifndef SHD_FILTER_BLUR
void main()
{
#if defined(SHD_EXTERNAL) || defined(SHD_TEX)
vec2 coord = tex_c;
#endif
#else // SHD_FILTER_BLUR
vec4 fetch_pixel(float ox, float oy)
{
vec2 coord = tex_c + vec2(ox, oy);
#endif // SHD_FILTER_BLUR
vec4 c;
#ifdef SHD_FILTER_DISPLACE
@ -205,6 +224,8 @@ void main()
new_alpha);
#endif
#ifndef SHD_FILTER_BLUR
gl_FragColor =
c
#ifndef SHD_NOMUL
@ -219,3 +240,51 @@ void main()
;
}
#else // SHD_FILTER_BLUR
return c;
}
void main()
{
float x, y, div_x, div_y;
float rx = blur_radius.x;
float ry = blur_radius.y;
vec4 acc = vec4(0.,0.,0.,0.);
vec4 c;
div_x = blur_divider.x;
div_y = blur_divider.y;
float diam_x = rx * 2.0 + 1.0;
float diam_y = ry * 2.0 + 1.0;
float div = 0.0;
// This is completely insane... but renders properly :)
for (y = -ry; y <= ry; y += 1.0)
{
float wy = (y + ry) / (diam_y - 1.0) * 6.0 - 3.0;
wy = (sin(wy + M_PI_2) + 1.0) / 2.0;
for (x = -rx; x <= rx; x += 1.0)
{
float wx = (x + rx) / (diam_x - 1.0) * 6.0 - 3.0;
wx = (sin(wx + M_PI_2) + 1.0) / 2.0;
vec4 px = fetch_pixel(x / div_x, y / div_y);
acc += px * wx * wy;
div += wx * wy;
}
}
c = acc / div;
#ifndef SHD_NOMUL
gl_FragColor = c * col;
#else
gl_FragColor = c;
#endif
}
#endif // SHD_FILTER_BLUR

View File

@ -78,6 +78,14 @@ varying vec2 displace_min;
varying vec2 displace_max;
#endif
/* Gfx Filters: blur */
#ifdef SHD_FILTER_BLUR
attribute vec2 filter_data_0;
attribute vec2 filter_data_1;
varying vec2 blur_radius;
varying vec2 blur_divider;
#endif
void main()
{
@ -157,5 +165,9 @@ void main()
displace_min = filter_data_1;
displace_max = filter_data_2;
#endif
}
#ifdef SHD_FILTER_BLUR
blur_radius = filter_data_0;
blur_divider = filter_data_1;
#endif
}

View File

@ -3029,7 +3029,7 @@ _gfx_filter_func_get(Evas_Filter_Command *cmd)
switch (cmd->mode)
{
case EVAS_FILTER_MODE_BLEND: funcptr = gl_filter_blend_func_get(cmd); break;
//case EVAS_FILTER_MODE_BLUR: funcptr = gl_filter_blur_func_get(cmd); break;
case EVAS_FILTER_MODE_BLUR: funcptr = gl_filter_blur_func_get(cmd); break;
//case EVAS_FILTER_MODE_BUMP: funcptr = gl_filter_bump_func_get(cmd); break;
case EVAS_FILTER_MODE_CURVE: funcptr = gl_filter_curve_func_get(cmd); break;
case EVAS_FILTER_MODE_DISPLACE: funcptr = gl_filter_displace_func_get(cmd); break;

View File

@ -10,7 +10,7 @@ extern int _evas_engine_GL_log_dom;
typedef Eina_Bool (* GL_Filter_Apply_Func) (Render_Engine_GL_Generic *re, Evas_Filter_Command *cmd);
GL_Filter_Apply_Func gl_filter_blend_func_get(Evas_Filter_Command *cmd);
//GL_Filter_Apply_Func gl_filter_blur_func_get(Evas_Filter_Command *cmd);
GL_Filter_Apply_Func gl_filter_blur_func_get(Evas_Filter_Command *cmd);
//GL_Filter_Apply_Func gl_filter_bump_func_get(Evas_Filter_Command *cmd);
GL_Filter_Apply_Func gl_filter_curve_func_get(Evas_Filter_Command *cmd);
GL_Filter_Apply_Func gl_filter_displace_func_get(Evas_Filter_Command *cmd);

View File

@ -0,0 +1,63 @@
#include "gl_engine_filter.h"
static Eina_Bool
_gl_filter_blur(Render_Engine_GL_Generic *re, Evas_Filter_Command *cmd)
{
Evas_Engine_GL_Context *gc;
Evas_GL_Image *image, *surface;
RGBA_Draw_Context *dc_save;
int x, y, w, h;
DEBUG_TIME_BEGIN();
x = cmd->draw.ox;
y = cmd->draw.oy;
w = cmd->input->w;
h = cmd->input->h;
re->window_use(re->software.ob);
gc = re->window_gl_context_get(re->software.ob);
image = evas_ector_buffer_drawable_image_get(cmd->input->buffer);
EINA_SAFETY_ON_NULL_RETURN_VAL(image, EINA_FALSE);
surface = evas_ector_buffer_render_image_get(cmd->output->buffer);
EINA_SAFETY_ON_NULL_RETURN_VAL(surface, EINA_FALSE);
evas_gl_common_context_target_surface_set(gc, surface);
dc_save = gc->dc;
gc->dc = evas_common_draw_context_new();
evas_common_draw_context_set_multiplier(gc->dc, cmd->draw.R, cmd->draw.G, cmd->draw.B, cmd->draw.A);
if (cmd->input == cmd->output)
gc->dc->render_op = EVAS_RENDER_COPY;
else
gc->dc->render_op = _gfx_to_evas_render_op(cmd->draw.rop);
DBG("blur %d @%p -> %d @%p", cmd->input->id, cmd->input->buffer,
cmd->output->id, cmd->output->buffer);
evas_gl_common_filter_blur_push(gc, image->tex, x, y, w, h,
cmd->blur.dx, cmd->blur.dy);
evas_common_draw_context_free(gc->dc);
gc->dc = dc_save;
evas_ector_buffer_engine_image_release(cmd->input->buffer, image);
evas_ector_buffer_engine_image_release(cmd->output->buffer, surface);
DEBUG_TIME_END();
return EINA_TRUE;
}
GL_Filter_Apply_Func
gl_filter_blur_func_get(Evas_Filter_Command *cmd)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input, NULL);
return _gl_filter_blur;
}