evas_render: Fix some clipping issues inside proxy

Inside a proxy, clipping information might be wrong since the
source object may be at a different position than within
the proxy. If source_clip is not set, then we need to discard
all clips that are outside the proxy context.

So we just propagate the clip information inside the current
draw context, and even recurse from clipper to clipper to
find the final state of clipping.

Map and proxies and others (who said masks?) should definitely
rely more on the same model.

This code is not a mess. At all. You gotta love evas_render.
This commit is contained in:
Jean-Philippe Andre 2015-09-03 17:38:48 +09:00
parent 4ac9d2af54
commit ef0ec6bed3
3 changed files with 102 additions and 23 deletions

View File

@ -137,6 +137,7 @@ _texture_proxy_subrender(Evas_Canvas3D_Texture *obj)
.eo_proxy = NULL,
.proxy_obj = NULL,
.eo_src = pd->source,
.src_obj = source,
.source_clip = EINA_FALSE
};

View File

@ -1203,6 +1203,31 @@ _evas_render_can_use_overlay(Evas_Public_Data *e, Evas_Object *eo_obj)
return EINA_TRUE;
}
static Eina_Bool
_proxy_context_clip(Evas_Public_Data *evas, void *ctx, Evas_Proxy_Render_Data *proxy_render_data, Evas_Object_Protected_Data *obj, int off_x, int off_y)
{
const Evas_Coord_Rectangle *clip;
Evas_Object_Protected_Data *clipper;
/* cache.clip can not be relied on, since the evas is frozen, but we need
* to set the clip. so we recurse from clipper to clipper until we reach
* the source object's clipper */
if (!proxy_render_data || proxy_render_data->source_clip) return EINA_TRUE;
if (!obj || !obj->cur->clipper) return EINA_TRUE;
clipper = obj->cur->clipper;
if (!clipper->cur->visible) return EINA_FALSE;
clip = &clipper->cur->geometry;
ENFN->context_clip_clip(ENDT, ctx, clip->x + off_x, clip->y + off_y, clip->w, clip->h);
/* stop if we found the source object's clipper */
if (clipper == proxy_render_data->proxy_obj->cur->clipper) return EINA_TRUE;
/* recurse to the clipper itself */
return _proxy_context_clip(evas, ctx, proxy_render_data, clipper, off_x, off_y);
}
static void
_evas_render_mapped_context_clip_set(Evas_Public_Data *evas, Evas_Object *eo_obj, Evas_Object_Protected_Data *obj, void *ctx, Evas_Proxy_Render_Data *proxy_render_data, int off_x, int off_y)
{
@ -1253,9 +1278,9 @@ evas_render_mapped(Evas_Public_Data *evas, Evas_Object *eo_obj,
Eina_Bool proxy_src_clip = EINA_TRUE;
void *ctx;
//Don't Render if the source is invisible.
if (!proxy_render_data)
{
/* don't render if the source is invisible */
if ((evas_object_is_source_invisible(eo_obj, obj)))
return clean_them;
}
@ -1264,6 +1289,10 @@ evas_render_mapped(Evas_Public_Data *evas, Evas_Object *eo_obj,
evas_object_clip_recalc(obj);
/* leave early if clipper is not visible */
if (obj->cur->clipper && !obj->cur->clipper->cur->visible)
return clean_them;
#ifdef REND_DBG
RD(level, "{\n");
RD(level, " evas_render_mapped(evas:%p, obj:%p", evas, obj);
@ -1500,6 +1529,7 @@ evas_render_mapped(Evas_Public_Data *evas, Evas_Object *eo_obj,
/* duplicate context and reset clip */
ctx = ENFN->context_dup(ENDT, context);
ENFN->context_clip_unset(ENDT, ctx);
//ENFN->context_multiplier_unset(ENDT, ctx); // this probably should be here, too
if (obj->map->surface)
{
@ -1611,6 +1641,18 @@ evas_render_mapped(Evas_Public_Data *evas, Evas_Object *eo_obj,
evas, do_async);
}
}
else if (!proxy_src_clip)
{
if (!_proxy_context_clip(evas, ctx, proxy_render_data, obj, off_x, off_y))
return clean_them;
}
#ifdef REND_DBG
int _c, _cx, _cy, _cw, _ch;
_c = ENFN->context_clip_get(ENDT, ctx, &_cx, &_cy, &_cw, &_ch);
RD(level, " draw smart children(clip: [%d] %d,%d %dx%d)\n",
_c, _cx, _cy, _cw, _ch);
#endif
EINA_INLIST_FOREACH
(evas_object_smart_members_get_direct(eo_obj), obj2)
@ -1634,14 +1676,25 @@ evas_render_mapped(Evas_Public_Data *evas, Evas_Object *eo_obj,
}
else
{
const Evas_Coord_Rectangle *clip = &obj->cur->geometry;
ENFN->context_clip_clip(ENDT, ctx, clip->x + off_x, clip->y + off_y, clip->w, clip->h);
if (obj->cur->clipper && (mapped > 1))
{
if (_evas_render_has_map(eo_obj, obj) ||
_evas_render_object_is_mask(obj->cur->clipper))
evas_object_clip_recalc(obj);
_evas_render_mapped_context_clip_set(evas, eo_obj, obj, ctx,
proxy_render_data,
off_x, off_y);
if (proxy_src_clip)
{
if (_evas_render_has_map(eo_obj, obj) ||
_evas_render_object_is_mask(obj->cur->clipper))
evas_object_clip_recalc(obj);
_evas_render_mapped_context_clip_set(evas, eo_obj, obj, ctx,
proxy_render_data,
off_x, off_y);
}
else
{
if (!_proxy_context_clip(evas, ctx, proxy_render_data, obj, off_x, off_y))
return clean_them;
}
/* Clipper masks */
if (_evas_render_object_is_mask(obj->cur->clipper))
@ -1664,11 +1717,13 @@ evas_render_mapped(Evas_Public_Data *evas, Evas_Object *eo_obj,
}
}
}
#ifdef REND_DBG
int _c, _cx, _cy, _cw, _ch;
_c = ENFN->context_clip_get(ENDT, ctx, &_cx, &_cy, &_cw, &_ch);
RD(level, " render(clip: [%d] %d,%d %dx%d)\n", _c, _cx, _cy, _cw, _ch);
#endif
obj->func->render(eo_obj, obj, obj->private_data,
ENDT, ctx, surface, off_x, off_y, EINA_FALSE);
}
@ -1677,26 +1732,39 @@ evas_render_mapped(Evas_Public_Data *evas, Evas_Object *eo_obj,
}
else
{
/* in this case we keep the parent context */
const Evas_Coord_Rectangle *clip = &obj->cur->geometry;
ctx = ENFN->context_dup(ENDT, context);
ENFN->context_clip_clip(ENDT, ctx, clip->x + off_x, clip->y + off_y, clip->w, clip->h);
if (obj->cur->clipper)
{
Evas_Object_Protected_Data *clipper = obj->cur->clipper;
int x, y, w, h;
if (_evas_render_has_map(eo_obj, obj) ||
_evas_render_object_is_mask(obj->cur->clipper))
evas_object_clip_recalc(obj);
x = obj->cur->cache.clip.x;
y = obj->cur->cache.clip.y;
w = obj->cur->cache.clip.w;
h = obj->cur->cache.clip.h;
RECTS_CLIP_TO_RECT(x, y, w, h,
clipper->cur->cache.clip.x,
clipper->cur->cache.clip.y,
clipper->cur->cache.clip.w,
clipper->cur->cache.clip.h);
ENFN->context_clip_set(ENDT, context, x + off_x, y + off_y, w, h);
ENFN->context_clip_clip(ENDT, context, ecx, ecy, ecw, ech);
if (proxy_src_clip)
{
if (_evas_render_has_map(eo_obj, obj) ||
_evas_render_object_is_mask(obj->cur->clipper))
evas_object_clip_recalc(obj);
x = obj->cur->cache.clip.x;
y = obj->cur->cache.clip.y;
w = obj->cur->cache.clip.w;
h = obj->cur->cache.clip.h;
RECTS_CLIP_TO_RECT(x, y, w, h,
clipper->cur->cache.clip.x,
clipper->cur->cache.clip.y,
clipper->cur->cache.clip.w,
clipper->cur->cache.clip.h);
ENFN->context_clip_set(ENDT, ctx, x + off_x, y + off_y, w, h);
if (proxy_src_clip)
ENFN->context_clip_clip(ENDT, ctx, ecx, ecy, ecw, ech);
}
else
{
if (!_proxy_context_clip(evas, ctx, proxy_render_data, obj, off_x, off_y))
return clean_them;
}
}
#ifdef REND_DBG
@ -1704,9 +1772,11 @@ evas_render_mapped(Evas_Public_Data *evas, Evas_Object *eo_obj,
_c = ENFN->context_clip_get(ENDT, context, &_cx, &_cy, &_cw, &_ch);
RD(level, " draw normal obj: render(clip: [%d] %d,%d %dx%d)\n", _c, _cx, _cy, _cw, _ch);
#endif
obj->func->render(eo_obj, obj, obj->private_data,
ENDT, context, surface,
ENDT, ctx, surface,
off_x, off_y, do_async);
ENFN->context_free(ENDT, ctx);
}
if (obj->changed_map) clean_them = EINA_TRUE;
}
@ -1782,9 +1852,13 @@ evas_render_proxy_subrender(Evas *eo_e, Evas_Object *eo_source, Evas_Object *eo_
.eo_proxy = eo_proxy,
.proxy_obj = proxy_obj,
.eo_src = eo_source,
.src_obj = source,
.source_clip = source_clip
};
/* protect changes to the objects' cache.clip */
evas_event_freeze(evas->evas);
ctx = ENFN->context_new(ENDT);
evas_render_mapped(evas, eo_source, source, ctx, proxy_write->surface,
-source->cur->geometry.x,
@ -1794,6 +1868,9 @@ evas_render_proxy_subrender(Evas *eo_e, Evas_Object *eo_source, Evas_Object *eo_
ENFN->context_free(ENDT, ctx);
proxy_write->surface = ENFN->image_dirty_region(ENDT, proxy_write->surface, 0, 0, w, h);
/* restore previous state */
evas_event_thaw(evas->evas);
}
end:
EINA_COW_WRITE_END(evas_object_proxy_cow, source->proxy, proxy_write);

View File

@ -1824,6 +1824,7 @@ struct _Evas_Proxy_Render_Data
Evas_Object *eo_proxy;
Evas_Object_Protected_Data *proxy_obj;
Evas_Object *eo_src;
Evas_Object_Protected_Data *src_obj;
Eina_Bool source_clip : 1;
};