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

1259 lines
31 KiB
C
Raw Normal View History

#include "evas_common.h"
#include "evas_private.h"
#include <math.h>
static void
_evas_map_calc_geom_change(Evas_Object *eo_obj)
{
int is, was = 0;
Evas_Object_Protected_Data *obj = eo_data_get(eo_obj, EVAS_OBJ_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(eo_obj, obj);
if (!obj->is_smart)
{
is = evas_object_is_in_output_rect(eo_obj, obj,
obj->layer->evas->pointer.x,
obj->layer->evas->pointer.y, 1, 1);
if ((is ^ was) && obj->cur.visible)
evas_event_feed_mouse_move(obj->layer->evas->evas,
obj->layer->evas->pointer.x,
obj->layer->evas->pointer.y,
obj->layer->evas->last_timestamp,
NULL);
}
}
evas_object_inform_call_move(eo_obj, obj);
evas_object_inform_call_resize(eo_obj);
}
static 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 = eo_data_get(eo_obj, EVAS_OBJ_CLASS);
if (!obj) return;
if (!obj->cur.map) return;
// WARN: Do not merge below code to SLP until it is fixed.
// It has an infinite loop bug.
if (obj->prev.map)
{
if (obj->prev.map != obj->cur.map)
{
// FIXME: this causes an infinite loop somewhere... hard to debug
if (obj->prev.map->count == obj->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->cur.map->points;
p2 = obj->prev.map->points;
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
if (memcmp(p, p2, sizeof(Evas_Map_Point) *
obj->prev.map->count) != 0)
ch = EINA_TRUE;
if (!ch)
{
if (obj->cache_map) evas_map_free(obj->cache_map);
obj->cache_map = obj->cur.map;
obj->cur.map = obj->prev.map;
}
}
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->cur.map->points;
p_end = p + obj->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;
2010-01-25 03:04:32 -08:00
if (obj->cur.map->normal_geometry.x != x1) ch = 1;
if (obj->cur.map->normal_geometry.y != yy1) ch = 1;
2010-01-25 03:04:32 -08:00
if (obj->cur.map->normal_geometry.w != (x2 - x1)) ch = 1;
if (obj->cur.map->normal_geometry.h != (yy2 - yy1)) ch = 1;
obj->cur.map->normal_geometry.x = x1;
obj->cur.map->normal_geometry.y = yy1;
obj->cur.map->normal_geometry.w = (x2 - x1);
obj->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 inline Evas_Map *
_evas_map_new(int count)
{
int i;
int alloc;
Evas_Map *m;
/* 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;
m->count = count;
m->persp.foc = 0;
m->alpha = 1;
m->smooth = 1;
m->magic = MAGIC_MAP;
for (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;
}
return m;
}
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->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);
if (!copy) return NULL;
memcpy(copy->points, orig->points, orig->count * sizeof(Evas_Map_Point));
copy->smooth = orig->smooth;
copy->alpha = orig->alpha;
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 = eo_data_get(eo_obj, EVAS_OBJ_CLASS);
if ((obj) && (obj->spans))
{
obj->layer->evas->engine.func->image_map_clean(obj->layer->evas->engine.data.output, obj->spans);
free(obj->spans);
obj->spans = NULL;
}
}
m->magic = 0;
free(m);
}
/****************************************************************************/
/* util functions for manipulating maps, so you don't need to know the math */
/****************************************************************************/
static inline void
_evas_map_util_points_populate(Evas_Map *m, const Evas_Coord x, const Evas_Coord y, const Evas_Coord w, const Evas_Coord h, const Evas_Coord z)
{
Evas_Map_Point *p = m->points;
int i;
p[0].x = x;
p[0].y = y;
p[0].z = z;
p[0].u = 0.0;
p[0].v = 0.0;
p[1].x = x + w;
p[1].y = y;
p[1].z = z;
p[1].u = w;
p[1].v = 0.0;
p[2].x = x + w;
p[2].y = y + h;
p[2].z = z;
p[2].u = w;
p[2].v = h;
p[3].x = x;
p[3].y = y + h;
p[3].z = z;
p[3].u = 0.0;
p[3].v = h;
for (i = 0; i < 4; i++)
{
p[i].px = p[i].x;
p[i].py = p[i].y;
}
}
Eina_Bool
evas_map_coords_get(const Evas_Map *m, Evas_Coord x, Evas_Coord y,
Evas_Coord *mx, Evas_Coord *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;
Evas_Coord xe[2];
double u[2] = { 0.0, 0.0 };
double v[2] = { 0.0, 0.0 };
/*
if (grab)
{
Evas_Coord 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)
{
Evas_Coord 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;
}
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 = eo_data_get(eo_parent, EVAS_OBJ_CLASS);
if (!parent) return EINA_FALSE;
list = evas_object_smart_members_get_direct(parent->smart.parent);
EINA_INLIST_FOREACH(list, o)
if (o->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;
}
EAPI void
evas_object_map_enable_set(Evas_Object *eo_obj, Eina_Bool enabled)
{
MAGIC_CHECK(eo_obj, Evas_Object, MAGIC_OBJ);
return;
MAGIC_CHECK_END();
eo_do(eo_obj, evas_obj_map_enable_set(enabled));
}
void
_map_enable_set(Eo *eo_obj, void *_pd, va_list *list)
{
Eina_Bool enabled = va_arg(*list, int);
Evas_Object_Protected_Data *obj = _pd;
Eina_Bool pchange = EINA_FALSE;
enabled = !!enabled;
if (obj->cur.usemap == enabled) return;
pchange = obj->changed;
obj->cur.usemap = enabled;
if (enabled)
{
if (!obj->cur.map)
obj->cur.map = _evas_map_new(4);
evas_object_mapped_clip_across_mark(eo_obj, obj);
// obj->cur.map->normal_geometry = obj->cur.geometry;
}
else
{
2012-09-07 01:02:32 -07:00
if (obj->map.surface)
{
obj->layer->evas->engine.func->image_map_surface_free
(obj->layer->evas->engine.data.output,
obj->map.surface);
obj->map.surface = NULL;
}
if (obj->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 = eo_data_get(eo_parents, EVAS_OBJ_CLASS);
if (parents)
parents->child_has_map = EINA_TRUE;
}
}
else
{
if (_evas_object_map_parent_check(obj->smart.parent))
evas_object_update_bounding_box(eo_obj, obj);
}
}
EAPI Eina_Bool
evas_object_map_enable_get(const Evas_Object *eo_obj)
{
MAGIC_CHECK(eo_obj, Evas_Object, MAGIC_OBJ);
return EINA_FALSE;
MAGIC_CHECK_END();
Eina_Bool enabled = EINA_FALSE;
eo_do((Eo *)eo_obj, evas_obj_map_enable_get(&enabled));
return enabled;
}
void
_map_enable_get(Eo *eo_obj EINA_UNUSED, void *_pd, va_list *list)
{
Eina_Bool *enabled = va_arg(*list, Eina_Bool *);
const Evas_Object_Protected_Data *obj = _pd;
*enabled = obj->cur.usemap;
}
EAPI void
evas_object_map_set(Evas_Object *eo_obj, const Evas_Map *map)
{
MAGIC_CHECK(eo_obj, Evas_Object, MAGIC_OBJ);
return;
MAGIC_CHECK_END();
eo_do(eo_obj, evas_obj_map_set(map));
}
void
_map_set(Eo *eo_obj, void *_pd, va_list *list)
{
const Evas_Map *map = va_arg(*list, const Evas_Map *);
Evas_Object_Protected_Data *obj = _pd;
2012-09-07 01:02:32 -07:00
if ((!map) || (map->count < 4))
{
2012-09-07 01:02:32 -07:00
if (obj->map.surface)
{
obj->layer->evas->engine.func->image_map_surface_free
(obj->layer->evas->engine.data.output,
obj->map.surface);
obj->map.surface = NULL;
}
if (obj->cur.map)
{
obj->changed_map = EINA_TRUE;
obj->prev.geometry = obj->cur.map->normal_geometry;
if (obj->prev.map == obj->cur.map)
obj->cur.map = NULL;
else if (!obj->cache_map)
{
obj->cache_map = obj->cur.map;
obj->cur.map = NULL;
}
else
{
_evas_map_free(eo_obj, obj->cur.map);
obj->cur.map = NULL;
}
if (!obj->prev.map)
{
evas_object_mapped_clip_across_mark(eo_obj, obj);
return;
}
if (!obj->cur.usemap) _evas_map_calc_geom_change(eo_obj);
else _evas_map_calc_map_geometry(eo_obj);
if (obj->cur.usemap)
evas_object_mapped_clip_across_mark(eo_obj, obj);
}
return;
}
if (obj->prev.map == obj->cur.map)
obj->cur.map = NULL;
if (!obj->cur.map)
{
obj->cur.map = obj->cache_map;
obj->cache_map = NULL;
}
// We do have the same exact count of point in this map, so just copy it
if ((obj->cur.map) && (obj->cur.map->count == map->count))
_evas_map_copy(obj->cur.map, map);
else
{
if (obj->cur.map) _evas_map_free(eo_obj, obj->cur.map);
obj->cur.map = _evas_map_dup(map);
if (obj->cur.usemap)
evas_object_mapped_clip_across_mark(eo_obj, obj);
}
_evas_map_calc_map_geometry(eo_obj);
}
EAPI const Evas_Map *
evas_object_map_get(const Evas_Object *eo_obj)
{
MAGIC_CHECK(eo_obj, Evas_Object, MAGIC_OBJ);
return NULL;
MAGIC_CHECK_END();
const Evas_Map *map = NULL;
eo_do((Eo *)eo_obj, evas_obj_map_get(&map));
return map;
}
void
_map_get(Eo *eo_obj EINA_UNUSED, void *_pd, va_list *list)
{
const Evas_Map **map = va_arg(*list, const Evas_Map **);
const Evas_Object_Protected_Data *obj = _pd;
*map = obj->cur.map;
}
EAPI Evas_Map *
evas_map_new(int count)
{
if (count != 4)
{
ERR("map point count (%i) != 4 is unsupported!", count);
return NULL;
}
return _evas_map_new(count);
}
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 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;
}
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();
Evas_Map_Point *p;
if (idx >= m->count) return;
p = m->points + idx;
p->x = p->px = x;
p->y = p->py = y;
p->z = z;
}
EAPI void
evas_map_point_coord_get(const Evas_Map *m, int idx, Evas_Coord *x, Evas_Coord *y, Evas_Coord *z)
{
MAGIC_CHECK(m, Evas_Map, MAGIC_MAP);
goto error;
MAGIC_CHECK_END();
const Evas_Map_Point *p;
if (idx >= m->count) goto error;
p = m->points + idx;
if (x) *x = p->x;
if (y) *y = p->y;
if (z) *z = p->z;
return;
error:
if (x) *x = 0;
if (y) *y = 0;
if (z) *z = 0;
}
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 >= 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 >= 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 >= 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 >= m->count) return;
p = m->points + idx;
if (r) *r = p->r;
if (g) *g = p->g;
if (b) *b = p->b;
if (a) *a = p->a;
}
EAPI void
evas_map_util_points_populate_from_object_full(Evas_Map *m, const Evas_Object *eo_obj, Evas_Coord z)
{
MAGIC_CHECK(eo_obj, Evas_Object, MAGIC_OBJ);
return;
MAGIC_CHECK_END();
Evas_Object_Protected_Data *obj = eo_data_get(eo_obj, EVAS_OBJ_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 = eo_data_get(eo_obj, EVAS_OBJ_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, 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;
}
}
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();
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_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();
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 = (((double)x) * zoomx);
y = (((double)y) * zoomy);
p->px = p->x = x + cx;
p->py = p->y = y + cy;
}
}
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();
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 (rz != 0.0)
{
xx = x * cos(rz);
yy = x * sin(rz);
x = xx - (y * sin(rz));
y = yy + (y * cos(rz));
}
if (ry != 0.0)
{
xx = x * cos(ry);
zz = x * sin(ry);
x = xx - (z * sin(ry));
z = zz + (z * cos(ry));
}
if (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_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();
Evas_Map_Point *p, *p_end;
p = m->points;
p_end = p + m->count;
for (; p < p_end; p++)
{
double x, y, z, uvx, uvy, uvz, uuvx, uuvy, uuvz;
x = p->x - cx;
y = p->y - cy;
z = p->z - cz;
uvx = qy * z - qz * y;
uvy = qz * x - qx * z;
uvz = qx * y - qy * x;
uuvx = qy * uvz - qz * uvy;
uuvy = qz * uvx - qx * uvz;
uuvz = qx * uvy - qy * uvx;
uvx *= (2.0f * qw);
uvy *= (2.0f * qw);
uvz *= (2.0f * qw);
uuvx *= 2.0f;
uuvy *= 2.0f;
uuvz *= 2.0f;
p->px = p->x = cx + x + uvx + uuvx;
p->py = p->y = cy + y + uvy + uuvy;
p->z = cz + z + uvz + uuvz;
}
}
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();
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 (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 (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_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();
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 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;
}
void
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 = eo_data_get(eo_obj, EVAS_OBJ_CLASS);
const Evas_Map_Point *p, *p_end;
RGBA_Map_Point *pts, *pt;
if (!obj) return;
if (obj->spans)
{
if (obj->spans->x != x || obj->spans->y != y ||
obj->spans->image.w != imagew || obj->spans->image.h != imageh ||
obj->spans->uv.w != uvw || obj->spans->uv.h != uvh)
obj->changed_map = EINA_TRUE;
}
else
{
obj->changed_map = EINA_TRUE;
}
if (!obj->changed_map) return ;
if (obj->cur.map && obj->spans && obj->cur.map->count != obj->spans->count)
{
if (obj->spans)
{
// Destroy engine side spans
free(obj->spans);
}
obj->spans = NULL;
}
if (!obj->spans)
obj->spans = calloc(1, sizeof (RGBA_Map) +
sizeof (RGBA_Map_Point) * (obj->cur.map->count - 1));
if (!obj->spans) return ;
obj->spans->count = obj->cur.map->count;
obj->spans->x = x;
obj->spans->y = y;
obj->spans->uv.w = uvw;
obj->spans->uv.h = uvh;
obj->spans->image.w = imagew;
obj->spans->image.h = imageh;
pts = obj->spans->pts;
p = obj->cur.map->points;
p_end = p + obj->cur.map->count;
pt = pts;
pts[0].px = obj->cur.map->persp.px << FP;
pts[0].py = obj->cur.map->persp.py << FP;
pts[0].foc = obj->cur.map->persp.foc << FP;
pts[0].z0 = obj->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;
pt->fx = p->px;
pt->fy = p->py;
pt->fz = p->z;
pt->u = ((lround(p->u) * imagew) / uvw) * FP1;
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->cur.map->count & 0x1)
{
pts[obj->cur.map->count] = pts[obj->cur.map->count -1];
}
// Request engine to update it's point
}