evas filters: Force redraw of snapshot if cutout shrank
authorJean-Philippe Andre <jp.andre@samsung.com>
Thu, 23 Mar 2017 06:55:41 +0000 (15:55 +0900)
committerJean-Philippe Andre <jp.andre@samsung.com>
Fri, 14 Apr 2017 02:26:44 +0000 (11:26 +0900)
The situation is clearly visible in the Snapshot test case:
increase the radius and a red glow would appear. This is because
the snapshot object was not marked as needing redraw and so had
no pixels under the opaque rectangle.

src/lib/evas/canvas/evas_filter_mixin.c
src/lib/evas/canvas/evas_render.c
src/lib/evas/include/evas_inline.x
src/lib/evas/include/evas_private.h

index f3ed146..1a74cd8 100644 (file)
@@ -27,6 +27,7 @@ struct _Evas_Object_Filter_Data
    Eina_Hash           *sources; // Evas_Filter_Proxy_Binding
    Eina_Inlist         *data; // Evas_Filter_Data_Binding
    Eina_Rectangle       prev_obscured, obscured;
+   Evas_Filter_Padding  prev_padding, padding;
    void                *output;
    struct {
       struct {
@@ -283,36 +284,31 @@ evas_filter_object_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
         if (!pd->data->chain)
           {
              Evas_Filter_Program *pgm;
+             Eina_Bool invalid;
 
              pgm = evas_filter_program_new(pd->data->name, alpha);
              evas_filter_program_source_set_all(pgm, pd->data->sources);
              evas_filter_program_data_set_all(pgm, pd->data->data);
              _evas_filter_state_set_internal(pgm, pd);
-             if (!evas_filter_program_parse(pgm, pd->data->code))
+             invalid = !evas_filter_program_parse(pgm, pd->data->code);
+             if (invalid)
                {
                   ERR("Filter program parsing failed");
                   evas_filter_program_del(pgm);
-
-                  if (!pd->data->invalid)
-                    {
-                       fcow = FCOW_BEGIN(pd);
-                       fcow->invalid = EINA_TRUE;
-                       FCOW_END(fcow, pd);
-                    }
-
-                  return EINA_FALSE;
+                  pgm = NULL;
                }
              fcow = FCOW_BEGIN(pd);
+             if (!invalid) evas_filter_program_padding_get(pgm, NULL, &fcow->padding);
              fcow->chain = pgm;
-             fcow->invalid = EINA_FALSE;
+             fcow->invalid = invalid;
              FCOW_END(fcow, pd);
+             if (invalid) return EINA_FALSE;
           }
         else if (previous && !pd->data->changed)
           {
-             Eina_Bool redraw;
+             Eina_Bool redraw = EINA_TRUE;
 
-             redraw = _evas_filter_state_set_internal(pd->data->chain, pd);
-             if (redraw)
+             if (_evas_filter_state_set_internal(pd->data->chain, pd))
                DBG("Filter redraw by state change!");
              else if (obj->changed)
                DBG("Filter redraw by object content change!");
@@ -441,6 +437,8 @@ evas_filter_object_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
         fcow->changed = EINA_FALSE;
         fcow->async = do_async;
         fcow->prev_obscured = fcow->obscured;
+        fcow->prev_padding = fcow->padding;
+        fcow->padding = pad;
         fcow->invalid = EINA_FALSE;
         FCOW_END(fcow, pd);
 
@@ -470,6 +468,7 @@ _efl_canvas_filter_internal_efl_gfx_filter_filter_program_set(Eo *eo_obj, Evas_F
    Evas_Object_Protected_Data *obj;
    Evas_Filter_Program *pgm = NULL;
    Evas_Object_Filter_Data *fcow;
+   Eina_Bool invalid = pd->data->invalid;
    Eina_Bool alpha;
 
    obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
@@ -494,16 +493,21 @@ _efl_canvas_filter_internal_efl_gfx_filter_filter_program_set(Eo *eo_obj, Evas_F
            evas_filter_program_source_set_all(pgm, fcow->sources);
            evas_filter_program_data_set_all(pgm, fcow->data);
            _evas_filter_state_set_internal(pgm, pd);
-           if (!evas_filter_program_parse(pgm, code))
+           invalid = !evas_filter_program_parse(pgm, code);
+           if (invalid)
              {
                 ERR("Parsing failed!");
                 evas_filter_program_del(pgm);
                 pgm = NULL;
              }
+           else
+             {
+                evas_filter_program_padding_get(pgm, NULL, &fcow->padding);
+             }
         }
       fcow->chain = pgm;
       fcow->changed = EINA_TRUE;
-      fcow->invalid = (pgm == NULL);
+      fcow->invalid = invalid;
       eina_stringshare_replace(&fcow->code, code);
    }
    FCOW_END(fcow, pd);
@@ -526,6 +530,7 @@ _efl_canvas_filter_internal_efl_gfx_filter_filter_source_set(Eo *eo_obj, Evas_Fi
    Evas_Filter_Proxy_Binding *pb, *pb_old = NULL;
    Evas_Object_Protected_Data *source = NULL;
    Evas_Object_Filter_Data *fcow = NULL;
+   Eina_Bool invalid = pd->data->invalid;
 
    obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
    if (eo_source)
@@ -589,14 +594,15 @@ _efl_canvas_filter_internal_efl_gfx_filter_filter_source_set(Eo *eo_obj, Evas_Fi
    eina_hash_add(fcow->sources, pb->name, pb);
    evas_filter_program_source_set_all(fcow->chain, fcow->sources);
    evas_filter_program_data_set_all(fcow->chain, fcow->data);
-   evas_filter_program_parse(fcow->chain, fcow->code);
+   invalid = !evas_filter_program_parse(fcow->chain, fcow->code);
+   if (!invalid) evas_filter_program_padding_get(fcow->chain, NULL, &fcow->padding);
 
    // Update object
 update:
    if (fcow)
      {
         fcow->changed = EINA_TRUE;
-        fcow->invalid = EINA_FALSE;
+        fcow->invalid = invalid;
         FCOW_END(fcow, pd);
      }
 
@@ -755,6 +761,7 @@ _efl_canvas_filter_internal_efl_gfx_filter_filter_data_set(Eo *eo_obj, Evas_Filt
 {
    Evas_Filter_Data_Binding *db, *found = NULL;
    Evas_Object_Filter_Data *fcow;
+   Eina_Bool invalid = pd->data->invalid;
 
    EINA_SAFETY_ON_NULL_RETURN(pd->data);
    EINA_SAFETY_ON_NULL_RETURN(name);
@@ -789,12 +796,13 @@ _efl_canvas_filter_internal_efl_gfx_filter_filter_data_set(Eo *eo_obj, Evas_Filt
            db->execute = execute;
            fcow->data = eina_inlist_append(fcow->data, EINA_INLIST_GET(db));
         }
-      fcow->invalid = EINA_FALSE;
       if (fcow->chain)
         {
            evas_filter_program_data_set_all(fcow->chain, fcow->data);
-           evas_filter_program_parse(fcow->chain, fcow->code);
+           invalid = !evas_filter_program_parse(fcow->chain, fcow->code);
+           if (!invalid) evas_filter_program_padding_get(fcow->chain, NULL, &fcow->padding);
         }
+      fcow->invalid = invalid;
       fcow->changed = 1;
    }
    FCOW_END(fcow, pd);
@@ -832,15 +840,18 @@ _efl_canvas_filter_internal_filter_output_buffer_get(Eo *obj EINA_UNUSED, Evas_F
    return pd->data->output;
 }
 
-void
+Eina_Bool
 _evas_filter_obscured_region_set(Evas_Object_Protected_Data *obj,
                                  const Eina_Rectangle rect)
 {
-   Evas_Filter_Data *pd;
    Evas_Object_Filter_Data *fcow;
+   Evas_Filter_Data *pd;
+   Eina_Rectangle prev;
 
    pd = efl_data_scope_get(obj->object, MY_CLASS);
-   if (!pd->data) return;
+   if (!pd->data) return EINA_FALSE;
+
+   prev = pd->data->prev_obscured;
 
    fcow = FCOW_BEGIN(pd);
    if ((rect.w <= 0) || (rect.h <= 0))
@@ -853,6 +864,19 @@ _evas_filter_obscured_region_set(Evas_Object_Protected_Data *obj,
         fcow->obscured.h = rect.h;
      }
    FCOW_END(fcow, pd);
+
+   // Snapshot objects need to be redrawn if the padding has increased
+   if ((pd->data->prev_padding.l < pd->data->padding.l) ||
+       (pd->data->prev_padding.r < pd->data->padding.r) ||
+       (pd->data->prev_padding.t < pd->data->padding.t) ||
+       (pd->data->prev_padding.b < pd->data->padding.b))
+     return EINA_TRUE;
+
+   // Snapshot objects need to be redrawn if the obscured region has shrank
+   if (!_evas_eina_rectangle_inside(&prev, &pd->data->obscured))
+     return EINA_TRUE;
+
+   return EINA_FALSE;
 }
 
 void
index cd63a63..5c7720c 100644 (file)
@@ -2747,18 +2747,6 @@ _is_obj_in_rect(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj,
 }
 #endif
 
-static inline Eina_Bool
-_rectangle_inside(Eina_Rectangle *big, Eina_Rectangle *small)
-{
-   Eina_Rectangle inter = *big;
-
-   if (!eina_rectangle_intersection(&inter, small))
-     return EINA_FALSE;
-   if ((inter.w == small->w) && (inter.h == small->h))
-     return EINA_TRUE;
-   return EINA_FALSE;
-}
-
 static void
 _snapshot_redraw_update(Evas_Public_Data *evas, Evas_Object_Protected_Data *snap)
 {
@@ -2769,16 +2757,23 @@ _snapshot_redraw_update(Evas_Public_Data *evas, Evas_Object_Protected_Data *snap
    const int h = snap->cur->geometry.h;
    Evas_Object_Protected_Data *obj;
    Evas_Active_Entry *ent;
-   Eina_Rectangle snap_rect = { x, y, w, h };
+   Eina_Rectangle snap_clip, snap_rect = { x, y, w, h };
    Eina_Rectangle opaque = {};
    void *surface;
 
    // FIXME: Use evas' standard rectangle logic instead of this bad algo
+   // FIXME: This walks ALL the objects in the canvas to find the opaque region
    // TODO: Improve opaque region support, maybe have more than one
    // TODO: Also list redraw regions (partial updates)
 
    if (!evas_object_is_visible(snap->object, snap)) return;
 
+   evas_object_clip_recalc(snap);
+   snap_clip.x = snap->cur->cache.clip.x;
+   snap_clip.y = snap->cur->cache.clip.y;
+   snap_clip.w = snap->cur->cache.clip.w;
+   snap_clip.h = snap->cur->cache.clip.h;
+
    surface = _evas_object_image_surface_get(snap, EINA_FALSE);
    if (!surface) need_redraw = EINA_TRUE;
    if (snap->changed) add_rect = EINA_TRUE;
@@ -2788,7 +2783,6 @@ _snapshot_redraw_update(Evas_Public_Data *evas, Evas_Object_Protected_Data *snap
         obj = ent->obj;
         if (obj == snap)
           {
-             if (!need_redraw) break;
              above = EINA_TRUE;
              continue;
           }
@@ -2811,35 +2805,28 @@ _snapshot_redraw_update(Evas_Public_Data *evas, Evas_Object_Protected_Data *snap
                   };
 
                   if ((opaque.w * opaque.h) < (cur.w * cur.h))
-                    {
-                       opaque = cur;
-                       continue;
-                    }
-
-                  if (!eina_rectangles_intersect(&snap_rect, &cur))
+                    opaque = cur;
+                  else if (!eina_rectangles_intersect(&snap_rect, &cur))
                     continue;
-
-                  if (!opaque.w || !opaque.h)
+                  else if (!opaque.w || !opaque.h)
                     opaque = cur;
-                  else if (_rectangle_inside(&cur, &opaque))
+                  else if (_evas_eina_rectangle_inside(&cur, &opaque))
                     opaque = cur;
-                  //else if (!_rectangle_inside(&opaque, &cur))
+                  //else if (!_evas_eina_rectangle_inside(&opaque, &cur))
+
+                  if (_evas_eina_rectangle_inside(&opaque, &snap_clip))
+                    return;
                }
           }
      }
 
-   _evas_filter_obscured_region_set(snap, opaque);
+   need_redraw |= _evas_filter_obscured_region_set(snap, opaque);
    snap->snapshot_needs_redraw |= need_redraw;
 
    if (add_rect || need_redraw)
      {
-        // FIXME: Only add necessary rects
-        // Note that with filters this is extremely tricky: a simple color
-        // change would mean redraw all. Also blurs, displace, etc... need
-        // to expand by the cutout_margin (filter padding).
-        ENFN->output_redraws_rect_add(ENDT,
-                                      snap->cur->geometry.x, snap->cur->geometry.y,
-                                      snap->cur->geometry.w, snap->cur->geometry.h);
+        // FIXME: Only add necessary rects (if object itself hasn't changed)
+        ENFN->output_redraws_rect_add(ENDT, x, y, w, h);
      }
 }
 
index d7aa37a..37cf44d 100644 (file)
@@ -327,6 +327,18 @@ evas_common_draw_context_cache_update(RGBA_Draw_Context *dc)
      }
 }
 
+static inline Eina_Bool
+_evas_eina_rectangle_inside(const Eina_Rectangle *big, const Eina_Rectangle *small)
+{
+   Eina_Rectangle inter = *big;
+
+   if (!eina_rectangle_intersection(&inter, small))
+     return EINA_FALSE;
+   if ((inter.w == small->w) && (inter.h == small->h))
+     return EINA_TRUE;
+   return EINA_FALSE;
+}
+
 #define _EVAS_COLOR_CLAMP(x, y) do { \
    if (x > y) { x = y; bad = 1; } \
    if (x < 0) { x = 0; bad = 1; } } while (0)
index 3571cd6..33e5a45 100644 (file)
@@ -2012,7 +2012,7 @@ Eina_Bool evas_vg_loader_svg(Evas_Object *vg, const Eina_File *f, const char *ke
 
 void *_evas_object_image_surface_get(Evas_Object_Protected_Data *obj, Eina_Bool create);
 void _evas_filter_radius_get(Evas_Object_Protected_Data *obj, int *l, int *r, int *t, int *b);
-void _evas_filter_obscured_region_set(Evas_Object_Protected_Data *obj, const Eina_Rectangle rect);
+Eina_Bool _evas_filter_obscured_region_set(Evas_Object_Protected_Data *obj, const Eina_Rectangle rect);
 Eina_Bool _evas_image_proxy_source_clip_get(const Eo *eo_obj);
 
 void _evas_focus_dispatch_event(Evas_Object_Protected_Data *obj,