evas/map - fixed afterimage problem. now we don't need work-around code anymore.
[framework/uifw/evas.git] / src / lib / canvas / evas_object_polygon.c
1 #include "evas_common.h"
2 #include "evas_private.h"
3
4 /* private magic number for polygon objects */
5 static const char o_type[] = "polygon";
6
7 /* private struct for line object internal data */
8 typedef struct _Evas_Object_Polygon      Evas_Object_Polygon;
9 typedef struct _Evas_Polygon_Point       Evas_Polygon_Point;
10
11 struct _Evas_Object_Polygon
12 {
13    DATA32               magic;
14    Eina_List           *points;
15    void                *engine_data;
16    struct {
17       int x, y;
18    } offset;
19    Evas_Coord_Rectangle geometry;
20    char                 changed : 1;
21 };
22
23 struct _Evas_Polygon_Point
24 {
25    Evas_Coord x, y;
26 };
27
28 /* private methods for polygon objects */
29 static void evas_object_polygon_init(Evas_Object *obj);
30 static void *evas_object_polygon_new(void);
31 static void evas_object_polygon_render(Evas_Object *obj, void *output, void *context, void *surface, int x, int y);
32 static void evas_object_polygon_free(Evas_Object *obj);
33 static void evas_object_polygon_render_pre(Evas_Object *obj);
34 static void evas_object_polygon_render_post(Evas_Object *obj);
35
36 static unsigned int evas_object_polygon_id_get(Evas_Object *obj);
37 static unsigned int evas_object_polygon_visual_id_get(Evas_Object *obj);
38 static void *evas_object_polygon_engine_data_get(Evas_Object *obj);
39
40 static int evas_object_polygon_is_opaque(Evas_Object *obj);
41 static int evas_object_polygon_was_opaque(Evas_Object *obj);
42 static int evas_object_polygon_is_inside(Evas_Object *obj, Evas_Coord x, Evas_Coord y);
43 static int evas_object_polygon_was_inside(Evas_Object *obj, Evas_Coord x, Evas_Coord y);
44
45 static const Evas_Object_Func object_func =
46 {
47    /* methods (compulsory) */
48    evas_object_polygon_free,
49      evas_object_polygon_render,
50      evas_object_polygon_render_pre,
51      evas_object_polygon_render_post,
52      evas_object_polygon_id_get,
53      evas_object_polygon_visual_id_get,
54      evas_object_polygon_engine_data_get,
55    /* these are optional. NULL = nothing */
56      NULL,
57      NULL,
58      NULL,
59      NULL,
60      evas_object_polygon_is_opaque,
61      evas_object_polygon_was_opaque,
62      evas_object_polygon_is_inside,
63      evas_object_polygon_was_inside,
64      NULL,
65      NULL,
66      NULL,
67      NULL,
68      NULL
69 };
70
71 /* the actual api call to add a rect */
72 /* it has no other api calls as all properties are standard */
73
74 EVAS_MEMPOOL(_mp_obj);
75
76 EAPI Evas_Object *
77 evas_object_polygon_add(Evas *e)
78 {
79    Evas_Object *obj;
80
81    MAGIC_CHECK(e, Evas, MAGIC_EVAS);
82    return NULL;
83    MAGIC_CHECK_END();
84    obj = evas_object_new(e);
85    evas_object_polygon_init(obj);
86    evas_object_inject(obj, e);
87    return obj;
88 }
89
90 EAPI void
91 evas_object_polygon_point_add(Evas_Object *obj, Evas_Coord x, Evas_Coord y)
92 {
93    Evas_Object_Polygon *o;
94    Evas_Polygon_Point *p;
95    Evas_Coord min_x, max_x, min_y, max_y;
96    int is, was = 0;
97
98    MAGIC_CHECK(obj, Evas_Object, MAGIC_OBJ);
99    return;
100    MAGIC_CHECK_END();
101    o = (Evas_Object_Polygon *)(obj->object_data);
102    MAGIC_CHECK(o, Evas_Object_Polygon, MAGIC_OBJ_POLYGON);
103    return;
104    MAGIC_CHECK_END();
105
106    if (obj->layer->evas->events_frozen <= 0)
107      {
108         if (!evas_event_passes_through(obj) &&
109             !evas_event_freezes_through(obj))
110           was = evas_object_is_in_output_rect(obj,
111                                               obj->layer->evas->pointer.x,
112                                               obj->layer->evas->pointer.y,
113                                               1, 1);
114      }
115    if (!o->points)
116      {
117         o->offset.x = obj->cur.geometry.x;
118         o->offset.y = obj->cur.geometry.y;
119      }
120    else
121      {
122         /* Update all points and take offset into account. */
123         Eina_List *over;
124
125         EINA_LIST_FOREACH(o->points, over, p)
126           {
127              p->x += o->offset.x;
128              p->y += o->offset.y;
129           }
130      }
131
132    p = malloc(sizeof(Evas_Polygon_Point));
133    if (!p) return;
134    p->x = x + o->offset.x;
135    p->y = y + o->offset.y;
136
137    if (!o->points)
138      {
139         obj->cur.geometry.x = p->x;
140         obj->cur.geometry.y = p->y;
141         obj->cur.geometry.w = 2;
142         obj->cur.geometry.h = 2;
143      }
144    else
145      {
146         if (p->x < obj->cur.geometry.x) min_x = p->x;
147         else min_x = obj->cur.geometry.x;
148         if (p->x > (obj->cur.geometry.x + obj->cur.geometry.w - 2))
149           max_x = p->x;
150         else max_x = obj->cur.geometry.x + obj->cur.geometry.w - 2;
151         if (p->y < obj->cur.geometry.y) min_y = p->y;
152         else min_y = obj->cur.geometry.y;
153         if (p->y > (obj->cur.geometry.y + obj->cur.geometry.h - 2))
154           max_y = p->y;
155         else max_y = obj->cur.geometry.y + obj->cur.geometry.h - 2;
156         obj->cur.geometry.x = min_x;
157         obj->cur.geometry.y = min_y;
158         obj->cur.geometry.w = max_x - min_x + 2;
159         obj->cur.geometry.h = max_y - min_y + 2;
160      }
161    o->points = eina_list_append(o->points, p);
162
163    o->geometry = obj->cur.geometry;
164    o->offset.x = 0;
165    o->offset.y = 0;
166
167    ////   obj->cur.cache.geometry.validity = 0;
168    o->changed = EINA_TRUE;
169    evas_object_change(obj);
170    evas_object_clip_dirty(obj);
171    evas_object_coords_recalc(obj);
172    if (obj->layer->evas->events_frozen <= 0)
173      {
174         is = evas_object_is_in_output_rect(obj,
175                                            obj->layer->evas->pointer.x,
176                                            obj->layer->evas->pointer.y, 1, 1);
177         if (!evas_event_passes_through(obj) &&
178             !evas_event_freezes_through(obj) )
179           {
180              if ((is ^ was) && obj->cur.visible)
181                evas_event_feed_mouse_move(obj->layer->evas,
182                                           obj->layer->evas->pointer.x,
183                                           obj->layer->evas->pointer.y,
184                                           obj->layer->evas->last_timestamp,
185                                           NULL);
186           }
187      }
188    evas_object_inform_call_move(obj);
189    evas_object_inform_call_resize(obj);
190 }
191
192 EAPI void
193 evas_object_polygon_points_clear(Evas_Object *obj)
194 {
195    Evas_Object_Polygon *o;
196    int is, was;
197
198    MAGIC_CHECK(obj, Evas_Object, MAGIC_OBJ);
199    return;
200    MAGIC_CHECK_END();
201    o = (Evas_Object_Polygon *)(obj->object_data);
202    MAGIC_CHECK(o, Evas_Object_Polygon, MAGIC_OBJ_POLYGON);
203    return;
204    MAGIC_CHECK_END();
205    was = evas_object_is_in_output_rect(obj,
206                                        obj->layer->evas->pointer.x,
207                                        obj->layer->evas->pointer.y, 1, 1);
208    while (o->points)
209      {
210         free(o->points->data);
211         o->points = eina_list_remove(o->points, o->points->data);
212      }
213    obj->cur.geometry.x = 0;
214    obj->cur.geometry.y = 0;
215    obj->cur.geometry.w = 0;
216    obj->cur.geometry.h = 0;
217    ////   obj->cur.cache.geometry.validity = 0;
218    o->changed = EINA_TRUE;
219    evas_object_change(obj);
220    evas_object_clip_dirty(obj);
221    evas_object_coords_recalc(obj);
222    is = evas_object_is_in_output_rect(obj,
223                                       obj->layer->evas->pointer.x,
224                                       obj->layer->evas->pointer.y, 1, 1);
225    if ((is || was) && obj->cur.visible)
226      evas_event_feed_mouse_move(obj->layer->evas,
227                                 obj->layer->evas->pointer.x,
228                                 obj->layer->evas->pointer.y,
229                                 obj->layer->evas->last_timestamp,
230                                 NULL);
231    evas_object_inform_call_move(obj);
232    evas_object_inform_call_resize(obj);
233 }
234
235 /* all nice and private */
236 static void
237 evas_object_polygon_init(Evas_Object *obj)
238 {
239    /* alloc image ob, setup methods and default values */
240    obj->object_data = evas_object_polygon_new();
241    /* set up default settings for this kind of object */
242    obj->cur.color.r = 255;
243    obj->cur.color.g = 255;
244    obj->cur.color.b = 255;
245    obj->cur.color.a = 255;
246    obj->cur.geometry.x = 0;
247    obj->cur.geometry.y = 0;
248    obj->cur.geometry.w = 0;
249    obj->cur.geometry.h = 0;
250    obj->cur.layer = 0;
251    /* set up object-specific settings */
252    obj->prev = obj->cur;
253    /* set up methods (compulsory) */
254    obj->func = &object_func;
255    obj->type = o_type;
256 }
257
258 static void *
259 evas_object_polygon_new(void)
260 {
261    Evas_Object_Polygon *o;
262
263    /* alloc obj private data */
264    EVAS_MEMPOOL_INIT(_mp_obj, "evas_object_polygon", Evas_Object_Polygon, 32, NULL);
265    o = EVAS_MEMPOOL_ALLOC(_mp_obj, Evas_Object_Polygon);
266    if (!o) return NULL;
267    EVAS_MEMPOOL_PREP(_mp_obj, o, Evas_Object_Polygon);
268    o->magic = MAGIC_OBJ_POLYGON;
269    return o;
270 }
271
272 static void
273 evas_object_polygon_free(Evas_Object *obj)
274 {
275    Evas_Object_Polygon *o;
276
277    /* frees private object data. very simple here */
278    o = (Evas_Object_Polygon *)(obj->object_data);
279    MAGIC_CHECK(o, Evas_Object_Polygon, MAGIC_OBJ_POLYGON);
280    return;
281    MAGIC_CHECK_END();
282    /* free obj */
283    while (o->points)
284      {
285         free(o->points->data);
286         o->points = eina_list_remove(o->points, o->points->data);
287      }
288    o->engine_data = obj->layer->evas->engine.func->polygon_points_clear(obj->layer->evas->engine.data.output,
289                                                                         obj->layer->evas->engine.data.context,
290                                                                         o->engine_data);
291    o->magic = 0;
292    EVAS_MEMPOOL_FREE(_mp_obj, o);
293 }
294
295 static void
296 evas_object_polygon_render(Evas_Object *obj, void *output, void *context, void *surface, int x, int y)
297 {
298    Evas_Object_Polygon *o;
299    Eina_List *l;
300    Evas_Polygon_Point *p;
301
302    /* render object to surface with context, and offxet by x,y */
303    o = (Evas_Object_Polygon *)(obj->object_data);
304    obj->layer->evas->engine.func->context_color_set(output,
305                                                     context,
306                                                     obj->cur.cache.clip.r,
307                                                     obj->cur.cache.clip.g,
308                                                     obj->cur.cache.clip.b,
309                                                     obj->cur.cache.clip.a);
310    obj->layer->evas->engine.func->context_multiplier_unset(output,
311                                                            context);
312    obj->layer->evas->engine.func->context_render_op_set(output, context,
313                                                         obj->cur.render_op);
314    if (o->changed)
315      {
316         o->engine_data = obj->layer->evas->engine.func->polygon_points_clear(obj->layer->evas->engine.data.output,
317                                                                              obj->layer->evas->engine.data.context,
318                                                                              o->engine_data);
319         EINA_LIST_FOREACH(o->points, l, p)
320           {
321              //px = evas_coord_world_x_to_screen(obj->layer->evas, p->x);
322              //py = evas_coord_world_y_to_screen(obj->layer->evas, p->y);
323              o->engine_data = obj->layer->evas->engine.func->polygon_point_add(obj->layer->evas->engine.data.output,
324                                                                                obj->layer->evas->engine.data.context,
325                                                                                o->engine_data,
326                                                                                p->x, p->y);
327           }
328      }
329
330    if (o->engine_data)
331      obj->layer->evas->engine.func->polygon_draw(output,
332                                                  context,
333                                                  surface,
334                                                  o->engine_data,
335                                                  o->offset.x + x, o->offset.y + y);
336 }
337
338 static void
339 evas_object_polygon_render_pre(Evas_Object *obj)
340 {
341    Evas_Object_Polygon *o;
342    int is_v, was_v;
343
344    /* dont pre-render the obj twice! */
345    if (obj->pre_render_done) return;
346    obj->pre_render_done = 1;
347    /* pre-render phase. this does anything an object needs to do just before */
348    /* rendering. this could mean loading the image data, retrieving it from */
349    /* elsewhere, decoding video etc. */
350    /* then when this is done the object needs to figure if it changed and */
351    /* if so what and where and add the appropriate redraw lines */
352    o = (Evas_Object_Polygon *)(obj->object_data);
353    /* if someone is clipping this obj - go calculate the clipper */
354    if (obj->cur.clipper)
355      {
356         if (obj->cur.cache.clip.dirty)
357           evas_object_clip_recalc(obj->cur.clipper);
358         obj->cur.clipper->func->render_pre(obj->cur.clipper);
359      }
360    /* now figure what changed and add draw rects */
361    /* if it just became visible or invisible */
362    is_v = evas_object_is_visible(obj);
363    was_v = evas_object_was_visible(obj);
364    if (is_v != was_v)
365      {
366         evas_object_render_pre_visible_change(&obj->layer->evas->clip_changes, obj, is_v, was_v);
367         goto done;
368      }
369    if (obj->changed_map)
370      {
371         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes,
372                                             obj);
373         goto done;
374      }
375    /* it's not visible - we accounted for it appearing or not so just abort */
376    if (!is_v) goto done;
377    /* clipper changed this is in addition to anything else for obj */
378    evas_object_render_pre_clipper_change(&obj->layer->evas->clip_changes, obj);
379    /* if we restacked (layer or just within a layer) */
380    if (obj->restack)
381      {
382         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
383         goto done;
384      }
385    /* if it changed render op */
386    if (obj->cur.render_op != obj->prev.render_op)
387      {
388         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
389         goto done;
390      }
391    /* if it changed color */
392    if ((obj->cur.color.r != obj->prev.color.r) ||
393        (obj->cur.color.g != obj->prev.color.g) ||
394        (obj->cur.color.b != obj->prev.color.b) ||
395        (obj->cur.color.a != obj->prev.color.a))
396      {
397         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
398         goto done;
399      }
400    /* if it changed geometry - and obviously not visibility or color */
401    /* calculate differences since we have a constant color fill */
402    /* we really only need to update the differences */
403    if ((obj->cur.geometry.x != obj->prev.geometry.x) ||
404        (obj->cur.geometry.y != obj->prev.geometry.y) ||
405        (obj->cur.geometry.w != obj->prev.geometry.w) ||
406        (obj->cur.geometry.h != obj->prev.geometry.h) ||
407        (o->changed))
408      {
409         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
410         goto done;
411      }
412    done:
413    if ((obj->cur.geometry.x != obj->prev.geometry.x) ||
414        (obj->cur.geometry.y != obj->prev.geometry.y))
415      {
416         if (!o->changed)
417           {
418              o->offset.x += obj->cur.geometry.x - obj->prev.geometry.x;
419              o->offset.y += obj->cur.geometry.y - obj->prev.geometry.y;
420           }
421         else
422           {
423              o->offset.x += obj->cur.geometry.x - o->geometry.x;
424              o->offset.y += obj->cur.geometry.y - o->geometry.y;
425           }
426      }
427    evas_object_render_pre_effect_updates(&obj->layer->evas->clip_changes, obj, is_v, was_v);
428 }
429
430 static void
431 evas_object_polygon_render_post(Evas_Object *obj)
432 {
433    Evas_Object_Polygon *o;
434
435    /* this moves the current data to the previous state parts of the object */
436    /* in whatever way is safest for the object. also if we don't need object */
437    /* data anymore we can free it if the object deems this is a good idea */
438    o = (Evas_Object_Polygon *)(obj->object_data);
439    /* remove those pesky changes */
440    evas_object_clip_changes_clean(obj);
441    /* move cur to prev safely for object data */
442    obj->prev = obj->cur;
443    o->changed = 0;
444 }
445
446 static unsigned int evas_object_polygon_id_get(Evas_Object *obj)
447 {
448    Evas_Object_Polygon *o;
449
450    o = (Evas_Object_Polygon *)(obj->object_data);
451    if (!o) return 0;
452    return MAGIC_OBJ_POLYGON;
453 }
454
455 static unsigned int evas_object_polygon_visual_id_get(Evas_Object *obj)
456 {
457    Evas_Object_Polygon *o;
458
459    o = (Evas_Object_Polygon *)(obj->object_data);
460    if (!o) return 0;
461    return MAGIC_OBJ_SHAPE;
462 }
463
464 static void *evas_object_polygon_engine_data_get(Evas_Object *obj)
465 {
466    Evas_Object_Polygon *o;
467
468    o = (Evas_Object_Polygon *)(obj->object_data);
469    if (!o) return NULL;
470    return o->engine_data;
471 }
472
473 static int
474 evas_object_polygon_is_opaque(Evas_Object *obj __UNUSED__)
475 {
476    /* this returns 1 if the internal object data implies that the object is */
477    /* currently fully opaque over the entire line it occupies */
478    return 0;
479 }
480
481 static int
482 evas_object_polygon_was_opaque(Evas_Object *obj __UNUSED__)
483 {
484    /* this returns 1 if the internal object data implies that the object was */
485    /* previously fully opaque over the entire line it occupies */
486    return 0;
487 }
488
489 /* We count the number of edges a "ray" 90 degs upwards from our point
490  * intersects with. If it's even, we are outside of the polygon, if it's odd,
491  * we are inside of it. */
492 static int
493 evas_object_polygon_is_inside(Evas_Object *obj, Evas_Coord x, Evas_Coord y)
494 {
495    Evas_Object_Polygon *o;
496    int num_edges = 0; /* Number of edges we crossed */
497    Eina_List *itr;
498    Evas_Polygon_Point *p;
499
500    o = (Evas_Object_Polygon *)(obj->object_data);
501    if (!o) return 0;
502    if (!o->points) return 0;
503
504    /* Adjust X and Y according to current geometry */
505    x -= o->offset.x;
506    y -= o->offset.y;
507
508    if (eina_list_count(o->points) == 1)
509      {
510         p = eina_list_data_get(o->points);
511         return ((p->x == x) && (p->y == y));
512      }
513
514    EINA_LIST_FOREACH(o->points, itr, p)
515      {
516         Evas_Coord line_y;
517         Eina_List *next = eina_list_next(itr);
518         Evas_Polygon_Point *p_next;
519         /* Get the next, or if there's no next, take the first */
520         if (next)
521           {
522              p_next = eina_list_data_get(next);
523           }
524         else
525           {
526              p_next = eina_list_data_get(o->points);
527           }
528
529         /* Make sure that we are directly below the edge,
530          * and that p->x != p_next->x */
531         if (((p->x < p_next->x) && (p->x <= x) && (x < p_next->x)) ||
532               ((p->x > p_next->x) && (p_next->x < x) && (x <= p->x)))
533           {
534              line_y = ((double) (p->y - p_next->y) /
535                    (double) (p->x - p_next->x)) *
536                 (x - p_next->x) + p_next->y;
537              /* We crossed that edge if the line is directly above us */
538              if (line_y < y)
539                 num_edges++;
540           }
541      }
542
543    /* Return true if num_edges is odd */
544    return ((num_edges % 2) == 1);
545 }
546
547 static int
548 evas_object_polygon_was_inside(Evas_Object *obj __UNUSED__, Evas_Coord x __UNUSED__, Evas_Coord y __UNUSED__)
549 {
550    /* this returns 1 if the canvas co-ordinates were inside the object based */
551    /* on object private data. not much use for rects, but for polys, images */
552    /* and other complex objects it might be */
553    return 1;
554 }