merged evas image masking code from brett
[framework/uifw/evas.git] / src / lib / canvas / evas_clip.c
1 #include "evas_common.h"
2 #include "evas_private.h"
3
4 void
5 evas_object_clip_dirty(Evas_Object *obj)
6 {
7    Eina_List *l;
8    Evas_Object *data;
9
10    obj->cur.cache.clip.dirty = 1;
11    EINA_LIST_FOREACH(obj->clip.clipees, l, data)
12      evas_object_clip_dirty(data);
13 }
14
15 void
16 evas_object_recalc_clippees(Evas_Object *obj)
17 {
18    Eina_List *l;
19    Evas_Object *data;
20
21    if (obj->cur.cache.clip.dirty)
22      {
23         evas_object_clip_recalc(obj);
24         EINA_LIST_FOREACH(obj->clip.clipees, l, data)
25           evas_object_recalc_clippees(data);
26      }
27 }
28
29 int
30 evas_object_clippers_was_visible(Evas_Object *obj)
31 {
32    if (obj->prev.visible)
33      {
34         if (obj->prev.clipper)
35           return evas_object_clippers_is_visible(obj->prev.clipper);
36         return 1;
37      }
38    return 0;
39 }
40
41 /* aaaaargh (pirate voice) ... notes!
42  * 
43  * we have a big problem until now that's gone undetected... until yesterday.
44  * that problem involves clips and maps and smart objects. hooray! 3 of the
45  * more complex bits of evas - and maps and smart objects  being one of the
46  * nastiest ones.
47  * 
48  * what is the problem? when a clip crosses a map boundary. that is to say
49  * that when the clipper and clippee are not within the child tree of the
50  * mapped object. in this case "bad stuff" happens. basically as clips are
51  * then used to render objects, but they no longer apply as you'd expect as
52  * the map transfomr the objects to-be-clipped separately from the objects
53  * that clip them and this whole relationship is broken by maps. it somehow
54  * managed to not break with the advent of smart objects. lucky me... but
55  * maps killed it. now... what do we do? that is a good question. detect
56  * such a broken link and "turn off clipping" in that event - sure. but this
57  * isn't going to be cheap as ANY addition or deletion of a map to an object
58  * or any change in clipper of an object or any change in smart object
59  * membership needs to walk the obj tree both up and down from the changed
60  * object and probably walk entire object trees to find these and mark them.
61  * thats silly-expensive and i was about to fix it that way but it has since
62  * dawned on me that that is just going to kill performance in some critical
63  * areas like during object setup and manipulation, as well as teardown.
64  * 
65  * aaaaagh! best for now is to document this as a "don't do it damnit!" thing
66  * and have the apps avoid it. but even then - how to do this? this is not
67  * easy. everywhere i turn so far i come up to either expensive operations,
68  * breaks in logic, or nasty re-work of apps or4 the whole concept of clipping,
69  * smart objects and maps... and that will have to wait for evas 2.0
70  * 
71  * the below does clip fixups etc. in the even a clip spans a map boundary.
72  * not pretty, but necessary.
73  */
74
75 #define MAP_ACROSS 1
76 static void
77 evas_object_child_map_across_mark(Evas_Object *obj, Evas_Object *map_obj, Eina_Bool force)
78 {
79 #ifdef MAP_ACROSS
80    if ((obj->cur.map_parent != map_obj) || force)
81      {
82         obj->cur.map_parent = map_obj;
83         obj->cur.cache.clip.dirty = 1;
84         evas_object_clip_recalc(obj);
85         if (obj->smart.smart)
86           {
87              Evas_Object *obj2;
88              
89              EINA_INLIST_FOREACH(evas_object_smart_members_get_direct(obj), obj2)
90                {
91                   // if obj has its own map - skip it. already done
92                   if ((obj2->cur.map) && (obj2->cur.usemap)) continue;
93                   evas_object_child_map_across_mark(obj2, map_obj, force);
94                }
95           }
96         else if (obj->clip.clipees)
97           {
98              Eina_List *l;
99              Evas_Object *obj2;
100              
101              EINA_LIST_FOREACH(obj->clip.clipees, l, obj2)
102                 evas_object_child_map_across_mark(obj2, map_obj, force);
103           }
104      }
105 #endif   
106 }
107
108 void
109 evas_object_clip_across_check(Evas_Object *obj)
110 {
111 #ifdef MAP_ACROSS
112    if (!obj->cur.clipper) return;
113    if (obj->cur.clipper->cur.map_parent != obj->cur.map_parent)
114       evas_object_child_map_across_mark(obj, obj->cur.map_parent, 1);
115 #endif   
116 }
117
118 void
119 evas_object_clip_across_clippees_check(Evas_Object *obj)
120 {
121 #ifdef MAP_ACROSS
122    Eina_List *l;
123    Evas_Object *obj2;
124
125    if (!obj->clip.clipees) return;
126 // schloooooooooooow:
127 //   evas_object_child_map_across_mark(obj, obj->cur.map_parent, 1);
128 // buggy:
129    evas_object_child_map_across_mark(obj, obj->cur.map_parent, 0);
130    if (obj->cur.cache.clip.dirty)
131      {
132         EINA_LIST_FOREACH(obj->clip.clipees, l, obj2)
133            evas_object_clip_across_clippees_check(obj2);
134      }
135 #endif   
136 }
137
138 // this function is called on an object when map is enabled or disabled on it
139 // thus creating a "map boundary" at that point.
140 // 
141 // FIXME: flip2 test broken in elm - might be show/hide of clips
142 void
143 evas_object_mapped_clip_across_mark(Evas_Object *obj)
144 {
145 #ifdef MAP_ACROSS
146    if ((obj->cur.map) && (obj->cur.usemap))
147       evas_object_child_map_across_mark(obj, obj, 0);
148    else
149      {
150         if (obj->smart.parent)
151            evas_object_child_map_across_mark
152            (obj, obj->smart.parent->cur.map_parent, 0);
153         else
154            evas_object_child_map_across_mark(obj, NULL, 0); 
155     }
156 #endif   
157 }
158
159 /* public functions */
160
161 /**
162  * @addtogroup Evas_Object_Group_Basic
163  * @{
164  */
165
166 /**
167  * Clip one object to another.
168  * @param obj The object to be clipped
169  * @param clip The object to clip @p obj by
170  *
171  * This function will clip the object @p obj to the area occupied by the
172  * object @p clipper. This means the object @p obj will only be visible within
173  * the area occupied by the clipping object (@p clip). The color of the object
174  * being clipped will be multiplied by the color of the clipping object, so
175  * the resulting color for the clipped object is
176  * RESULT = (OBJ * CLIP) / (255 * 255) per color element (red, green, blue and
177  * alpha). Clipping is recursive, so clip objects may be clipped by other
178  * objects, and their color will in tern be multiplied. You may NOT set up
179  * circular clipping lists (i.e. object 1 clips object 2 which clips object 1).
180  * The behavior of Evas is undefined in this case. Objects which do not clip
181  * others are visible as normal, those that clip 1 or more objects become
182  * invisible themselves, only affecting what they clip. If an object ceases to
183  * have other objects being clipped by it, it will become visible again. The
184  * visibility of an object affects the objects that are clipped by it, so if
185  * the object clipping others is not shown, the objects clipped will not be
186  * shown either. If the object was being clipped by another object when this
187  * function is called, it is implicitly removed from the clipper it is being
188  * clipped to, and now is made to clip its new clipper.
189  *
190  * At the moment the only objects that can validly be used to clip other
191  * objects are rectangle objects. All other object types are invalid and the
192  * result of using them is undefined.
193  *
194  * The clip object @p clip must be a valid object, but may also be NULL in
195  * which case the effect of this function is the same as calling
196  * evas_object_clip_unset() on the @p obj object.
197  *
198  * Example:
199  * @code
200  * extern Evas *evas;
201  * extern Evas_Object *obj;
202  * Evas_Object *clipper;
203  *
204  * clipper = evas_object_rectangle_add(evas);
205  * evas_object_color_set(clipper, 255, 255, 255, 255);
206  * evas_object_move(clipper, 10, 10);
207  * evas_object_resize(clipper, 20, 50);
208  * evas_object_clip_set(obj, clipper);
209  * evas_object_show(clipper);
210  * @endcode
211  *
212  */
213 EAPI void
214 evas_object_clip_set(Evas_Object *obj, Evas_Object *clip)
215 {
216    MAGIC_CHECK(obj, Evas_Object, MAGIC_OBJ);
217    return;
218    MAGIC_CHECK_END();
219    if (!clip)
220      {
221         evas_object_clip_unset(obj);
222         return;
223      }
224    MAGIC_CHECK(clip, Evas_Object, MAGIC_OBJ);
225    return;
226    MAGIC_CHECK_END();
227    if (obj->cur.clipper == clip) return;
228    if (obj == clip) return;
229    if (evas_object_intercept_call_clip_set(obj, clip)) return;
230    if (obj->smart.smart)
231      {
232        if (obj->smart.smart->smart_class->clip_set)
233           obj->smart.smart->smart_class->clip_set(obj, clip);
234      }
235    if (obj->cur.clipper)
236      {
237         /* unclip */
238         obj->cur.clipper->clip.clipees = eina_list_remove(obj->cur.clipper->clip.clipees, obj);
239         if (!obj->cur.clipper->clip.clipees)
240           {
241              obj->cur.clipper->cur.have_clipees = 0;
242              if (obj->cur.clipper->cur.visible)
243                evas_damage_rectangle_add(obj->cur.clipper->layer->evas,
244                                          obj->cur.clipper->cur.geometry.x,
245                                          obj->cur.clipper->cur.geometry.y,
246                                          obj->cur.clipper->cur.geometry.w,
247                                          obj->cur.clipper->cur.geometry.h);
248           }
249         evas_object_change(obj->cur.clipper);
250         evas_object_change(obj);
251         obj->cur.clipper = NULL;
252      }
253    /* clip me */
254    if ((!clip->clip.clipees) && (clip->cur.visible))
255      {
256         /* Basically it just went invisible */
257         clip->changed = 1;
258         clip->layer->evas->changed = 1;
259         evas_damage_rectangle_add(clip->layer->evas,
260                                   clip->cur.geometry.x, clip->cur.geometry.y,
261                                   clip->cur.geometry.w, clip->cur.geometry.h);
262      }
263    obj->cur.clipper = clip;
264    clip->clip.clipees = eina_list_append(clip->clip.clipees, obj);
265    if (clip->clip.clipees) clip->cur.have_clipees = 1;
266
267    /* If it's NOT a rectangle set the mask bits too */
268    /* FIXME: Optmz ths chck */
269    if (strcmp(evas_object_type_get(clip),"rectangle") == 0)
270       obj->cur.mask = NULL;
271    else
272      {
273         void *engdata;
274         obj->cur.mask = clip;
275         engdata = clip->func->engine_data_get(clip);
276         /* FIXME: Images only */
277         clip->layer->evas->engine.func->image_mask_create(
278                                       clip->layer->evas->engine.data.output,
279                                       engdata);
280      }
281    evas_object_change(clip);
282    evas_object_change(obj);
283    evas_object_clip_dirty(obj);
284    evas_object_recalc_clippees(obj);
285    if ((!obj->smart.smart) &&
286        (!((obj->cur.map) && (obj->cur.usemap))))
287      {
288         if (evas_object_is_in_output_rect(obj,
289                                           obj->layer->evas->pointer.x,
290                                           obj->layer->evas->pointer.y, 1, 1))
291           evas_event_feed_mouse_move(obj->layer->evas,
292                                      obj->layer->evas->pointer.x,
293                                      obj->layer->evas->pointer.y,
294                                      obj->layer->evas->last_timestamp,
295                                      NULL);
296      }
297    evas_object_clip_across_check(obj);
298 }
299
300 /**
301  * Get the object clipping this one (if any).
302  * @param obj The object to get the clipper from
303  *
304  * This function returns the the object clipping @p obj. If @p obj not being
305  * clipped, NULL is returned. The object @p obj must be a valid object.
306  *
307  * See also evas_object_clip_set(), evas_object_clip_unset() and
308  * evas_object_clipees_get().
309  *
310  * Example:
311  * @code
312  * extern Evas_Object *obj;
313  * Evas_Object *clipper;
314  *
315  * clipper = evas_object_clip_get(obj);
316  * if (clipper) evas_object_show(clipper);
317  * @endcode
318  *
319  */
320 EAPI Evas_Object *
321 evas_object_clip_get(const Evas_Object *obj)
322 {
323    MAGIC_CHECK(obj, Evas_Object, MAGIC_OBJ);
324    return NULL;
325    MAGIC_CHECK_END();
326    return obj->cur.clipper;
327 }
328
329 /**
330  * Disable clipping for an object.
331  *
332  * @param obj The object to cease clipping on
333  *
334  * This function disables clipping for the object @p obj, if it was already
335  * clipped. If it wasn't, this has no effect. The object @p obj must be a
336  * valid object.
337  *
338  * See also evas_object_clip_set(), evas_object_clipees_get() and
339  * evas_object_clip_get().
340  *
341  * Example:
342  * @code
343  * extern Evas_Object *obj;
344  * Evas_Object *clipper;
345  *
346  * clipper = evas_object_clip_get(obj);
347  * if (clipper)
348  *   {
349  *     evas_object_clip_unset(obj);
350  *     evas_object_hide(obj);
351  *   }
352  * @endcode
353  *
354  */
355 EAPI void
356 evas_object_clip_unset(Evas_Object *obj)
357 {
358    MAGIC_CHECK(obj, Evas_Object, MAGIC_OBJ);
359    return;
360    MAGIC_CHECK_END();
361    if (!obj->cur.clipper) return;
362    /* unclip */
363    if (evas_object_intercept_call_clip_unset(obj)) return;
364    if (obj->smart.smart)
365      {
366        if (obj->smart.smart->smart_class->clip_unset)
367           obj->smart.smart->smart_class->clip_unset(obj);
368      }
369    if (obj->cur.clipper)
370      {
371         obj->cur.clipper->clip.clipees = eina_list_remove(obj->cur.clipper->clip.clipees, obj);
372         if (!obj->cur.clipper->clip.clipees)
373           {
374              obj->cur.clipper->cur.have_clipees = 0;
375              if (obj->cur.clipper->cur.visible)
376                evas_damage_rectangle_add(obj->cur.clipper->layer->evas,
377                                          obj->cur.clipper->cur.geometry.x,
378                                          obj->cur.clipper->cur.geometry.y,
379                                          obj->cur.clipper->cur.geometry.w,
380                                          obj->cur.clipper->cur.geometry.h);
381           }
382         evas_object_change(obj->cur.clipper);
383      }
384    obj->cur.clipper = NULL;
385    evas_object_change(obj);
386    evas_object_clip_dirty(obj);
387    evas_object_recalc_clippees(obj);
388    if ((!obj->smart.smart) && 
389        (!((obj->cur.map) && (obj->cur.usemap))))
390      {
391         if (evas_object_is_in_output_rect(obj,
392                                           obj->layer->evas->pointer.x,
393                                           obj->layer->evas->pointer.y, 1, 1))
394           evas_event_feed_mouse_move(obj->layer->evas,
395                                      obj->layer->evas->pointer.x,
396                                      obj->layer->evas->pointer.y,
397                                      obj->layer->evas->last_timestamp,
398                                      NULL);
399      }
400    evas_object_clip_across_check(obj);
401 }
402
403 /**
404  * Return a list of objects currently clipped by a specific object.
405  *
406  * @param obj The object to get a list of clippees from
407  *
408  * This returns the inernal list handle that contains all objects clipped by
409  * the object @p obj. If none are clipped, it returns NULL. This list is only
410  * valid until the clip list is changed and should be fetched again with another
411  * call to evas_object_clipees_get() if any objects being clipped by this object
412  * are unclipped, clipped by a new object, are deleted or the clipper is
413  * deleted.  These operations will invalidate the list returned so it should
414  * not be used anymore after that point. Any use of the list after this may have
415  * undefined results, not limited just to strange behavior but possible
416  * segfaults and other strange memory errors. The object @p obj must be a valid
417  * object.
418  *
419  * See also evas_object_clip_set(), evas_object_clip_unset() and
420  * evas_object_clip_get().
421  *
422  * Example:
423  * @code
424  * extern Evas_Object *obj;
425  * Evas_Object *clipper;
426  *
427  * clipper = evas_object_clip_get(obj);
428  * if (clipper)
429  *   {
430  *     Eina_List *clippees, *l;
431  *     Evas_Object *obj_tmp;
432  *
433  *     clippees = evas_object_clipees_get(clipper);
434  *     printf("Clipper clips %i objects\n", eina_list_count(clippees));
435  *     EINA_LIST_FOREACH(clippees, l, obj_tmp)
436  *         evas_object_show(obj_tmp);
437  *   }
438  * @endcode
439  */
440 EAPI const Eina_List *
441 evas_object_clipees_get(const Evas_Object *obj)
442 {
443    MAGIC_CHECK(obj, Evas_Object, MAGIC_OBJ);
444    return NULL;
445    MAGIC_CHECK_END();
446    return obj->clip.clipees;
447 }
448
449
450 EAPI void
451 evas_object_mask_set(Evas_Object *obj, Evas_Object *mask)
452 {
453    MAGIC_CHECK(obj, Evas_Object, MAGIC_OBJ);
454    return;
455    MAGIC_CHECK_END();
456    if (!mask)
457      {
458         //evas_object_clip_unset(obj);
459         return;
460      }
461    MAGIC_CHECK(mask, Evas_Object, MAGIC_OBJ);
462    return;
463    MAGIC_CHECK_END();
464    if (obj->cur.mask == mask) return;
465    if (obj == mask) return;
466
467    obj->cur.mask = mask;
468
469    evas_object_clip_set(obj, mask);
470 }
471
472
473
474 /**
475  * @}
476  */