jpeg pushed a commit to branch master.

http://git.enlightenment.org/core/efl.git/commit/?id=01a4ecd92c41e9159aafa59754a7be842f5ca0fb

commit 01a4ecd92c41e9159aafa59754a7be842f5ca0fb
Author: Jean-Philippe Andre <[email protected]>
Date:   Wed Apr 5 15:32:07 2017 +0900

    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).
---
 src/lib/evas/canvas/evas_filter_mixin.c            |  4 +-
 src/lib/evas/canvas/evas_object_textblock.c        |  2 +-
 src/lib/evas/filters/evas_filter.c                 | 64 ++++++++++++++--------
 src/lib/evas/filters/evas_filter_parser.c          |  4 +-
 src/lib/evas/filters/evas_filter_private.h         |  6 ++
 src/lib/evas/include/evas_filter.h                 |  4 +-
 .../engines/gl_generic/filters/gl_filter_blend.c   | 56 +++++++++++--------
 7 files changed, 90 insertions(+), 50 deletions(-)

diff --git a/src/lib/evas/canvas/evas_filter_mixin.c 
b/src/lib/evas/canvas/evas_filter_mixin.c
index d9ada4f..31a7628 100644
--- a/src/lib/evas/canvas/evas_filter_mixin.c
+++ b/src/lib/evas/canvas/evas_filter_mixin.c
@@ -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?");
diff --git a/src/lib/evas/canvas/evas_object_textblock.c 
b/src/lib/evas/canvas/evas_object_textblock.c
index f65624f..abf076c 100644
--- a/src/lib/evas/canvas/evas_object_textblock.c
+++ b/src/lib/evas/canvas/evas_object_textblock.c
@@ -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);
diff --git a/src/lib/evas/filters/evas_filter.c 
b/src/lib/evas/filters/evas_filter.c
index 2500590..419c662 100644
--- a/src/lib/evas/filters/evas_filter.c
+++ b/src/lib/evas/filters/evas_filter.c
@@ -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;
diff --git a/src/lib/evas/filters/evas_filter_parser.c 
b/src/lib/evas/filters/evas_filter_parser.c
index 60a7ecb..ccf2806 100644
--- a/src/lib/evas/filters/evas_filter_parser.c
+++ b/src/lib/evas/filters/evas_filter_parser.c
@@ -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);
diff --git a/src/lib/evas/filters/evas_filter_private.h 
b/src/lib/evas/filters/evas_filter_private.h
index 01cda48..576cef3 100644
--- a/src/lib/evas/filters/evas_filter_private.h
+++ b/src/lib/evas/filters/evas_filter_private.h
@@ -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;
diff --git a/src/lib/evas/include/evas_filter.h 
b/src/lib/evas/include/evas_filter.h
index 6972159..ca79b36 100644
--- a/src/lib/evas/include/evas_filter.h
+++ b/src/lib/evas/include/evas_filter.h
@@ -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)
diff --git a/src/modules/evas/engines/gl_generic/filters/gl_filter_blend.c 
b/src/modules/evas/engines/gl_generic/filters/gl_filter_blend.c
index 5f6c84e..3cc1b0f 100644
--- a/src/modules/evas/engines/gl_generic/filters/gl_filter_blend.c
+++ b/src/modules/evas/engines/gl_generic/filters/gl_filter_blend.c
@@ -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;

-- 


Reply via email to