efl/src/lib/evas/canvas/evas_map.c

1424 lines
38 KiB
C
Raw Normal View History

#include "evas_map.h"
static void
_evas_map_calc_geom_change(Evas_Object *eo_obj)
{
Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
if (!obj) return;
evas_object_change(eo_obj, obj);
evas_object_clip_dirty(eo_obj, obj);
if (!(obj->layer->evas->is_frozen))
{
evas_object_recalc_clippees(obj);
if (!obj->is_smart && obj->cur->visible)
{
_evas_canvas_event_pointer_in_list_mouse_move_feed(obj->layer->evas, NULL, eo_obj, obj, 1, 1,
EINA_TRUE, NULL);
}
}
evas_object_inform_call_move(eo_obj, obj);
evas_object_inform_call_resize(eo_obj);
}
void
_evas_map_calc_map_geometry(Evas_Object *eo_obj)
{
Evas_Coord x1, x2, yy1, yy2;
const Evas_Map_Point *p, *p_end;
Eina_Bool ch = EINA_FALSE;
Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
if (!obj) return;
if (!obj->map->cur.map) return;
if (obj->map->prev.map)
{
if (obj->map->prev.map != obj->map->cur.map)
{
// FIXME: this causes an infinite loop somewhere... hard to debug
if (obj->map->prev.map->count == obj->map->cur.map->count)
{
const Evas_Map_Point *p2;
finally found evas_map weirdness bug. CEDRIC code...! commit #74180. now here's the rub. from the glibc manual page: ... int memcmp(const void *s1, const void *s2, size_t n); DESCRIPTION The memcmp() function compares the first n bytes (each interpreted as unsigned char) of the memory areas s1 and s2. It returns an integer less than, equal to, or greater than zero if s1 is found, respectively, to be less than, to match, or be greater than s2. RETURN VALUE The memcmp() function returns an integer less than, equal to, or greater than zero if the first n bytes of s1 is found, respectively, to be less than, to match, or be greater than the first n bytes of s2. ... this explicitly says that s1 and s2 have their BYTES compared... and then returns just some value < 0, 0 or > 0 based on the difference. what that value is and means is not defined, as long as it is < 0, 0 or > 0. so the C standard has this to say: 6.3.1.3 Signed and unsigned integers 2. Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type. so a result of -255 (possible) is converted by REPEATEDLY adding the max size of the new type (255) until within range... so ... -255 + 255 = 0 ... within range.. BUT FALSE! so why do we see this now? something changed in memcpy behavior. before we ONLY saw values of -1, 0 or 1 - nothing else, NOW we see thins like: -12288 49152 4096 16384 61440 -53248 so memcpy changed behavior, though within specs of the manual page anyway, but now the values can be ones that break the simple assignment. SVN revision: 75159
2012-08-11 21:17:42 -07:00
p = obj->map->cur.map->points;
p2 = obj->map->prev.map->points;
if (memcmp(p, p2, sizeof(Evas_Map_Point) *
obj->map->prev.map->count) != 0)
finally found evas_map weirdness bug. CEDRIC code...! commit #74180. now here's the rub. from the glibc manual page: ... int memcmp(const void *s1, const void *s2, size_t n); DESCRIPTION The memcmp() function compares the first n bytes (each interpreted as unsigned char) of the memory areas s1 and s2. It returns an integer less than, equal to, or greater than zero if s1 is found, respectively, to be less than, to match, or be greater than s2. RETURN VALUE The memcmp() function returns an integer less than, equal to, or greater than zero if the first n bytes of s1 is found, respectively, to be less than, to match, or be greater than the first n bytes of s2. ... this explicitly says that s1 and s2 have their BYTES compared... and then returns just some value < 0, 0 or > 0 based on the difference. what that value is and means is not defined, as long as it is < 0, 0 or > 0. so the C standard has this to say: 6.3.1.3 Signed and unsigned integers 2. Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type. so a result of -255 (possible) is converted by REPEATEDLY adding the max size of the new type (255) until within range... so ... -255 + 255 = 0 ... within range.. BUT FALSE! so why do we see this now? something changed in memcpy behavior. before we ONLY saw values of -1, 0 or 1 - nothing else, NOW we see thins like: -12288 49152 4096 16384 61440 -53248 so memcpy changed behavior, though within specs of the manual page anyway, but now the values can be ones that break the simple assignment. SVN revision: 75159
2012-08-11 21:17:42 -07:00
ch = EINA_TRUE;
if (!ch)
{
EINA_COW_WRITE_BEGIN(evas_object_map_cow, obj->map, Evas_Object_Map_Data, map_write)
{
if (map_write->cache_map) evas_map_free(map_write->cache_map);
map_write->cache_map = map_write->cur.map;
map_write->cur.map = map_write->prev.map;
}
EINA_COW_WRITE_END(evas_object_map_cow, obj->map, map_write);
}
}
else
2012-08-11 21:18:44 -07:00
ch = EINA_TRUE;
}
}
else
2012-08-11 21:18:44 -07:00
ch = EINA_TRUE;
p = obj->map->cur.map->points;
p_end = p + obj->map->cur.map->count;
x1 = x2 = lround(p->x);
yy1 = yy2 = lround(p->y);
p++;
for (; p < p_end; p++)
{
Evas_Coord x, y;
x = lround(p->x);
y = lround(p->y);
if (x < x1) x1 = x;
if (x > x2) x2 = x;
if (y < yy1) yy1 = y;
if (y > yy2) yy2 = y;
}
// this causes clip-out bugs now mapped objs canbe opaque!!!
// // add 1 pixel of fuzz around the map region to ensure updates are correct
// x1 -= 1; yy1 -= 1;
// x2 += 1; yy2 += 1;
if (obj->map->cur.map->normal_geometry.x != x1) ch = 1;
if (obj->map->cur.map->normal_geometry.y != yy1) ch = 1;
if (obj->map->cur.map->normal_geometry.w != (x2 - x1)) ch = 1;
if (obj->map->cur.map->normal_geometry.h != (yy2 - yy1)) ch = 1;
obj->map->cur.map->normal_geometry.x = x1;
obj->map->cur.map->normal_geometry.y = yy1;
obj->map->cur.map->normal_geometry.w = (x2 - x1);
obj->map->cur.map->normal_geometry.h = (yy2 - yy1);
obj->changed_map = ch;
// This shouldn't really be needed, but without it we do have case
// where the clip is wrong when a map doesn't change, so always forcing
// it, as long as someone doesn't find a better fix.
evas_object_clip_dirty(eo_obj, obj);
if (ch) _evas_map_calc_geom_change(eo_obj);
}
static void
evas_object_map_move_sync(Evas_Object *eo_obj)
{
Evas_Object_Protected_Data *obj;
Evas_Map *m;
Evas_Map_Point *p;
Evas_Coord diff_x, diff_y;
int i, count;
obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
if (!obj) return;
if ((!obj->map->cur.map->move_sync.enabled) ||
((obj->map->cur.map->move_sync.diff_x == 0) &&
(obj->map->cur.map->move_sync.diff_y == 0)))
return;
m = obj->map->cur.map;
p = m->points;
count = m->count;
diff_x = m->move_sync.diff_x;
diff_y = m->move_sync.diff_y;
for (i = 0; i < count; i++, p++)
{
p->px += diff_x;
p->py += diff_y;
p->x += diff_x;
p->y += diff_y;
}
m->move_sync.diff_x = 0;
m->move_sync.diff_y = 0;
_evas_map_calc_map_geometry(eo_obj);
}
static void
_evas_map_init(Evas_Map *m, int count, Eina_Bool sync)
{
m->move_sync.enabled = sync;
m->count = count;
m->alpha = 1;
m->smooth = 1;
m->magic = MAGIC_MAP;
for (int i = 0; i < count; i++)
{
m->points[i].r = 255;
m->points[i].g = 255;
m->points[i].b = 255;
m->points[i].a = 255;
}
}
Evas_Map *
_evas_map_new(int count, Eina_Bool sync)
{
Evas_Map *m;
int alloc;
/* Adjust allocation such that: at least 4 points, and always an even
* number: this allows the software engine to work efficiently */
alloc = (count < 4) ? 4 : count;
if (alloc & 0x1) alloc ++;
m = calloc(1, sizeof(Evas_Map) + (alloc * sizeof(Evas_Map_Point)));
if (!m) return NULL;
_evas_map_init(m, count, sync);
return m;
}
void
_evas_map_reset(Evas_Map *m)
{
int alloc, count;
Eina_Bool sync;
if (!m) return;
/* Adjust allocation such that: at least 4 points, and always an even
* number: this allows the software engine to work efficiently */
alloc = (m->count < 4) ? 4 : m->count;
if (alloc & 0x1) alloc ++;
count = m->count;
sync = m->move_sync.enabled;
memset(m, 0, sizeof(Evas_Map) + (alloc * sizeof(Evas_Map_Point)));
_evas_map_init(m, count, sync);
}
static inline Eina_Bool
_evas_map_copy(Evas_Map *dst, const Evas_Map *src)
{
if (dst->count != src->count)
{
ERR("cannot copy map of different sizes: dst=%i, src=%i", dst->count, src->count);
return EINA_FALSE;
}
if (dst == src) return EINA_TRUE;
if (dst->points != src->points)
memcpy(dst->points, src->points, src->count * sizeof(Evas_Map_Point));
dst->smooth = src->smooth;
dst->alpha = src->alpha;
dst->move_sync = src->move_sync;
dst->persp = src->persp;
return EINA_TRUE;
}
static inline Evas_Map *
_evas_map_dup(const Evas_Map *orig)
{
Evas_Map *copy = _evas_map_new(orig->count, EINA_FALSE);
if (!copy) return NULL;
memcpy(copy->points, orig->points, orig->count * sizeof(Evas_Map_Point));
copy->smooth = orig->smooth;
copy->alpha = orig->alpha;
copy->move_sync = orig->move_sync;
copy->persp = orig->persp;
return copy;
}
static inline void
_evas_map_free(Evas_Object *eo_obj, Evas_Map *m)
{
if (eo_obj)
{
Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
if ((obj) && (obj->map->spans))
{
obj->layer->evas->engine.func->image_map_clean(ENC, obj->map->spans);
EINA_COW_WRITE_BEGIN(evas_object_map_cow, obj->map, Evas_Object_Map_Data, map_write)
{
free(map_write->spans);
map_write->spans = NULL;
}
EINA_COW_WRITE_END(evas_object_map_cow, obj->map, map_write);
}
}
m->magic = 0;
free(m);
}
2017-06-28 14:42:28 -07:00
EAPI Eina_Bool
evas_map_coords_get(const Evas_Map *m, double x, double y,
double *mx, double *my, int grab)
{
MAGIC_CHECK(m, Evas_Map, MAGIC_MAP);
return EINA_FALSE;
MAGIC_CHECK_END();
if (m->count < 4) return EINA_FALSE;
Eina_Bool inside = evas_map_inside_get(m, x, y);
if ((!mx) && (!my)) return inside;
// FIXME: need to handle grab mode and extrapolate coords outside map
if (grab && !inside) return EINA_FALSE;
int i, j, edges, edge[m->count][2];
Eina_Bool douv = EINA_FALSE;
double xe[2];
double u[2] = { 0.0, 0.0 };
double v[2] = { 0.0, 0.0 };
/*
if (grab)
{
double ymin, ymax;
ymin = m->points[0].y;
ymax = m->points[0].y;
for (i = 1; i < m->count; i++)
{
if (m->points[i].y < ymin) ymin = m->points[i].y;
else if (m->points[i].y > ymax) ymax = m->points[i].y;
}
if (y <= ymin) y = ymin + 1;
if (y >= ymax) y = ymax - 1;
}
*/
edges = EINA_FALSE;
for (i = 0; i < m->count; i++)
{
j = (i + 1) % m->count;
if ((m->points[i].y <= y) && (m->points[j].y > y))
{
edge[edges][0] = i;
edge[edges][1] = j;
edges++;
}
else if ((m->points[j].y <= y) && (m->points[i].y > y))
{
edge[edges][0] = j;
edge[edges][1] = i;
edges++;
}
}
if ((mx) || (my)) douv = EINA_TRUE;
for (i = 0; i < (edges - 1); i+= 2)
{
double yp, yd;
j = i + 1;
yd = m->points[edge[i][1]].y - m->points[edge[i][0]].y;
if (yd > 0)
{
yp = y - m->points[edge[i][0]].y;
xe[0] = m->points[edge[i][1]].x - m->points[edge[i][0]].x;
xe[0] = m->points[edge[i][0]].x + ((xe[0] * yp) / yd);
if (douv)
{
u[0] = m->points[edge[i][1]].u - m->points[edge[i][0]].u;
u[0] = m->points[edge[i][0]].u + ((u[0] * yp) / yd);
v[0] = m->points[edge[i][1]].v - m->points[edge[i][0]].v;
v[0] = m->points[edge[i][0]].v + ((v[0] * yp) / yd);
}
}
else
{
xe[0] = m->points[edge[i][0]].x;
if (douv)
{
u[0] = m->points[edge[i][0]].u;
v[0] = m->points[edge[i][0]].v;
}
}
yd = m->points[edge[j][1]].y - m->points[edge[j][0]].y;
if (yd > 0)
{
yp = y - m->points[edge[j][0]].y;
xe[1] = m->points[edge[j][1]].x - m->points[edge[j][0]].x;
xe[1] = m->points[edge[j][0]].x + ((xe[1] * yp) / yd);
if (douv)
{
u[1] = m->points[edge[j][1]].u - m->points[edge[j][0]].u;
u[1] = m->points[edge[j][0]].u + ((u[1] * yp) / yd);
v[1] = m->points[edge[j][1]].v - m->points[edge[j][0]].v;
v[1] = m->points[edge[j][0]].v + ((v[1] * yp) / yd);
}
}
else
{
xe[1] = m->points[edge[j][0]].x;
if (douv)
{
u[1] = m->points[edge[j][0]].u;
v[1] = m->points[edge[j][0]].v;
}
}
if (xe[0] > xe[1])
{
int ti;
ti = xe[0]; xe[0] = xe[1]; xe[1] = ti;
if (douv)
{
double td;
td = u[0]; u[0] = u[1]; u[1] = td;
td = v[0]; v[0] = v[1]; v[1] = td;
}
}
if ((x >= xe[0]) && (x < xe[1]))
{
if (douv)
{
if (mx)
*mx = u[0] + (((x - xe[0]) * (u[1] - u[0])) /
(xe[1] - xe[0]));
if (my)
*my = v[0] + (((x - xe[0]) * (v[1] - v[0])) /
(xe[1] - xe[0]));
}
return EINA_TRUE;
}
/*
if (grab)
{
if (douv)
{
if (mx)
*mx = u[0] + (((x - xe[0]) * (u[1] - u[0])) /
(xe[1] - xe[0]));
if (my)
*my = v[0] + (((x - xe[0]) * (v[1] - v[0])) /
(xe[1] - xe[0]));
}
return EINA_TRUE;
}
*/
}
return EINA_FALSE;
}
Eina_Bool
evas_map_inside_get(const Evas_Map *m, Evas_Coord x, Evas_Coord y)
{
int i = 0, j = m->count - 1;
double pt1_x, pt1_y, pt2_x, pt2_y, tmp_x;
Eina_Bool inside = EINA_FALSE;
//Check the point inside the map coords by using Jordan curve theorem.
for (i = 0; i < m->count; i++)
{
pt1_x = m->points[i].x;
pt1_y = m->points[i].y;
pt2_x = m->points[j].x;
pt2_y = m->points[j].y;
//Is the point inside the map on y axis?
if (((y >= pt1_y) && (y < pt2_y)) || ((y >= pt2_y) && (y < pt1_y)))
{
//Check the point is left side of the line segment.
tmp_x = (pt1_x + ((pt2_x - pt1_x) / (pt2_y - pt1_y)) *
((double)y - pt1_y));
if ((double)x < tmp_x) inside = !inside;
}
j = i;
}
return inside;
}
#if 0
static Eina_Bool
_evas_object_map_parent_check(Evas_Object *eo_parent)
{
const Eina_Inlist *list;
const Evas_Object_Protected_Data *o;
if (!eo_parent) return EINA_FALSE;
Evas_Object_Protected_Data *parent = efl_data_scope_get(eo_parent, EFL_CANVAS_OBJECT_CLASS);
if (!parent) return EINA_FALSE;
list = evas_object_smart_members_get_direct(parent->smart.parent);
EINA_INLIST_FOREACH(list, o)
if (o->map->cur.usemap) break;
if (o) return EINA_FALSE; /* Still some child have a map enable */
parent->child_has_map = EINA_FALSE;
_evas_object_map_parent_check(parent->smart.parent);
return EINA_TRUE;
}
#endif
void
_evas_object_map_enable_set(Eo *eo_obj, Evas_Object_Protected_Data *obj,
Eina_Bool enabled)
{
Eina_Bool pchange = EINA_FALSE;
enabled = !!enabled;
if (obj->map->cur.usemap == enabled) return;
pchange = obj->changed;
evas_object_async_block(obj);
EINA_COW_WRITE_BEGIN(evas_object_map_cow, obj->map, Evas_Object_Map_Data, map_write)
map_write->cur.usemap = enabled;
EINA_COW_WRITE_END(evas_object_map_cow, obj->map, map_write);
if (enabled)
{
if (!obj->map->cur.map)
{
EINA_COW_WRITE_BEGIN(evas_object_map_cow, obj->map, Evas_Object_Map_Data, map_write)
map_write->cur.map = _evas_map_new(4, EINA_FALSE);
EINA_COW_WRITE_END(evas_object_map_cow, obj->map, map_write);
}
evas_object_mapped_clip_across_mark(eo_obj, obj);
// obj->map->cur.map->normal_geometry = obj->cur->geometry;
}
else
{
if (obj->map->surface)
2012-09-07 01:02:32 -07:00
{
EINA_COW_WRITE_BEGIN(evas_object_map_cow, obj->map, Evas_Object_Map_Data, map_write)
{
obj->layer->evas->engine.func->image_free(ENC, map_write->surface);
map_write->surface = NULL;
}
EINA_COW_WRITE_END(evas_object_map_cow, obj->map, map_write);
2012-09-07 01:02:32 -07:00
}
if (obj->map->cur.map)
{
_evas_map_calc_geom_change(eo_obj);
evas_object_mapped_clip_across_mark(eo_obj, obj);
}
}
_evas_map_calc_map_geometry(eo_obj);
/* This is a bit heavy handed, but it fixes the case of same geometry, but
* changed colour or UV settings. */
evas_object_change(eo_obj, obj);
if (!obj->changed_pchange) obj->changed_pchange = pchange;
obj->changed_map = EINA_TRUE;
if (enabled)
{
Evas_Object *eo_parents;
Evas_Object_Protected_Data *parents = NULL;
for (eo_parents = obj->smart.parent; eo_parents; eo_parents = parents->smart.parent)
{
parents = efl_data_scope_get(eo_parents, EFL_CANVAS_OBJECT_CLASS);
if (!parents) break;
parents->child_has_map = EINA_TRUE;
}
evas_object_update_bounding_box(eo_obj, obj, NULL);
}
else
{
evas_object_update_bounding_box(eo_obj, obj, NULL);
}
}
EAPI void
evas_object_map_enable_set(Eo *eo_obj, Eina_Bool enabled)
{
Evas_Object_Protected_Data *obj = EVAS_OBJ_GET_OR_RETURN(eo_obj);
_evas_object_map_enable_set(eo_obj, obj, enabled);
}
EAPI Eina_Bool
evas_object_map_enable_get(const Eo *eo_obj)
{
Evas_Object_Protected_Data *obj = EVAS_OBJ_GET_OR_RETURN(eo_obj, EINA_FALSE);
return obj->map->cur.usemap;
}
EAPI void
evas_object_map_set(Evas_Object *eo_obj, const Evas_Map *map)
{
Evas_Object_Protected_Data *obj = EVAS_OBJ_GET_OR_RETURN(eo_obj);
evas_object_async_block(obj);
// check if the new map and current map attributes are same
if (map && obj->map->cur.map &&
(obj->map->cur.map->alpha == map->alpha) &&
(obj->map->cur.map->smooth == map->smooth) &&
(obj->map->cur.map->move_sync.enabled == map->move_sync.enabled) &&
(obj->map->cur.map->move_sync.diff_x == map->move_sync.diff_x) &&
(obj->map->cur.map->move_sync.diff_y == map->move_sync.diff_y) &&
(obj->map->cur.map->count == map->count))
{
const Evas_Map_Point *p1, *p2;
p1 = obj->map->cur.map->points;
p2 = map->points;
if (!memcmp(p1, p2, sizeof(Evas_Map_Point) * map->count) &&
!memcmp(&map->persp, &obj->map->cur.map->persp, sizeof(map->persp)))
return;
}
/* changed_pchange means map's change.
* This flag will be used to decide whether to redraw the map surface.
* And value of flag would be EINA_FALSE after rendering. */
obj->changed_pchange = EINA_TRUE;
2012-09-07 01:02:32 -07:00
if ((!map) || (map->count < 4))
{
if (obj->map->surface)
2012-09-07 01:02:32 -07:00
{
EINA_COW_WRITE_BEGIN(evas_object_map_cow, obj->map, Evas_Object_Map_Data, map_write)
{
obj->layer->evas->engine.func->image_free(ENC, map_write->surface);
map_write->surface = NULL;
}
EINA_COW_WRITE_END(evas_object_map_cow, obj->map, map_write);
2012-09-07 01:02:32 -07:00
}
if (obj->map->cur.map)
{
obj->changed_map = EINA_TRUE;
EINA_COW_STATE_WRITE_BEGIN(obj, state_write, prev)
{
state_write->geometry = obj->map->cur.map->normal_geometry;
}
EINA_COW_STATE_WRITE_END(obj, state_write, prev);
EINA_COW_WRITE_BEGIN(evas_object_map_cow, obj->map, Evas_Object_Map_Data, map_write)
{
if (map_write->prev.map == map_write->cur.map)
map_write->cur.map = NULL;
else if (!map_write->cache_map)
{
map_write->cache_map = map_write->cur.map;
map_write->cur.map = NULL;
}
else
{
_evas_map_free(eo_obj, map_write->cur.map);
map_write->cur.map = NULL;
}
}
EINA_COW_WRITE_END(evas_object_map_cow, obj->map, map_write);
if (!obj->map->prev.map)
{
evas_object_update_bounding_box(eo_obj, obj, NULL);
evas_object_mapped_clip_across_mark(eo_obj, obj);
return;
}
if (!obj->map->cur.usemap) _evas_map_calc_geom_change(eo_obj);
else _evas_map_calc_map_geometry(eo_obj);
if (obj->map->cur.usemap)
evas_object_mapped_clip_across_mark(eo_obj, obj);
}
evas_object_update_bounding_box(eo_obj, obj, NULL);
return;
}
if (obj->map->prev.map != NULL &&
obj->map->prev.map == obj->map->cur.map)
{
EINA_COW_WRITE_BEGIN(evas_object_map_cow, obj->map, Evas_Object_Map_Data, map_write)
map_write->cur.map = NULL;
EINA_COW_WRITE_END(evas_object_map_cow, obj->map, map_write);
}
if (!obj->map->cur.map && obj->map->cache_map)
{
EINA_COW_WRITE_BEGIN(evas_object_map_cow, obj->map, Evas_Object_Map_Data, map_write)
{
map_write->cur.map = map_write->cache_map;
map_write->cache_map = NULL;
}
EINA_COW_WRITE_END(evas_object_map_cow, obj->map, map_write);
}
// We do have the same exact count of point in this map, so just copy it
if ((obj->map->cur.map) && (obj->map->cur.map->count == map->count))
_evas_map_copy(obj->map->cur.map, map);
else
{
if (obj->map->cur.map) _evas_map_free(eo_obj, obj->map->cur.map);
EINA_COW_WRITE_BEGIN(evas_object_map_cow, obj->map, Evas_Object_Map_Data, map_write)
map_write->cur.map = _evas_map_dup(map);
EINA_COW_WRITE_END(evas_object_map_cow, obj->map, map_write);
if (obj->map->cur.usemap)
evas_object_mapped_clip_across_mark(eo_obj, obj);
}
evas_object_update_bounding_box(eo_obj, obj, NULL);
_evas_map_calc_map_geometry(eo_obj);
}
EAPI const Evas_Map *
evas_object_map_get(const Evas_Object *eo_obj)
{
Evas_Object_Protected_Data *obj = EVAS_OBJ_GET_OR_RETURN((Eo *) eo_obj, NULL);
evas_object_async_block(obj);
return obj->map->cur.map;
}
EAPI Evas_Map *
evas_map_new(int count)
{
if ((count <= 0) || (count % 4 != 0))
{
ERR("map point count (%i) should be multiples of 4!", count);
return NULL;
}
return _evas_map_new(count, EINA_FALSE);
}
EAPI void
evas_map_smooth_set(Evas_Map *m, Eina_Bool enabled)
{
MAGIC_CHECK(m, Evas_Map, MAGIC_MAP);
return;
MAGIC_CHECK_END();
m->smooth = enabled;
}
EAPI Eina_Bool
evas_map_smooth_get(const Evas_Map *m)
{
MAGIC_CHECK(m, Evas_Map, MAGIC_MAP);
return EINA_FALSE;
MAGIC_CHECK_END();
return m->smooth;
}
EAPI void
evas_map_alpha_set(Evas_Map *m, Eina_Bool enabled)
{
MAGIC_CHECK(m, Evas_Map, MAGIC_MAP);
return;
MAGIC_CHECK_END();
m->alpha = enabled;
}
EAPI Eina_Bool
evas_map_alpha_get(const Evas_Map *m)
{
MAGIC_CHECK(m, Evas_Map, MAGIC_MAP);
return EINA_FALSE;
MAGIC_CHECK_END();
return m->alpha;
}
EAPI void
evas_map_util_object_move_sync_set(Evas_Map *m, Eina_Bool enabled)
{
MAGIC_CHECK(m, Evas_Map, MAGIC_MAP);
return;
MAGIC_CHECK_END();
if (!enabled)
{
m->move_sync.diff_x = 0;
m->move_sync.diff_y = 0;
}
m->move_sync.enabled = !!enabled;
}
EAPI Eina_Bool
evas_map_util_object_move_sync_get(const Evas_Map *m)
{
MAGIC_CHECK(m, Evas_Map, MAGIC_MAP);
return EINA_FALSE;
MAGIC_CHECK_END();
return m->move_sync.enabled;
}
EAPI Evas_Map *
evas_map_dup(const Evas_Map *m)
{
MAGIC_CHECK(m, Evas_Map, MAGIC_MAP);
return NULL;
MAGIC_CHECK_END();
return _evas_map_dup(m);
}
EAPI void
evas_map_free(Evas_Map *m)
{
MAGIC_CHECK(m, Evas_Map, MAGIC_MAP);
return;
MAGIC_CHECK_END();
_evas_map_free(NULL, m);
}
EAPI int
evas_map_count_get(const Evas_Map *m)
{
MAGIC_CHECK(m, Evas_Map, MAGIC_MAP);
return -1;
MAGIC_CHECK_END();
return m->count;
}
/* FIXME: coordinates should be float/double for accuracy.
Rotation center position will be flickered by rounding problem.
Now fixed in EO APIs.
*/
EAPI void
evas_map_point_coord_set(Evas_Map *m, int idx, Evas_Coord x, Evas_Coord y, Evas_Coord z)
{
MAGIC_CHECK(m, Evas_Map, MAGIC_MAP);
return;
MAGIC_CHECK_END();
_map_point_coord_set(m, idx, x, y, z);
}
EAPI void
evas_map_point_coord_get(const Evas_Map *m, int idx, Evas_Coord *x, Evas_Coord *y, Evas_Coord *z)
{
double dx, dy, dz;
_map_point_coord_get(m, idx, &dx, &dy, &dz);
if (x) *x = lround(dx);
if (y) *y = lround(dy);
if (z) *z = lround(dz);
}
EAPI void
evas_map_point_image_uv_set(Evas_Map *m, int idx, double u, double v)
{
MAGIC_CHECK(m, Evas_Map, MAGIC_MAP);
return;
MAGIC_CHECK_END();
Evas_Map_Point *p;
if ((idx < 0) || (idx >= m->count)) return;
p = m->points + idx;
p->u = u;
p->v = v;
}
EAPI void
evas_map_point_image_uv_get(const Evas_Map *m, int idx, double *u, double *v)
{
MAGIC_CHECK(m, Evas_Map, MAGIC_MAP);
goto error;
MAGIC_CHECK_END();
const Evas_Map_Point *p;
if ((idx < 0) || (idx >= m->count)) goto error;
p = m->points + idx;
if (u) *u = p->u;
if (v) *v = p->v;
return;
error:
if (u) *u = 0.0;
if (v) *v = 0.0;
}
EAPI void
evas_map_point_color_set(Evas_Map *m, int idx, int r, int g, int b, int a)
{
MAGIC_CHECK(m, Evas_Map, MAGIC_MAP);
return;
MAGIC_CHECK_END();
Evas_Map_Point *p;
if ((idx < 0) || (idx >= m->count)) return;
p = m->points + idx;
p->r = r;
p->g = g;
p->b = b;
p->a = a;
}
EAPI void
evas_map_point_color_get(const Evas_Map *m, int idx, int *r, int *g, int *b, int *a)
{
MAGIC_CHECK(m, Evas_Map, MAGIC_MAP);
return;
MAGIC_CHECK_END();
const Evas_Map_Point *p;
if ((idx < 0) || (idx >= m->count)) goto error;
p = m->points + idx;
if (r) *r = p->r;
if (g) *g = p->g;
if (b) *b = p->b;
if (a) *a = p->a;
error:
if (r) *r = 255;
if (g) *g = 255;
if (b) *b = 255;
if (a) *a = 255;
}
EAPI void
evas_map_util_points_populate_from_object_full(Evas_Map *m, const Evas_Object *eo_obj, Evas_Coord z)
{
2014-06-22 02:08:56 -07:00
MAGIC_CHECK(m, Evas_Map, MAGIC_MAP);
return;
MAGIC_CHECK_END();
MAGIC_CHECK(eo_obj, Evas_Object, MAGIC_OBJ);
return;
MAGIC_CHECK_END();
Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
if (!obj) return;
if (m->count != 4)
{
ERR("map has count=%d where 4 was expected.", m->count);
return;
}
_evas_map_util_points_populate(m, obj->cur->geometry.x, obj->cur->geometry.y,
obj->cur->geometry.w, obj->cur->geometry.h, z);
}
EAPI void
evas_map_util_points_populate_from_object(Evas_Map *m, const Evas_Object *eo_obj)
{
MAGIC_CHECK(m, Evas_Map, MAGIC_MAP);
return;
MAGIC_CHECK_END();
MAGIC_CHECK(eo_obj, Evas_Object, MAGIC_OBJ);
return;
MAGIC_CHECK_END();
Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
2014-06-22 02:08:56 -07:00
if (!obj) return;
if (m->count != 4)
{
ERR("map has count=%d where 4 was expected.", m->count);
return;
}
_evas_map_util_points_populate(m, obj->cur->geometry.x, obj->cur->geometry.y,
obj->cur->geometry.w, obj->cur->geometry.h, 0);
}
EAPI void
evas_map_util_points_populate_from_geometry(Evas_Map *m, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h, Evas_Coord z)
{
MAGIC_CHECK(m, Evas_Map, MAGIC_MAP);
return;
MAGIC_CHECK_END();
if (m->count != 4)
{
ERR("map has count=%d where 4 was expected.", m->count);
return;
}
_evas_map_util_points_populate(m, x, y, w, h, z);
}
EAPI void
evas_map_util_points_color_set(Evas_Map *m, int r, int g, int b, int a)
{
MAGIC_CHECK(m, Evas_Map, MAGIC_MAP);
return;
MAGIC_CHECK_END();
Evas_Map_Point *p, *p_end;
p = m->points;
p_end = p + m->count;
for (; p < p_end; p++)
{
p->r = r;
p->g = g;
p->b = b;
p->a = a;
}
}
void
_map_util_rotate(Evas_Map *m, double degrees, double cx, double cy)
{
double r = (degrees * M_PI) / 180.0;
Evas_Map_Point *p, *p_end;
p = m->points;
p_end = p + m->count;
for (; p < p_end; p++)
{
double x, y, xx, yy;
x = p->x - cx;
y = p->y - cy;
xx = x * cos(r);
yy = x * sin(r);
x = xx - (y * sin(r));
y = yy + (y * cos(r));
p->px = p->x = x + cx;
p->py = p->y = y + cy;
}
}
EAPI void
evas_map_util_rotate(Evas_Map *m, double degrees, Evas_Coord cx, Evas_Coord cy)
{
MAGIC_CHECK(m, Evas_Map, MAGIC_MAP);
return;
MAGIC_CHECK_END();
_map_util_rotate(m, degrees, (double) cx, (double) cy);
}
void
_map_util_zoom(Evas_Map *m, double zoomx, double zoomy, double cx, double cy)
{
Evas_Map_Point *p, *p_end;
p = m->points;
p_end = p + m->count;
for (; p < p_end; p++)
{
double x, y;
x = p->x - cx;
y = p->y - cy;
x *= zoomx;
y *= zoomy;
p->px = p->x = x + cx;
p->py = p->y = y + cy;
}
}
EAPI void
evas_map_util_zoom(Evas_Map *m, double zoomx, double zoomy, Evas_Coord cx, Evas_Coord cy)
{
MAGIC_CHECK(m, Evas_Map, MAGIC_MAP);
return;
MAGIC_CHECK_END();
_map_util_zoom(m, zoomx, zoomy, (double) cx, (double) cy);
}
void
_map_util_translate(Evas_Map *m, double dx, double dy, double dz)
{
Evas_Map_Point *p, *p_end;
p = m->points;
p_end = p + m->count;
for (; p < p_end; p++)
{
p->px = (p->x += dx);
p->py = (p->y += dy);
p->z += dz;
}
}
void
_map_util_3d_rotate(Evas_Map *m, double dx, double dy, double dz,
double cx, double cy, double cz)
{
double rz = (dz * M_PI) / 180.0;
double rx = (dx * M_PI) / 180.0;
double ry = (dy * M_PI) / 180.0;
Evas_Map_Point *p, *p_end;
p = m->points;
p_end = p + m->count;
for (; p < p_end; p++)
{
double x, y, z, xx, yy, zz;
x = p->x - cx;
y = p->y - cy;
z = p->z - cz;
if (!EINA_DBL_EQ(rz, 0.0))
{
xx = x * cos(rz);
yy = x * sin(rz);
x = xx - (y * sin(rz));
y = yy + (y * cos(rz));
}
if (!EINA_DBL_EQ(ry, 0.0))
{
xx = x * cos(ry);
zz = x * sin(ry);
x = xx - (z * sin(ry));
z = zz + (z * cos(ry));
}
if (!EINA_DBL_EQ(rx, 0.0))
{
zz = z * cos(rx);
yy = z * sin(rx);
z = zz - (y * sin(rx));
y = yy + (y * cos(rx));
}
p->px = p->x = x + cx;
p->py = p->y = y + cy;
p->z = z + cz;
}
}
EAPI void
evas_map_util_3d_rotate(Evas_Map *m, double dx, double dy, double dz,
Evas_Coord cx, Evas_Coord cy, Evas_Coord cz)
{
MAGIC_CHECK(m, Evas_Map, MAGIC_MAP);
return;
MAGIC_CHECK_END();
_map_util_3d_rotate(m, dx, dy, dz, (double) cx, (double) cy, (double) cz);
}
void
_map_util_quat_rotate(Evas_Map *m, double qx, double qy, double qz,
double qw, double cx, double cy, double cz)
{
Eina_Quaternion q;
Eina_Point_3D c;
MAGIC_CHECK(m, Evas_Map, MAGIC_MAP);
return;
MAGIC_CHECK_END();
Evas_Map_Point *p, *p_end;
p = m->points;
p_end = p + m->count;
q.x = qx;
q.y = qy;
q.z = qz;
q.w = qw;
c.x = cx;
c.y = cy;
c.z = cz;
for (; p < p_end; p++)
{
Eina_Point_3D current;
current.x = p->x;
current.y = p->y;
current.z = p->z;
eina_quaternion_rotate(&current, &c, &q);
p->px = p->x = current.x;
p->py = p->y = current.y;
p->z = current.z;
}
}
EAPI void
evas_map_util_quat_rotate(Evas_Map *m, double qx, double qy, double qz,
double qw, double cx, double cy, double cz)
{
MAGIC_CHECK(m, Evas_Map, MAGIC_MAP);
return;
MAGIC_CHECK_END();
_map_util_quat_rotate(m, qx, qy, qz, qw, cx, cy, cz);
}
void
_map_util_3d_lighting(Evas_Map *m,
double lx, double ly, double lz,
int lr, int lg, int lb, int ar, int ag, int ab)
{
int i;
for (i = 0; i < m->count; i++)
{
double x, y, z;
double nx, ny, nz, x1, yy1, z1, x2, yy2, z2, ln, br;
int h, j, mr, mg, mb;
x = m->points[i].x;
y = m->points[i].y;
z = m->points[i].z;
// calc normal
h = (i - 1 + 4) % 4 + (i & ~0x3); // prev point
j = (i + 1) % 4 + (i & ~0x3); // next point
x1 = m->points[h].x - x;
yy1 = m->points[h].y - y;
z1 = m->points[h].z - z;
x2 = m->points[j].x - x;
yy2 = m->points[j].y - y;
z2 = m->points[j].z - z;
nx = (yy1 * z2) - (z1 * yy2);
ny = (z1 * x2) - (x1 * z2);
nz = (x1 * yy2) - (yy1 * x2);
ln = (nx * nx) + (ny * ny) + (nz * nz);
ln = sqrt(ln);
if (!EINA_DBL_EQ(ln, 0.0))
{
nx /= ln;
ny /= ln;
nz /= ln;
}
// calc point -> light vector
x = lx - x;
y = ly - y;
z = lz - z;
ln = (x * x) + (y * y) + (z * z);
ln = sqrt(ln);
if (!EINA_DBL_EQ(ln, 0.0))
{
x /= ln;
y /= ln;
z /= ln;
}
// brightness - tan (0.0 -> 1.0 brightness really)
br = (nx * x) + (ny * y) + (nz * z);
if (br < 0.0) br = 0.0;
mr = ar + ((lr - ar) * br);
mg = ag + ((lg - ag) * br);
mb = ab + ((lb - ab) * br);
if (m->points[i].a != 255)
{
mr = (mr * m->points[i].a) / 255;
mg = (mg * m->points[i].a) / 255;
mb = (mb * m->points[i].a) / 255;
}
m->points[i].r = (m->points[i].r * mr) / 255;
m->points[i].g = (m->points[i].g * mg) / 255;
m->points[i].b = (m->points[i].b * mb) / 255;
}
}
EAPI void
evas_map_util_3d_lighting(Evas_Map *m,
Evas_Coord lx, Evas_Coord ly, Evas_Coord lz,
int lr, int lg, int lb, int ar, int ag, int ab)
{
MAGIC_CHECK(m, Evas_Map, MAGIC_MAP);
return;
MAGIC_CHECK_END();
_map_util_3d_lighting(m, (double) lx, (double) ly, (double)
lz, lr, lg, lb, ar, ag, ab);
}
void
_map_util_3d_perspective(Evas_Map *m, double px, double py, double z0, double foc)
{
Evas_Map_Point *p, *p_end;
p = m->points;
p_end = p + m->count;
m->persp.px = px;
m->persp.py = py;
m->persp.z0 = z0;
m->persp.foc = foc;
if (foc <= 0) return;
for (; p < p_end; p++)
{
double x, y, zz;
x = p->x - px;
y = p->y - py;
zz = ((p->z - z0) + foc);
if (zz > 0)
{
x = (x * foc) / zz;
y = (y * foc) / zz;
}
p->x = px + x;
p->y = py + y;
}
}
EAPI void
evas_map_util_3d_perspective(Evas_Map *m,
Evas_Coord px, Evas_Coord py,
Evas_Coord z0, Evas_Coord foc)
{
MAGIC_CHECK(m, Evas_Map, MAGIC_MAP);
return;
MAGIC_CHECK_END();
_map_util_3d_perspective(m, (double) px, (double) py, (double) z0, (double) foc);
}
EAPI Eina_Bool
evas_map_util_clockwise_get(Evas_Map *m)
{
MAGIC_CHECK(m, Evas_Map, MAGIC_MAP);
return EINA_FALSE;
MAGIC_CHECK_END();
int i, j, k, count;
long long c;
if (m->count < 3) return EINA_FALSE;
count = 0;
for (i = 0; i < m->count; i++)
{
j = (i + 1) % m->count;
k = (i + 2) % m->count;
c =
((m->points[j].x - m->points[i].x) *
(m->points[k].y - m->points[j].y))
-
((m->points[j].y - m->points[i].y) *
(m->points[k].x - m->points[j].x));
if (c < 0) count--;
else if (c > 0) count++;
}
if (count > 0) return EINA_TRUE;
return EINA_FALSE;
}
/****************************************************************************/
/* If the return value is true, the map surface should be redrawn. */
/****************************************************************************/
Eina_Bool
evas_object_map_update(Evas_Object *eo_obj,
int x, int y,
int imagew, int imageh,
int uvw, int uvh)
{
Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
const Evas_Map_Point *p, *p_end;
RGBA_Map_Point *pts, *pt;
if (!obj) return EINA_FALSE;
if (obj->map->spans)
{
if (obj->map->spans->x != x || obj->map->spans->y != y ||
obj->map->spans->image.w != imagew || obj->map->spans->image.h != imageh ||
obj->map->spans->uv.w != uvw || obj->map->spans->uv.h != uvh)
obj->changed_map = EINA_TRUE;
}
else
{
obj->changed_map = EINA_TRUE;
}
evas_object_map_move_sync(eo_obj);
if (!obj->changed_map) return EINA_FALSE;
if (obj->map->spans && obj->map->cur.map->count != obj->map->spans->count)
{
EINA_COW_WRITE_BEGIN(evas_object_map_cow, obj->map, Evas_Object_Map_Data, map_write)
{
if (map_write->spans)
{
// Destroy engine side spans
free(map_write->spans);
}
map_write->spans = NULL;
}
EINA_COW_WRITE_END(evas_object_map_cow, obj->map, map_write);
}
if (!obj->map->spans)
{
EINA_COW_WRITE_BEGIN(evas_object_map_cow, obj->map, Evas_Object_Map_Data, map_write)
map_write->spans = calloc(1, sizeof (RGBA_Map) +
sizeof (RGBA_Map_Point) * (map_write->cur.map->count - 1));
EINA_COW_WRITE_END(evas_object_map_cow, obj->map, map_write);
}
if (!obj->map->spans) return EINA_FALSE;
EINA_COW_WRITE_BEGIN(evas_object_map_cow, obj->map, Evas_Object_Map_Data, map_write)
{
map_write->spans->count = obj->map->cur.map->count;
map_write->spans->x = x;
map_write->spans->y = y;
map_write->spans->uv.w = uvw;
map_write->spans->uv.h = uvh;
map_write->spans->image.w = imagew;
map_write->spans->image.h = imageh;
pts = obj->map->spans->pts;
p = obj->map->cur.map->points;
p_end = p + obj->map->cur.map->count;
pt = pts;
}
EINA_COW_WRITE_END(evas_object_map_cow, obj->map, map_write);
pts[0].px = obj->map->cur.map->persp.px << FP;
pts[0].py = obj->map->cur.map->persp.py << FP;
pts[0].foc = obj->map->cur.map->persp.foc << FP;
pts[0].z0 = obj->map->cur.map->persp.z0 << FP;
// draw geom +x +y
for (; p < p_end; p++, pt++)
{
pt->x = (lround(p->x) + x) * FP1;
pt->y = (lround(p->y) + y) * FP1;
pt->z = (lround(p->z) ) * FP1;
/* FIXME: Adding the framespace is a workaround for a bug on the EGL
* wayland backend, which does not affect other ports. Remove this when
* it is correctly handled inside the GL rendering code, which handles
* maps with perspective set. */
pt->fx = p->px + obj->layer->evas->framespace.x;
pt->fy = p->py + obj->layer->evas->framespace.y;
pt->fz = p->z;
if ((uvw == 0) || (imagew == 0)) pt->u = 0;
else pt->u = ((lround(p->u) * imagew) / uvw) * FP1;
if ((uvh == 0) || (imageh == 0)) pt->v = 0;
else pt->v = ((lround(p->v) * imageh) / uvh) * FP1;
if (pt->u < 0) pt->u = 0;
else if (pt->u > (imagew * FP1)) pt->u = (imagew * FP1);
if (pt->v < 0) pt->v = 0;
else if (pt->v > (imageh * FP1)) pt->v = (imageh * FP1);
pt->col = ARGB_JOIN(p->a, p->r, p->g, p->b);
}
if (obj->map->cur.map->count & 0x1)
{
pts[obj->map->cur.map->count] = pts[obj->map->cur.map->count -1];
}
// Request engine to update it's point
obj->changed_map = EINA_FALSE;
return obj->changed_pchange;
}
void
evas_map_object_move_diff_set(Evas_Map *m,
Evas_Coord diff_x,
Evas_Coord diff_y)
{
MAGIC_CHECK(m, Evas_Map, MAGIC_MAP);
return;
MAGIC_CHECK_END();
m->move_sync.diff_x += diff_x;
m->move_sync.diff_y += diff_y;
}