evas filters: Force redraw of snapshot if cutout shrank

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.
This commit is contained in:
Jean-Philippe Andre 2017-03-23 15:55:41 +09:00
parent a8b6c1cd7f
commit 530399acde
4 changed files with 80 additions and 57 deletions

View 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

View 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;
else if (!opaque.w || !opaque.h)
opaque = cur;
else if (_evas_eina_rectangle_inside(&cur, &opaque))
opaque = cur;
//else if (!_evas_eina_rectangle_inside(&opaque, &cur))
if (!opaque.w || !opaque.h)
opaque = cur;
else if (_rectangle_inside(&cur, &opaque))
opaque = cur;
//else if (!_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);
}
}

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

View 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,