forked from enlightenment/efl
evas filters: Adjust downscale coordinates to avoid artifacts
This avoids sampling artifacts when moving or resizing a snapshot object over a region with sharp content (eg. text).
This commit is contained in:
parent
5467d1eb3e
commit
01a4ecd92c
|
@ -401,7 +401,7 @@ evas_filter_object_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
|
|||
|
||||
if (filter)
|
||||
{
|
||||
ok = evas_filter_context_program_reuse(filter, pd->data->chain);
|
||||
ok = evas_filter_context_program_reuse(filter, pd->data->chain, X, Y);
|
||||
if (!ok)
|
||||
{
|
||||
fcow = FCOW_BEGIN(pd);
|
||||
|
@ -417,7 +417,7 @@ evas_filter_object_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
|
|||
filter = evas_filter_context_new(obj->layer->evas, do_async, 0);
|
||||
|
||||
// Run script
|
||||
ok = evas_filter_context_program_use(filter, pd->data->chain, EINA_FALSE);
|
||||
ok = evas_filter_context_program_use(filter, pd->data->chain, EINA_FALSE, X, Y);
|
||||
if (!filter || !ok)
|
||||
{
|
||||
ERR("Parsing failed?");
|
||||
|
|
|
@ -13476,7 +13476,7 @@ evas_object_textblock_render(Evas_Object *eo_obj EINA_UNUSED,
|
|||
ctx = evas_filter_context_new(obj->layer->evas, do_async, ti->gfx_filter);
|
||||
evas_filter_state_prepare(eo_obj, &state, ti);
|
||||
evas_filter_program_state_set(pgm, &state);
|
||||
ok = evas_filter_context_program_use(ctx, pgm, EINA_FALSE);
|
||||
ok = evas_filter_context_program_use(ctx, pgm, EINA_FALSE, 0, 0);
|
||||
if (!ok)
|
||||
{
|
||||
evas_filter_context_destroy(ctx);
|
||||
|
|
|
@ -165,7 +165,7 @@ evas_filter_context_proxy_render_all(Evas_Filter_Context *ctx, Eo *eo_obj,
|
|||
}
|
||||
|
||||
Eina_Bool
|
||||
evas_filter_context_program_reuse(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm)
|
||||
evas_filter_context_program_reuse(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm, int x, int y)
|
||||
{
|
||||
Evas_Filter_Buffer *fb;
|
||||
Eina_List *li;
|
||||
|
@ -196,7 +196,7 @@ evas_filter_context_program_reuse(Evas_Filter_Context *ctx, Evas_Filter_Program
|
|||
fb->dirty = EINA_FALSE;
|
||||
}
|
||||
|
||||
return evas_filter_context_program_use(ctx, pgm, EINA_TRUE);
|
||||
return evas_filter_context_program_use(ctx, pgm, EINA_TRUE, x, y);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -691,19 +691,24 @@ evas_filter_command_blur_add_gl(Evas_Filter_Context *ctx,
|
|||
int rx, int ry, int ox, int oy, int count,
|
||||
int R, int G, int B, int A)
|
||||
{
|
||||
Evas_Filter_Command *cmd;
|
||||
Evas_Filter_Command *cmd = NULL;
|
||||
Evas_Filter_Buffer *dx_in, *dx_out, *dy_in, *dy_out, *tmp = NULL;
|
||||
int down_x = 1, down_y = 1;
|
||||
int pad_x = 0, pad_y = 0;
|
||||
double dx, dy;
|
||||
|
||||
/* GL blur implementation:
|
||||
* - Create intermediate buffer T (variable size)
|
||||
* - Downscale to buffer T
|
||||
* - Apply X blur kernel
|
||||
* - Apply Y blur kernel while scaling up
|
||||
*
|
||||
* - TODO: Fix distortion X vs. Y
|
||||
* - TODO: Calculate best scaline and blur radius
|
||||
* - TODO: Add post-processing? (2D single-pass)
|
||||
* - Create intermediate buffer T1, T2
|
||||
* - Downscale input to buffer T1
|
||||
* - Apply X blur kernel from T1 to T2
|
||||
* - Apply Y blur kernel from T2 back to output
|
||||
*
|
||||
* In order to avoid sampling artifacts when moving or resizing a filtered
|
||||
* snapshot, we make sure that we always sample and scale based on the same
|
||||
* original pixels positions:
|
||||
* - Input pixels must be aligned to down_x,down_y boundaries
|
||||
* - T1/T2 buffer size is up to 1px larger than [input / scale_x,y]
|
||||
*/
|
||||
|
||||
dx = rx;
|
||||
|
@ -714,34 +719,43 @@ evas_filter_command_blur_add_gl(Evas_Filter_Context *ctx,
|
|||
#if 1
|
||||
if (type == EVAS_FILTER_BLUR_DEFAULT)
|
||||
{
|
||||
int down_x = 1, down_y = 1;
|
||||
|
||||
/* For now, disable scaling - testing perfect gaussian blur until it's
|
||||
* ready: */
|
||||
// Apply downscaling for large enough radii only.
|
||||
down_x = 1 << evas_filter_smallest_pow2_larger_than(dx / 2) / 2;
|
||||
down_y = 1 << evas_filter_smallest_pow2_larger_than(dy / 2) / 2;
|
||||
|
||||
// Downscaling to max 4 times for perfect picture quality (with
|
||||
// the standard scaling fragment shader and SHD_SAM22).
|
||||
if (down_x > 4) down_x = 4;
|
||||
if (down_y > 4) down_y = 4;
|
||||
|
||||
if (down_x > 1 && down_y > 1)
|
||||
{
|
||||
tmp = evas_filter_temporary_buffer_get(ctx,
|
||||
ceil(ctx->w / down_x),
|
||||
ceil(ctx->h / down_y),
|
||||
in->alpha_only, EINA_TRUE);
|
||||
int ww, hh;
|
||||
|
||||
pad_x = ctx->x % down_x;
|
||||
pad_y = ctx->y % down_y;
|
||||
|
||||
ww = ceil((double) ctx->w / down_x) + 1;
|
||||
hh = ceil((double) ctx->h / down_y) + 1;
|
||||
|
||||
tmp = evas_filter_temporary_buffer_get(ctx, ww, hh, in->alpha_only, EINA_TRUE);
|
||||
if (!tmp) goto fail;
|
||||
|
||||
dx = rx / down_x;
|
||||
dy = ry / down_y;
|
||||
dx /= (double) down_x;
|
||||
dy /= (double) down_y;
|
||||
|
||||
XDBG("Add GL downscale %d (%dx%d) -> %d (%dx%d)", in->id, in->w, in->h, tmp->id, tmp->w, tmp->h);
|
||||
cmd = _command_new(ctx, EVAS_FILTER_MODE_BLEND, in, NULL, tmp);
|
||||
if (!cmd) goto fail;
|
||||
cmd->draw.fillmode = EVAS_FILTER_FILL_MODE_STRETCH_XY;
|
||||
cmd->draw.scale.down = EINA_TRUE;
|
||||
cmd->draw.scale.pad_x = pad_x;
|
||||
cmd->draw.scale.pad_y = pad_y;
|
||||
cmd->draw.scale.factor_x = down_x;
|
||||
cmd->draw.scale.factor_y = down_y;
|
||||
dx_in = tmp;
|
||||
|
||||
tmp = evas_filter_temporary_buffer_get(ctx, ctx->w / down_x, ctx->h / down_y,
|
||||
in->alpha_only, EINA_TRUE);
|
||||
tmp = evas_filter_temporary_buffer_get(ctx, ww, hh, in->alpha_only, EINA_TRUE);
|
||||
if (!tmp) goto fail;
|
||||
dy_out = tmp;
|
||||
}
|
||||
|
@ -780,6 +794,7 @@ evas_filter_command_blur_add_gl(Evas_Filter_Context *ctx,
|
|||
cmd->blur.count = count;
|
||||
}
|
||||
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, NULL);
|
||||
if (cmd->output != out)
|
||||
{
|
||||
XDBG("Add GL upscale %d (%dx%d) -> %d (%dx%d)",
|
||||
|
@ -787,6 +802,11 @@ evas_filter_command_blur_add_gl(Evas_Filter_Context *ctx,
|
|||
cmd = _command_new(ctx, EVAS_FILTER_MODE_BLEND, cmd->output, NULL, out);
|
||||
if (!cmd) goto fail;
|
||||
cmd->draw.fillmode = EVAS_FILTER_FILL_MODE_STRETCH_XY;
|
||||
cmd->draw.scale.down = EINA_FALSE;
|
||||
cmd->draw.scale.pad_x = pad_x;
|
||||
cmd->draw.scale.pad_y = pad_y;
|
||||
cmd->draw.scale.factor_x = down_x;
|
||||
cmd->draw.scale.factor_y = down_y;
|
||||
}
|
||||
|
||||
cmd->draw.ox = ox;
|
||||
|
|
|
@ -3474,7 +3474,7 @@ _instruction_dump(Evas_Filter_Instruction *instr)
|
|||
Eina_Bool
|
||||
evas_filter_context_program_use(Evas_Filter_Context *ctx,
|
||||
Evas_Filter_Program *pgm,
|
||||
Eina_Bool reuse)
|
||||
Eina_Bool reuse, int object_x, int object_y)
|
||||
{
|
||||
Evas_Filter_Instruction *instr;
|
||||
Eina_Bool success = EINA_FALSE;
|
||||
|
@ -3489,6 +3489,8 @@ evas_filter_context_program_use(Evas_Filter_Context *ctx,
|
|||
// Copy current state (size, edje state val, color class, etc...)
|
||||
ctx->w = pgm->state.w;
|
||||
ctx->h = pgm->state.h;
|
||||
ctx->x = object_x;
|
||||
ctx->y = object_y;
|
||||
|
||||
// Create empty context with all required buffers
|
||||
evas_filter_context_clear(ctx, reuse);
|
||||
|
|
|
@ -125,6 +125,7 @@ struct _Evas_Filter_Context
|
|||
evas_filter_buffer_scaled_get_func buffer_scaled_get;
|
||||
|
||||
// Variables changing at each run
|
||||
int x, y; // Position of the object (for GL downscaling of snapshots)
|
||||
int w, h; // Dimensions of the input/output buffers
|
||||
|
||||
struct {
|
||||
|
@ -233,6 +234,11 @@ struct _Evas_Filter_Command
|
|||
int l, r, t, b;
|
||||
};
|
||||
} clip;
|
||||
struct {
|
||||
int factor_x, factor_y;
|
||||
int pad_x, pad_y;
|
||||
Eina_Bool down;
|
||||
} scale;
|
||||
Evas_Filter_Fill_Mode fillmode;
|
||||
Eina_Bool clip_use : 1;
|
||||
Eina_Bool clip_mode_lrtb : 1;
|
||||
|
|
|
@ -147,8 +147,8 @@ void *evas_filter_context_data_get(Evas_Filter_Context *ctx);
|
|||
Eina_Bool evas_filter_context_async_get(Evas_Filter_Context *ctx);
|
||||
void evas_filter_context_size_get(Evas_Filter_Context *ctx, int *w, int *H);
|
||||
void evas_filter_context_destroy(Evas_Filter_Context *ctx);
|
||||
Eina_Bool evas_filter_context_program_use(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm, Eina_Bool reuse);
|
||||
Eina_Bool evas_filter_context_program_reuse(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm);
|
||||
Eina_Bool evas_filter_context_program_use(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm, Eina_Bool reuse, int object_x, int object_y);
|
||||
Eina_Bool evas_filter_context_program_reuse(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm, int x, int y);
|
||||
void evas_filter_context_proxy_render_all(Evas_Filter_Context *ctx, Eo *eo_obj, Eina_Bool do_async);
|
||||
void evas_filter_context_post_run_callback_set(Evas_Filter_Context *ctx, Evas_Filter_Cb cb, void *data);
|
||||
#define evas_filter_context_autodestroy(ctx) evas_filter_context_post_run_callback_set(ctx, ((Evas_Filter_Cb) evas_filter_context_destroy), ctx)
|
||||
|
|
|
@ -12,8 +12,6 @@ _mapped_blend(Evas_Engine_GL_Context *gc,
|
|||
int row, col, rows, cols;
|
||||
Eina_Bool ret = EINA_TRUE;
|
||||
|
||||
EINA_SAFETY_ON_FALSE_RETURN_VAL((sx == 0) && (sy == 0), EINA_FALSE);
|
||||
|
||||
if (fillmode == EVAS_FILTER_FILL_MODE_NONE)
|
||||
{
|
||||
DBG("blend: %d,%d,%d,%d --> %d,%d,%d,%d", sx, sy, sw, sh, dx, dy, sw, sh);
|
||||
|
@ -35,7 +33,6 @@ _mapped_blend(Evas_Engine_GL_Context *gc,
|
|||
else if (fillmode & EVAS_FILTER_FILL_MODE_STRETCH_X)
|
||||
{
|
||||
cols = 0;
|
||||
dx = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -58,7 +55,6 @@ _mapped_blend(Evas_Engine_GL_Context *gc,
|
|||
else if (fillmode & EVAS_FILTER_FILL_MODE_STRETCH_Y)
|
||||
{
|
||||
rows = 0;
|
||||
dy = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -95,9 +91,10 @@ _mapped_blend(Evas_Engine_GL_Context *gc,
|
|||
src_y = 0;
|
||||
if (fillmode & EVAS_FILTER_FILL_MODE_STRETCH_Y)
|
||||
{
|
||||
src_y = sy;
|
||||
src_h = sh;
|
||||
dst_y = dy;
|
||||
dst_h = dh;
|
||||
dst_y = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -133,9 +130,10 @@ _mapped_blend(Evas_Engine_GL_Context *gc,
|
|||
src_x = 0;
|
||||
if (fillmode & EVAS_FILTER_FILL_MODE_STRETCH_X)
|
||||
{
|
||||
src_x = sx;
|
||||
src_w = sw;
|
||||
dst_x = dx;
|
||||
dst_w = dw;
|
||||
dst_x = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -163,7 +161,7 @@ _gl_filter_blend(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 src_w, src_h, dst_w, dst_h;
|
||||
int src_w, src_h, dst_w, dst_h, src_x, src_y, dst_x, dst_y;
|
||||
|
||||
DEBUG_TIME_BEGIN();
|
||||
|
||||
|
@ -188,40 +186,54 @@ _gl_filter_blend(Render_Engine_GL_Generic *re, Evas_Filter_Command *cmd)
|
|||
if ((cmd->draw.fillmode == EVAS_FILTER_FILL_MODE_STRETCH_XY) &&
|
||||
((image->w != surface->w) || (image->h != surface->h)))
|
||||
{
|
||||
int scale_w, scale_h;
|
||||
double scale_w, scale_h;
|
||||
int pad_x, pad_y;
|
||||
|
||||
if ((image->w >= surface->w) ||
|
||||
(image->h >= surface->h))
|
||||
pad_x = cmd->draw.scale.pad_x;
|
||||
pad_y = cmd->draw.scale.pad_y;
|
||||
scale_w = MAX(cmd->draw.scale.factor_x, 1);
|
||||
scale_h = MAX(cmd->draw.scale.factor_y, 1);
|
||||
|
||||
if (cmd->draw.scale.down)
|
||||
{
|
||||
scale_w = image->w / surface->w;
|
||||
scale_h = image->h / surface->h;
|
||||
src_w = ((image->w + (scale_w - 1)) / scale_w) * scale_w;
|
||||
src_h = ((image->h + (scale_h - 1)) / scale_h) * scale_h;
|
||||
dst_w = surface->w;
|
||||
dst_h = surface->h;
|
||||
src_x = -pad_x;
|
||||
src_y = -pad_y;
|
||||
src_w = (ceil(image->w / scale_w) + 1) * scale_w;
|
||||
src_h = (ceil(image->h / scale_h) + 1) * scale_h;
|
||||
dst_x = 0;
|
||||
dst_y = 0;
|
||||
dst_w = src_w / scale_w;
|
||||
dst_h = src_h / scale_h;
|
||||
}
|
||||
else
|
||||
{
|
||||
scale_w = surface->w / image->w;
|
||||
scale_h = surface->h / image->h;
|
||||
src_x = 0;
|
||||
src_y = 0;
|
||||
src_w = image->w;
|
||||
src_h = image->h;
|
||||
dst_w = ((surface->w + (scale_w - 1)) / scale_w) * scale_w;
|
||||
dst_h = ((surface->h + (scale_h - 1)) / scale_h) * scale_h;
|
||||
dst_x = cmd->draw.ox - pad_x;
|
||||
dst_y = cmd->draw.oy - pad_y;
|
||||
dst_w = (ceil(surface->w / scale_w) + 1) * scale_w;
|
||||
dst_h = (ceil(surface->h / scale_h) + 1) * scale_h;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
src_x = 0;
|
||||
src_y = 0;
|
||||
src_w = image->w;
|
||||
src_h = image->h;
|
||||
dst_x = cmd->draw.ox;
|
||||
dst_y = cmd->draw.oy;
|
||||
dst_w = surface->w;
|
||||
dst_h = surface->h;
|
||||
}
|
||||
|
||||
DBG("blend %d @%p -> %d @%p", cmd->input->id, cmd->input->buffer,
|
||||
cmd->output->id, cmd->output->buffer);
|
||||
_mapped_blend(gc, image, cmd->draw.fillmode, 0, 0, src_w, src_h,
|
||||
cmd->draw.ox, cmd->draw.oy, dst_w, dst_h);
|
||||
_mapped_blend(gc, image, cmd->draw.fillmode,
|
||||
src_x, src_y, src_w, src_h,
|
||||
dst_x, dst_y, dst_w, dst_h);
|
||||
|
||||
evas_common_draw_context_free(gc->dc);
|
||||
gc->dc = dc_save;
|
||||
|
|
Loading…
Reference in New Issue