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:
Jean-Philippe Andre 2017-04-05 15:32:07 +09:00
parent 5467d1eb3e
commit 01a4ecd92c
7 changed files with 90 additions and 50 deletions

View File

@ -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?");

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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