efl/legacy/evas/src/lib/canvas/evas_render.c

1438 lines
44 KiB
C

#include "evas_common.h"
#include "evas_private.h"
// debug rendering
//#define REND_DGB 1
#ifdef REND_DGB
static FILE *dbf = NULL;
static void
rend_dbg(const char *txt)
{
if (!dbf)
{
dbf = fopen("EVAS-RENDER-DEBUG.log", "w");
if (!dbf) return;
}
fputs(txt, dbf);
fflush(dbf);
}
#define RD(args...) \
{ \
char __tmpbuf[4096]; \
\
snprintf(__tmpbuf, sizeof(__tmpbuf), ##args); \
rend_dbg(__tmpbuf); \
}
#define RDI(xxxx) \
{ \
char __tmpbuf[4096]; int __tmpi; \
for (__tmpi = 0; __tmpi < xxxx; __tmpi++) \
__tmpbuf[__tmpi] = ' '; \
__tmpbuf[__tmpi] = 0; \
rend_dbg(__tmpbuf); \
}
#else
#define RD(args...)
#define RDI(x)
#endif
static Eina_List *
evas_render_updates_internal(Evas *e, unsigned char make_updates, unsigned char do_draw);
/**
* Add a damage rectangle.
*
* @param e The given canvas pointer.
* @param x The rectangle's left position.
* @param y The rectangle's top position.
* @param w The rectangle's width.
* @param h The rectangle's height.
*
* This is the function by which one tells evas that a part of the
* canvas has to be repainted.
*
* @ingroup Evas_Canvas
*/
EAPI void
evas_damage_rectangle_add(Evas *e, int x, int y, int w, int h)
{
Eina_Rectangle *r;
MAGIC_CHECK(e, Evas, MAGIC_EVAS);
return;
MAGIC_CHECK_END();
NEW_RECT(r, x, y, w, h);
if (!r) return;
e->damages = eina_list_append(e->damages, r);
e->changed = 1;
}
/**
* Add an obscured region.
*
* @param e The given canvas pointer.
* @param x The rectangle's left position.
* @param y The rectangle's top position
* @param w The rectangle's width.
* @param h The rectangle's height.
*
* This is the function by which one tells evas that a part of the
* canvas has not to be repainted. To make this region one that have
* to be repainted, call the function evas_obscured_clear().
*
* @see evas_obscured_clear().
*
* @ingroup Evas_Canvas
*/
EAPI void
evas_obscured_rectangle_add(Evas *e, int x, int y, int w, int h)
{
Eina_Rectangle *r;
MAGIC_CHECK(e, Evas, MAGIC_EVAS);
return;
MAGIC_CHECK_END();
NEW_RECT(r, x, y, w, h);
if (!r) return;
e->obscures = eina_list_append(e->obscures, r);
}
/**
* Remove all obscured region rectangles from the canvas.
*
* @param e The given canvas pointer.
*
* This function removes all the rectangles from the obscured list of
* the canvas. It takes obscured areas added with
* evas_obscured_rectangle_add() and makes it a region that have to be
* repainted.
*
* @ingroup Evas_Canvas
*/
EAPI void
evas_obscured_clear(Evas *e)
{
Eina_Rectangle *r;
MAGIC_CHECK(e, Evas, MAGIC_EVAS);
return;
MAGIC_CHECK_END();
EINA_LIST_FREE(e->obscures, r)
{
eina_rectangle_free(r);
}
}
static Eina_Bool
_evas_render_has_map(Evas_Object *obj)
{
return ((!((obj->func->can_map) && (obj->func->can_map(obj)))) &&
((obj->cur.map) && (obj->cur.usemap)));
}
static Eina_Bool
_evas_render_had_map(Evas_Object *obj)
{
return ((!obj->cur.map) && (obj->prev.usemap));
}
static Eina_Bool
_evas_render_is_relevant(Evas_Object *obj)
{
return ((evas_object_is_visible(obj) && (!obj->cur.have_clipees)) ||
(evas_object_was_visible(obj) && (!obj->prev.have_clipees)));
}
static Eina_Bool
_evas_render_can_render(Evas_Object *obj)
{
return (evas_object_is_visible(obj) && (!obj->cur.have_clipees));
}
static void
_evas_render_prev_cur_clip_cache_add(Evas *e, Evas_Object *obj)
{
e->engine.func->output_redraws_rect_add(e->engine.data.output,
obj->prev.cache.clip.x,
obj->prev.cache.clip.y,
obj->prev.cache.clip.w,
obj->prev.cache.clip.h);
e->engine.func->output_redraws_rect_add(e->engine.data.output,
obj->cur.cache.clip.x,
obj->cur.cache.clip.y,
obj->cur.cache.clip.w,
obj->cur.cache.clip.h);
}
static void
_evas_render_cur_clip_cache_del(Evas *e, Evas_Object *obj)
{
e->engine.func->output_redraws_rect_del(e->engine.data.output,
obj->cur.cache.clip.x,
obj->cur.cache.clip.y,
obj->cur.cache.clip.w,
obj->cur.cache.clip.h);
}
static void
_evas_render_phase1_direct(Evas *e,
Eina_Array *active_objects,
Eina_Array *restack_objects,
Eina_Array *delete_objects,
Eina_Array *render_objects)
{
unsigned int i;
RD(" [--- PHASE 1 DIRECT\n");
for (i = 0; i < render_objects->count; i++)
{
Evas_Object *obj;
obj = eina_array_data_get(render_objects, i);
RD(" OBJ [%p] changed %i\n", obj, obj->changed);
if (obj->changed)
{
evas_object_clip_recalc(obj);
obj->func->render_pre(obj);
if (obj->pre_render_done)
{
if ((obj->smart.smart) && _evas_render_has_map(obj))
{
RD(" has map + smart\n");
_evas_render_prev_cur_clip_cache_add(e, obj);
}
}
else if (_evas_render_had_map(obj))
{
RD(" no pre-render done\n");
_evas_render_prev_cur_clip_cache_add(e, obj);
}
}
else
{
if (obj->smart.smart)
{
// obj->func->render_pre(obj);
}
else if (obj->rect_del)
{
RD(" rect del\n");
_evas_render_cur_clip_cache_del(e, obj);
}
}
}
RD(" ---]\n");
}
static Eina_Bool
_evas_render_phase1_object_process(Evas *e, Evas_Object *obj,
Eina_Array *active_objects,
Eina_Array *restack_objects,
Eina_Array *delete_objects,
Eina_Array *render_objects,
int restack, int map
#ifdef REND_DGB
, int level
#endif
)
{
Eina_Bool clean_them = EINA_FALSE;
Evas_Object *obj2;
int is_active;
obj->rect_del = 0;
obj->render_pre = 0;
#ifndef EVAS_FRAME_QUEUING
/* because of clip objects - delete 2 cycles later */
if (obj->delete_me == 2)
#else
if (obj->delete_me == evas_common_frameq_get_frameq_sz() + 2)
#endif
eina_array_push(delete_objects, obj);
else if (obj->delete_me != 0) obj->delete_me++;
/* If the object will be removed, we should not cache anything during this run. */
if (obj->delete_me != 0) clean_them = EINA_TRUE;
/* build active object list */
evas_object_clip_recalc(obj);
is_active = evas_object_is_active(obj);
obj->is_active = is_active;
RDI(level);
RD(" [--- PROCESS [%p] '%s' active = %i, del = %i | %i %i %ix%i\n", obj, obj->type, is_active, obj->delete_me, obj->cur.geometry.x, obj->cur.geometry.y, obj->cur.geometry.w, obj->cur.geometry.h);
if ((is_active) || (obj->delete_me != 0))
eina_array_push(active_objects, obj);
#ifdef REND_DGB
if (!is_active)
{
RDI(level);
RD(" [%p] vis: %i, cache.clip.vis: %i cache.clip.a: %i [%p]\n", obj, obj->cur.visible, obj->cur.cache.clip.visible, obj->cur.cache.clip.a, obj->func->is_visible);
}
#endif
if (_evas_render_has_map(obj)) map = 1;
if ((restack) && (!map))
{
if (!obj->changed)
{
eina_array_push(&e->pending_objects, obj);
obj->changed = 1;
}
obj->restack = 1;
clean_them = EINA_TRUE;
}
if (map)
{
RDI(level);
RD(" obj mapped\n");
if (obj->changed)
{
evas_object_clip_recalc(obj);
if ((obj->restack) &&
(is_active) && (!obj->clip.clipees) &&
((evas_object_is_visible(obj) && (!obj->cur.have_clipees)) ||
(evas_object_was_visible(obj) && (!obj->prev.have_clipees))))
{
eina_array_push(render_objects, obj);
_evas_render_prev_cur_clip_cache_add(e, obj);
obj->render_pre = 1;
}
else if ((is_active) && (!obj->clip.clipees) &&
((evas_object_is_visible(obj) && (!obj->cur.have_clipees)) ||
(evas_object_was_visible(obj) && (!obj->prev.have_clipees))))
{
eina_array_push(render_objects, obj);
_evas_render_prev_cur_clip_cache_add(e, obj);
obj->render_pre = 1;
}
}
return clean_them;
}
else if (_evas_render_had_map(obj))
{
RDI(level);
RD(" had map - restack objs\n");
// eina_array_push(restack_objects, obj);
_evas_render_prev_cur_clip_cache_add(e, obj);
}
/* handle normal rendering. this object knows how to handle maps */
if (obj->changed)
{
if (obj->smart.smart)
{
RDI(level);
RD(" changed + smart - render ok\n");
eina_array_push(render_objects, obj);
obj->render_pre = 1;
EINA_INLIST_FOREACH(evas_object_smart_members_get_direct(obj), obj2)
{
_evas_render_phase1_object_process(e, obj2,
active_objects,
restack_objects,
delete_objects,
render_objects,
obj->restack,
map
#ifdef REND_DGB
, level + 1
#endif
);
}
}
else
{
if ((is_active) && (!obj->clip.clipees) &&
_evas_render_is_relevant(obj))
{
RDI(level);
RD(" relevant + active\n");
if (obj->restack)
eina_array_push(restack_objects, obj);
else
{
eina_array_push(render_objects, obj);
obj->render_pre = 1;
}
}
else
{
RDI(level);
RD(" skip - not smart, not active or clippees or not relevant\n");
}
}
}
else
{
RD(" not changed... [%i] -> (%i %i %p %i) [%i]\n",
evas_object_is_visible(obj),
obj->cur.visible, obj->cur.cache.clip.visible, obj->smart.smart, obj->cur.cache.clip.a,
evas_object_was_visible(obj));
if ((!obj->clip.clipees) && (obj->delete_me == 0) &&
(_evas_render_can_render(obj) ||
(evas_object_was_visible(obj) && (!obj->prev.have_clipees))))
{
if (obj->smart.smart)
{
RDI(level);
RD(" smart + visible/was visible + not clip\n");
eina_array_push(render_objects, obj);
obj->render_pre = 1;
EINA_INLIST_FOREACH
(evas_object_smart_members_get_direct(obj), obj2)
{
_evas_render_phase1_object_process(e, obj2,
active_objects,
restack_objects,
delete_objects,
render_objects,
restack, map
#ifdef REND_DGB
, level + 1
#endif
);
}
}
else
{
if (evas_object_is_opaque(obj) &&
evas_object_is_visible(obj))
{
RDI(level);
RD(" opaque + visible\n");
eina_array_push(render_objects, obj);
obj->rect_del = 1;
}
else if (evas_object_is_visible(obj))
{
RDI(level);
RD(" visible\n");
eina_array_push(render_objects, obj);
obj->render_pre = 1;
}
else
{
RDI(level);
RD(" skip\n");
}
}
}
/*
else if (obj->smart.smart)
{
RDI(level);
RD(" smart + mot visible/was visible\n");
eina_array_push(render_objects, obj);
obj->render_pre = 1;
EINA_INLIST_FOREACH
(evas_object_smart_members_get_direct(obj), obj2)
{
_evas_render_phase1_object_process(e, obj2,
active_objects,
restack_objects,
delete_objects,
render_objects,
restack, map
#ifdef REND_DGB
, level + 1
#endif
);
}
}
*/
}
if (!is_active) obj->restack = 0;
RDI(level);
RD(" ---]\n");
return clean_them;
}
static Eina_Bool
_evas_render_phase1_process(Evas *e,
Eina_Array *active_objects,
Eina_Array *restack_objects,
Eina_Array *delete_objects,
Eina_Array *render_objects)
{
Evas_Layer *lay;
Eina_Bool clean_them = EINA_FALSE;
RD(" [--- PHASE 1\n");
EINA_INLIST_FOREACH(e->layers, lay)
{
Evas_Object *obj;
EINA_INLIST_FOREACH(lay->objects, obj)
{
clean_them |= _evas_render_phase1_object_process
(e, obj, active_objects, restack_objects, delete_objects,
render_objects, 0, 0
#ifdef REND_DGB
, 1
#endif
);
}
}
RD(" ---]\n");
return clean_them;
}
static void
_evas_render_check_pending_objects(Eina_Array *pending_objects, Evas *e)
{
unsigned int i;
for (i = 0; i < pending_objects->count; ++i)
{
Evas_Object *obj;
int is_active, ok = 0;
obj = eina_array_data_get(pending_objects, i);
if (!obj->layer) goto clean_stuff;
evas_object_clip_recalc(obj);
is_active = evas_object_is_active(obj);
if ((!is_active) && (!obj->is_active) && (!obj->render_pre) &&
(!obj->rect_del))
{
ok = 1;
goto clean_stuff;
}
if (obj->is_active == is_active)
{
if (obj->changed)
{
if (obj->smart.smart)
{
if (obj->render_pre || obj->rect_del) ok = 1;
}
else
if ((is_active) && (obj->restack) && (!obj->clip.clipees) &&
(_evas_render_can_render(obj) ||
(evas_object_was_visible(obj) && (!obj->prev.have_clipees))))
{
if (!(obj->render_pre || obj->rect_del)) ok = 1;
}
else
if (is_active && (!obj->clip.clipees) &&
(_evas_render_can_render(obj) ||
(evas_object_was_visible(obj) && (!obj->prev.have_clipees))))
{
if (obj->render_pre || obj->rect_del) ok = 1;
}
}
else
{
if ((!obj->clip.clipees) && (obj->delete_me == 0) &&
(!obj->cur.have_clipees || (evas_object_was_visible(obj) && (!obj->prev.have_clipees)))
&& evas_object_is_opaque(obj) && evas_object_is_visible(obj))
{
if (obj->rect_del || obj->smart.smart) ok = 1;
}
}
}
clean_stuff:
if (!ok)
{
eina_array_clean(&e->active_objects);
eina_array_clean(&e->render_objects);
eina_array_clean(&e->restack_objects);
eina_array_clean(&e->delete_objects);
e->invalidate = 1;
return ;
}
}
}
Eina_Bool
pending_change(void *data, void *gdata __UNUSED__)
{
Evas_Object *obj;
obj = data;
if (obj->delete_me) return EINA_FALSE;
if (obj->pre_render_done)
{
RD(" OBJ [%p] pending change %i -> 0, pre %i\n", obj, obj->changed, obj->pre_render_done);
obj->pre_render_done = 0;
//// FIXME: this wipes out changes
obj->changed = 0;
}
return obj->changed ? EINA_TRUE : EINA_FALSE;
}
static Eina_Bool
evas_render_mapped(Evas *e, Evas_Object *obj, void *context, void *surface,
int off_x, int off_y, int mapped
#ifdef REND_DGB
, int level
#endif
)
{
void *ctx;
Evas_Object *obj2;
Eina_Bool clean_them = EINA_FALSE;
evas_object_clip_recalc(obj);
RDI(level);
RD(" { evas_render_mapped(%p, %p, %p, %p, %i, %i, %i, %i)\n", e, obj, context, surface, off_x, off_y, mapped, level);
if (mapped)
{
if ((!evas_object_is_visible(obj)) || (obj->clip.clipees) ||
(obj->cur.have_clipees))
{
RDI(level);
RD(" }\n");
return clean_them;
}
}
else if (!(((evas_object_is_active(obj) && (!obj->clip.clipees) &&
(_evas_render_can_render(obj))))
))
{
RDI(level);
RD(" }\n");
return clean_them;
}
// set render_pre - for child objs that may not have gotten it.
obj->pre_render_done = 1;
if (_evas_render_has_map(obj))
{
const Evas_Map_Point *p, *p_end;
RGBA_Map_Point pts[4], *pt;
int sw, sh;
int changed = 0, rendered = 0;
clean_them = EINA_TRUE;
sw = obj->cur.geometry.w;
sh = obj->cur.geometry.h;
RDI(level);
RD(" mapped obj: %ix%i\n", sw, sh);
if ((sw <= 0) || (sh <= 0))
{
RDI(level);
RD(" }\n");
return clean_them;
}
p = obj->cur.map->points;
p_end = p + 4;
pt = pts;
for (; p < p_end; p++, pt++)
{
pt->x = (p->x + off_x) << FP;
pt->y = (p->y + off_y) << FP;
pt->z = (p->z) << FP;
pt->u = p->u * FP1;
pt->v = p->v * FP1;
pt->col = ARGB_JOIN(p->a, p->r, p->g, p->b);
}
if (obj->cur.map->surface)
{
if ((obj->cur.map->surface_w != sw) ||
(obj->cur.map->surface_h != sh))
{
RDI(level);
RD(" new surf: %ix%i\n", sw, sh);
obj->layer->evas->engine.func->image_map_surface_free
(e->engine.data.output, obj->cur.map->surface);
obj->cur.map->surface = NULL;
}
}
if (!obj->cur.map->surface)
{
obj->cur.map->surface_w = sw;
obj->cur.map->surface_h = sh;
obj->cur.map->surface =
obj->layer->evas->engine.func->image_map_surface_new
(e->engine.data.output, obj->cur.map->surface_w,
obj->cur.map->surface_h,
obj->cur.map->alpha);
RDI(level);
RD(" fisrt surf: %ix%i\n", sw, sh);
changed = 1;
}
if (obj->smart.smart)
{
Evas_Object *obj2;
EINA_INLIST_FOREACH(evas_object_smart_members_get_direct(obj), obj2)
{
if (obj2->changed)
{
obj2->changed = 0;
changed = 1;
break;
}
}
obj->changed = 0;
}
else
{
if (obj->changed)
{
changed = 1;
obj->changed = 0;
}
}
// clear surface before re-render
if ((changed) && (obj->cur.map->surface))
{
RDI(level);
RD(" children redraw\n");
// FIXME: calculate "changes" within map surface and only clear
// and re-render those
if (obj->cur.map->alpha)
{
ctx = e->engine.func->context_new(e->engine.data.output);
e->engine.func->context_color_set
(e->engine.data.output, ctx, 0, 0, 0, 0);
e->engine.func->context_render_op_set
(e->engine.data.output, ctx, EVAS_RENDER_COPY);
e->engine.func->rectangle_draw(e->engine.data.output,
ctx,
obj->cur.map->surface,
0, 0,
obj->cur.map->surface_w,
obj->cur.map->surface_h);
e->engine.func->context_free(e->engine.data.output, ctx);
}
ctx = e->engine.func->context_new(e->engine.data.output);
off_x = -obj->cur.geometry.x;
off_y = -obj->cur.geometry.y;
if (obj->smart.smart)
{
EINA_INLIST_FOREACH
(evas_object_smart_members_get_direct(obj), obj2)
{
clean_them |= evas_render_mapped(e, obj2, ctx,
obj->cur.map->surface,
off_x, off_y, 1
#ifdef REND_DGB
, level + 1
#endif
);
}
}
else
{
int x = 0, y = 0, w = 0, h = 0;
w = obj->cur.map->surface_w;
h = obj->cur.map->surface_h;
RECTS_CLIP_TO_RECT(x, y, w, h,
obj->cur.geometry.x + off_x,
obj->cur.geometry.y + off_y,
obj->cur.geometry.w,
obj->cur.geometry.h);
e->engine.func->context_clip_set(e->engine.data.output,
ctx, x, y, w, h);
obj->func->render(obj, e->engine.data.output, ctx,
obj->cur.map->surface, off_x, off_y);
}
e->engine.func->context_free(e->engine.data.output, ctx);
rendered = 1;
}
RDI(level);
RD(" draw map4\n");
if (rendered)
{
obj->cur.map->surface = e->engine.func->image_dirty_region
(e->engine.data.output, obj->cur.map->surface,
0, 0, obj->cur.map->surface_w, obj->cur.map->surface_h);
}
obj->layer->evas->engine.func->image_map4_draw
(e->engine.data.output, e->engine.data.context, surface,
obj->cur.map->surface, pts, obj->cur.map->smooth, 0);
// FIXME: needs to cache these maps and
// keep them only rendering updates
// obj->layer->evas->engine.func->image_map_surface_free
// (e->engine.data.output, obj->cur.map->surface);
// obj->cur.map->surface = NULL;
}
else
{
if (mapped)
{
RDI(level);
RD(" draw child of mapped obj\n");
ctx = e->engine.func->context_new(e->engine.data.output);
if (obj->smart.smart)
{
EINA_INLIST_FOREACH
(evas_object_smart_members_get_direct(obj), obj2)
{
clean_them |= evas_render_mapped(e, obj2, ctx,
surface,
off_x, off_y, 1
#ifdef REND_DGB
, level + 1
#endif
);
}
}
else
obj->func->render(obj, e->engine.data.output, ctx,
surface, off_x, off_y);
e->engine.func->context_free(e->engine.data.output, ctx);
}
else
{
RDI(level);
RD(" draw normal obj\n");
obj->func->render(obj, e->engine.data.output, context, surface,
off_x, off_y);
}
}
RDI(level);
RD(" }\n");
return clean_them;
}
static Eina_List *
evas_render_updates_internal(Evas *e,
unsigned char make_updates,
unsigned char do_draw)
{
Eina_List *updates = NULL;
Eina_List *ll;
void *surface;
Eina_Bool clean_them = EINA_FALSE;
Eina_Bool alpha;
Eina_Rectangle *r;
int ux, uy, uw, uh;
int cx, cy, cw, ch;
unsigned int i, j;
int haveup = 0;
MAGIC_CHECK(e, Evas, MAGIC_EVAS);
return NULL;
MAGIC_CHECK_END();
if (!e->changed) return NULL;
evas_call_smarts_calculate(e);
RD("[--- RENDER EVAS (size: %ix%i)\n", e->viewport.w, e->viewport.h);
/* Check if the modified object mean recalculating every thing */
if (!e->invalidate)
_evas_render_check_pending_objects(&e->pending_objects, e);
/* phase 1. add extra updates for changed objects */
if (e->invalidate || e->render_objects.count <= 0)
clean_them = _evas_render_phase1_process(e, &e->active_objects, &e->restack_objects, &e->delete_objects, &e->render_objects);
_evas_render_phase1_direct(e, &e->active_objects, &e->restack_objects, &e->delete_objects, &e->render_objects);
/* phase 2. force updates for restacks */
for (i = 0; i < e->restack_objects.count; ++i)
{
Evas_Object *obj;
obj = eina_array_data_get(&e->restack_objects, i);
obj->func->render_pre(obj);
_evas_render_prev_cur_clip_cache_add(e, obj);
}
eina_array_clean(&e->restack_objects);
/* phase 3. add exposes */
EINA_LIST_FREE(e->damages, r)
{
e->engine.func->output_redraws_rect_add(e->engine.data.output,
r->x, r->y, r->w, r->h);
eina_rectangle_free(r);
}
/* phase 4. output & viewport changes */
if (e->viewport.changed)
{
e->engine.func->output_redraws_rect_add(e->engine.data.output,
0, 0,
e->output.w, e->output.h);
}
if (e->output.changed)
{
e->engine.func->output_resize(e->engine.data.output,
e->output.w, e->output.h);
e->engine.func->output_redraws_rect_add(e->engine.data.output,
0, 0,
e->output.w, e->output.h);
}
if ((e->output.w != e->viewport.w) || (e->output.h != e->viewport.h))
{
ERR("viewport size != output size!");
}
/* phase 5. add obscures */
EINA_LIST_FOREACH(e->obscures, ll, r)
{
e->engine.func->output_redraws_rect_del(e->engine.data.output,
r->x, r->y, r->w, r->h);
}
/* build obscure objects list of active objects that obscure */
for (i = 0; i < e->active_objects.count; ++i)
{
Evas_Object *obj;
obj = eina_array_data_get(&e->active_objects, i);
if (UNLIKELY((evas_object_is_opaque(obj) ||
((obj->func->has_opaque_rect) &&
(obj->func->has_opaque_rect(obj)))) &&
evas_object_is_visible(obj) &&
(!obj->clip.clipees) &&
(obj->cur.visible) &&
(!obj->delete_me) &&
(obj->cur.cache.clip.visible) &&
(!obj->smart.smart)))
/* obscuring_objects = eina_list_append(obscuring_objects, obj); */
eina_array_push(&e->obscuring_objects, obj);
}
/* save this list */
/* obscuring_objects_orig = obscuring_objects; */
/* obscuring_objects = NULL; */
/* phase 6. go thru each update rect and render objects in it*/
if (do_draw)
{
unsigned int offset = 0;
alpha = e->engine.func->canvas_alpha_get(e->engine.data.output, e->engine.data.context);
while ((surface =
e->engine.func->output_redraws_next_update_get(e->engine.data.output,
&ux, &uy, &uw, &uh,
&cx, &cy, &cw, &ch)))
{
int off_x, off_y;
RD(" [--- UPDATE %i %i %ix%i\n", ux, uy, uw, uh);
if (make_updates)
{
Eina_Rectangle *rect;
NEW_RECT(rect, ux, uy, uw, uh);
if (rect)
updates = eina_list_append(updates, rect);
}
haveup = 1;
off_x = cx - ux;
off_y = cy - uy;
/* build obscuring objects list (in order from bottom to top) */
for (i = 0; i < e->obscuring_objects.count; ++i)
{
Evas_Object *obj;
obj = (Evas_Object *) eina_array_data_get(&e->obscuring_objects, i);
if (evas_object_is_in_output_rect(obj, ux, uy, uw, uh))
{
eina_array_push(&e->temporary_objects, obj);
/* reset the background of the area if needed (using cutout and engine alpha flag to help) */
if (alpha)
{
if (evas_object_is_opaque(obj))
e->engine.func->context_cutout_add(e->engine.data.output,
e->engine.data.context,
obj->cur.cache.clip.x + off_x,
obj->cur.cache.clip.y + off_y,
obj->cur.cache.clip.w,
obj->cur.cache.clip.h);
else
{
if (obj->func->get_opaque_rect)
{
Evas_Coord obx, oby, obw, obh;
obj->func->get_opaque_rect(obj, &obx, &oby, &obw, &obh);
if ((obw > 0) && (obh > 0))
{
obx += off_x;
oby += off_y;
RECTS_CLIP_TO_RECT(obx, oby, obw, obh,
obj->cur.cache.clip.x + off_x,
obj->cur.cache.clip.y + off_y,
obj->cur.cache.clip.w,
obj->cur.cache.clip.h);
e->engine.func->context_cutout_add(e->engine.data.output,
e->engine.data.context,
obx, oby,
obw, obh);
}
}
}
}
}
}
if (alpha)
{
e->engine.func->context_clip_set(e->engine.data.output,
e->engine.data.context,
ux, uy, uw, uh);
e->engine.func->context_color_set(e->engine.data.output, e->engine.data.context, 0, 0, 0, 0);
e->engine.func->context_multiplier_unset(e->engine.data.output, e->engine.data.context);
e->engine.func->context_render_op_set(e->engine.data.output, e->engine.data.context, EVAS_RENDER_COPY);
e->engine.func->rectangle_draw(e->engine.data.output,
e->engine.data.context,
surface,
cx, cy, cw, ch);
e->engine.func->context_cutout_clear(e->engine.data.output,
e->engine.data.context);
e->engine.func->context_clip_unset(e->engine.data.output,
e->engine.data.context);
}
/* render all object that intersect with rect */
for (i = 0; i < e->active_objects.count; ++i)
{
Evas_Object *obj;
obj = eina_array_data_get(&e->active_objects, i);
/* if it's in our outpout rect and it doesn't clip anything */
RD(" OBJ: [%p] '%s' %i %i %ix%i\n", obj, obj->type, obj->cur.geometry.x, obj->cur.geometry.y, obj->cur.geometry.w, obj->cur.geometry.h);
if ((evas_object_is_in_output_rect(obj, ux, uy, uw, uh) ||
(obj->smart.smart)) &&
(!obj->clip.clipees) &&
(obj->cur.visible) &&
(!obj->delete_me) &&
(obj->cur.cache.clip.visible) &&
// (!obj->smart.smart) &&
(obj->cur.color.a > 0))
{
int x, y, w, h;
RD(" DRAW (vis: %i, a: %i, clipees: %p\n", obj->cur.visible, obj->cur.color.a, obj->clip.clipees);
if ((e->temporary_objects.count > offset) &&
(eina_array_data_get(&e->temporary_objects, offset) == obj))
offset++;
x = cx; y = cy; w = cw; h = ch;
if (obj->cur.clipper)
{
if (_evas_render_has_map(obj))
{
evas_object_clip_recalc(obj);
}
/* hmmmm clip seems to kill eweather in elm-test
printf("clip: %4i %4i %4ix%4i to %4i %4i %4ix%4i\n",
x, y, w, h,
obj->cur.cache.clip.x + off_x,
obj->cur.cache.clip.y + off_y,
obj->cur.cache.clip.w,
obj->cur.cache.clip.h);
*/
RECTS_CLIP_TO_RECT(x, y, w, h,
obj->cur.cache.clip.x + off_x,
obj->cur.cache.clip.y + off_y,
obj->cur.cache.clip.w,
obj->cur.cache.clip.h);
}
if (((w > 0) && (h > 0)) || (obj->smart.smart))
{
if (!obj->smart.smart)
{
RECTS_CLIP_TO_RECT(x, y, w, h,
obj->cur.cache.clip.x + off_x,
obj->cur.cache.clip.y + off_y,
obj->cur.cache.clip.w,
obj->cur.cache.clip.h);
}
e->engine.func->context_clip_set(e->engine.data.output,
e->engine.data.context,
x, y, w, h);
#if 1 /* FIXME: this can slow things down... figure out optimum... coverage */
for (j = offset; j < e->temporary_objects.count; ++j)
{
Evas_Object *obj2;
obj2 = (Evas_Object *) eina_array_data_get(&e->temporary_objects, j);
if (evas_object_is_opaque(obj2))
e->engine.func->context_cutout_add(e->engine.data.output,
e->engine.data.context,
obj2->cur.cache.clip.x + off_x,
obj2->cur.cache.clip.y + off_y,
obj2->cur.cache.clip.w,
obj2->cur.cache.clip.h);
else
{
if (obj2->func->get_opaque_rect)
{
Evas_Coord obx, oby, obw, obh;
obj2->func->get_opaque_rect
(obj2, &obx, &oby, &obw, &obh);
if ((obw > 0) && (obh > 0))
{
obx += off_x;
oby += off_y;
RECTS_CLIP_TO_RECT(obx, oby, obw, obh,
obj2->cur.cache.clip.x + off_x,
obj2->cur.cache.clip.y + off_y,
obj2->cur.cache.clip.w,
obj2->cur.cache.clip.h);
e->engine.func->context_cutout_add(e->engine.data.output,
e->engine.data.context,
obx, oby,
obw, obh);
}
}
}
}
#endif
clean_them |= evas_render_mapped(e, obj, e->engine.data.context,
surface, off_x, off_y, 0
#ifdef REND_DGB
, 1
#endif
);
e->engine.func->context_cutout_clear(e->engine.data.output,
e->engine.data.context);
}
}
}
/* punch rect out */
e->engine.func->output_redraws_next_update_push(e->engine.data.output,
surface,
ux, uy, uw, uh);
/* free obscuring objects list */
eina_array_clean(&e->temporary_objects);
RD(" ---]\n");
}
/* flush redraws */
if (haveup)
{
evas_event_callback_call(e, EVAS_CALLBACK_RENDER_FLUSH_PRE, NULL);
e->engine.func->output_flush(e->engine.data.output);
evas_event_callback_call(e, EVAS_CALLBACK_RENDER_FLUSH_POST, NULL);
}
}
/* clear redraws */
e->engine.func->output_redraws_clear(e->engine.data.output);
/* and do a post render pass */
for (i = 0; i < e->active_objects.count; ++i)
{
Evas_Object *obj;
obj = eina_array_data_get(&e->active_objects, i);
obj->pre_render_done = 0;
RD(" OBJ [%p] post... %i %i\n", obj, obj->changed, do_draw);
if ((obj->changed) && (do_draw))
{
RD(" OBJ [%p] post... func1\n", obj);
obj->func->render_post(obj);
obj->restack = 0;
obj->changed = 0;
}
else if ((obj->cur.map != obj->prev.map) ||
(obj->cur.usemap != obj->prev.usemap))
{
RD(" OBJ [%p] post... func2\n", obj);
obj->func->render_post(obj);
obj->restack = 0;
obj->changed = 0;
}
/* moved to other pre-process phase 1
if (obj->delete_me == 2)
{
delete_objects = eina_list_append(delete_objects, obj);
}
else if (obj->delete_me != 0) obj->delete_me++;
*/
}
/* free our obscuring object list */
eina_array_clean(&e->obscuring_objects);
/* If some object are still marked as changed, do not remove
them from the pending list. */
eina_array_remove(&e->pending_objects, pending_change, NULL);
/* delete all objects flagged for deletion now */
for (i = 0; i < e->delete_objects.count; ++i)
{
Evas_Object *obj;
obj = eina_array_data_get(&e->delete_objects, i);
evas_object_free(obj, 1);
}
eina_array_clean(&e->delete_objects);
e->changed = 0;
e->viewport.changed = 0;
e->output.changed = 0;
e->invalidate = 0;
for (i = 0; i < e->render_objects.count; ++i)
{
Evas_Object *obj;
obj = eina_array_data_get(&e->render_objects, i);
obj->pre_render_done = 0;
}
/* If their are some object to restack or some object to delete,
* it's useless to keep the render object list around. */
if (clean_them)
{
eina_array_clean(&e->active_objects);
eina_array_clean(&e->render_objects);
eina_array_clean(&e->restack_objects);
eina_array_clean(&e->delete_objects);
eina_array_clean(&e->obscuring_objects);
e->invalidate = 1;
}
evas_module_clean();
RD("---]\n");
return updates;
}
/**
* Free the rectangles returned by evas_render_updates().
*
* @param updates The list of updated rectangles of the canvas.
*
* This function removes the region from the render updates list. It
* makes the region doesn't be render updated anymore.
*
* @ingroup Evas_Canvas
*/
EAPI void
evas_render_updates_free(Eina_List *updates)
{
Eina_Rectangle *r;
EINA_LIST_FREE(updates, r)
eina_rectangle_free(r);
}
/**
* Force immediate renderization of the given canvas.
*
* @param e The given canvas pointer.
* @return A newly allocated list of updated rectangles of the canvas.
* Free this list with evas_render_updates_free().
*
* This function forces an immediate renderization update of the given
* given canvas.
*
* @ingroup Evas_Canvas
*/
EAPI Eina_List *
evas_render_updates(Evas *e)
{
MAGIC_CHECK(e, Evas, MAGIC_EVAS);
return NULL;
MAGIC_CHECK_END();
#ifdef EVAS_FRAME_QUEUING
evas_common_frameq_flush_ready ();
#endif
if (!e->changed) return NULL;
return evas_render_updates_internal(e, 1, 1);
}
/**
* Force renderization of the given canvas.
*
* @param e The given canvas pointer.
*
* @ingroup Evas_Canvas
*/
EAPI void
evas_render(Evas *e)
{
MAGIC_CHECK(e, Evas, MAGIC_EVAS);
return;
MAGIC_CHECK_END();
#ifdef EVAS_FRAME_QUEUING
evas_common_frameq_flush_ready ();
#endif
if (!e->changed) return;
evas_render_updates_internal(e, 0, 1);
}
/**
* Update the canvas internal objects but not triggering immediate
* renderization.
*
* @param e The given canvas pointer.
*
* This function updates the canvas internal objects not triggering
* renderization. To force renderization function evas_render() should
* be used.
*
* @see evas_render.
*
* @ingroup Evas_Canvas
*/
EAPI void
evas_norender(Evas *e)
{
MAGIC_CHECK(e, Evas, MAGIC_EVAS);
return;
MAGIC_CHECK_END();
// if (!e->changed) return;
evas_render_updates_internal(e, 0, 0);
}
/**
* Make the canvas discard internally cached data used for rendering.
*
* @param e The given canvas pointer.
*
* This function flushes the arrays of delete, active and render objects.
* Other things it may also discard are: shared memory segments,
* temporary scratch buffers, cached data to avoid re-compute of that data etc.
*
* @ingroup Evas_Canvas
*/
EAPI void
evas_render_idle_flush(Evas *e)
{
MAGIC_CHECK(e, Evas, MAGIC_EVAS);
return;
MAGIC_CHECK_END();
evas_fonts_zero_presure(e);
if ((e->engine.func) && (e->engine.func->output_idle_flush) &&
(e->engine.data.output))
e->engine.func->output_idle_flush(e->engine.data.output);
eina_array_flush(&e->delete_objects);
eina_array_flush(&e->active_objects);
eina_array_flush(&e->restack_objects);
eina_array_flush(&e->render_objects);
eina_array_flush(&e->clip_changes);
e->invalidate = 1;
}
EAPI void
evas_sync(Evas *e)
{
#ifdef EVAS_FRAME_QUEUING
MAGIC_CHECK(e, Evas, MAGIC_EVAS);
return;
MAGIC_CHECK_END();
evas_common_frameq_flush ();
#endif
}
/**
* Make the canvas discard as much data as possible used by the engine at
* runtime.
*
* @param e The given canvas pointer.
*
* This function will unload images, delete textures and much more, where
* possible. You may also want to call evas_render_idle_flush() immediately
* prior to this to perhaps discard a little more, though evas_render_dump()
* should implicitly delete most of what evas_render_idle_flush() might
* discard too.
*
* @ingroup Evas_Canvas
*/
static void
_evas_render_dump_map_surfaces(Evas_Object *obj)
{
if ((obj->cur.map) && obj->cur.map->surface)
{
obj->layer->evas->engine.func->image_map_surface_free
(obj->layer->evas->engine.data.output, obj->cur.map->surface);
obj->cur.map->surface = NULL;
}
if (obj->smart.smart)
{
Evas_Object *obj2;
EINA_INLIST_FOREACH(evas_object_smart_members_get_direct(obj), obj2)
_evas_render_dump_map_surfaces(obj2);
}
}
EAPI void
evas_render_dump(Evas *e)
{
Evas_Layer *lay;
MAGIC_CHECK(e, Evas, MAGIC_EVAS);
return;
MAGIC_CHECK_END();
EINA_INLIST_FOREACH(e->layers, lay)
{
Evas_Object *obj;
EINA_INLIST_FOREACH(lay->objects, obj)
_evas_render_dump_map_surfaces(obj);
}
if ((e->engine.func) && (e->engine.func->output_dump) &&
(e->engine.data.output))
e->engine.func->output_dump(e->engine.data.output);
}
void
evas_render_invalidate(Evas *e)
{
MAGIC_CHECK(e, Evas, MAGIC_EVAS);
return;
MAGIC_CHECK_END();
eina_array_clean(&e->active_objects);
eina_array_clean(&e->render_objects);
eina_array_flush(&e->restack_objects);
eina_array_flush(&e->delete_objects);
e->invalidate = 1;
}
void
evas_render_object_recalc(Evas_Object *obj)
{
MAGIC_CHECK(obj, Evas_Object, MAGIC_OBJ);
return;
MAGIC_CHECK_END();
#ifndef EVAS_FRAME_QUEUING
if ((!obj->changed) && (obj->delete_me < 2))
#else
if ((!obj->changed) )
#endif
{
Evas *e;
e = obj->layer->evas;
if ((!e) || (e->cleanup)) return;
#ifdef EVAS_FRAME_QUEUING
if (obj->delete_me >= evas_common_frameq_get_frameq_sz() + 2) return;
#endif
eina_array_push(&e->pending_objects, obj);
obj->changed = 1;
}
}