1 /*
2  * Clutter.
3  *
4  * An OpenGL based 'interactive canvas' library.
5  *
6  * Copyright (C) 2010  Intel Corporation.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library. If not, see
20  * <http://www.gnu.org/licenses/>.
21  *
22  * Authors:
23  *      Robert Bragg <robert@linux.intel.com>
24  *      Emmanuele Bassi <ebassi@linux.intel.com>
25  */
26 
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30 
31 #include <string.h>
32 
33 #include <glib-object.h>
34 #include <math.h>
35 
36 #include "clutter-actor-private.h"
37 #include "clutter-paint-volume-private.h"
38 #include "clutter-private.h"
39 #include "clutter-stage-private.h"
40 
41 G_DEFINE_BOXED_TYPE (ClutterPaintVolume, clutter_paint_volume,
42                      clutter_paint_volume_copy,
43                      clutter_paint_volume_free);
44 
45 /*<private>
46  * _clutter_paint_volume_new:
47  * @actor: a #ClutterActor
48  *
49  * Creates a new #ClutterPaintVolume for the given @actor.
50  *
51  * Return value: the newly allocated #ClutterPaintVolume. Use
52  *   clutter_paint_volume_free() to free the resources it uses
53  *
54  * Since: 1.6
55  */
56 ClutterPaintVolume *
_clutter_paint_volume_new(ClutterActor * actor)57 _clutter_paint_volume_new (ClutterActor *actor)
58 {
59   ClutterPaintVolume *pv;
60 
61   g_return_val_if_fail (actor != NULL, NULL);
62 
63   pv = g_slice_new (ClutterPaintVolume);
64 
65   pv->actor = actor;
66 
67   memset (pv->vertices, 0, 8 * sizeof (ClutterVertex));
68 
69   pv->is_static = FALSE;
70   pv->is_empty = TRUE;
71   pv->is_axis_aligned = TRUE;
72   pv->is_complete = TRUE;
73   pv->is_2d = TRUE;
74 
75   return pv;
76 }
77 
78 /* Since paint volumes are used so heavily in a typical paint
79  * traversal of a Clutter scene graph and since paint volumes often
80  * have a very short life cycle that maps well to stack allocation we
81  * allow initializing a static ClutterPaintVolume variable to avoid
82  * hammering the slice allocator.
83  *
84  * We were seeing slice allocation take about 1% cumulative CPU time
85  * for some very simple clutter tests which although it isn't a *lot*
86  * this is an easy way to basically drop that to 0%.
87  *
88  * The PaintVolume will be internally marked as static and
89  * clutter_paint_volume_free should still be used to "free" static
90  * volumes. This allows us to potentially store dynamically allocated
91  * data inside paint volumes in the future since we would be able to
92  * free it during _paint_volume_free().
93  */
94 void
_clutter_paint_volume_init_static(ClutterPaintVolume * pv,ClutterActor * actor)95 _clutter_paint_volume_init_static (ClutterPaintVolume *pv,
96                                    ClutterActor *actor)
97 {
98   pv->actor = actor;
99 
100   memset (pv->vertices, 0, 8 * sizeof (ClutterVertex));
101 
102   pv->is_static = TRUE;
103   pv->is_empty = TRUE;
104   pv->is_axis_aligned = TRUE;
105   pv->is_complete = TRUE;
106   pv->is_2d = TRUE;
107 }
108 
109 void
_clutter_paint_volume_copy_static(const ClutterPaintVolume * src_pv,ClutterPaintVolume * dst_pv)110 _clutter_paint_volume_copy_static (const ClutterPaintVolume *src_pv,
111                                    ClutterPaintVolume       *dst_pv)
112 {
113 
114   g_return_if_fail (src_pv != NULL && dst_pv != NULL);
115 
116   memcpy (dst_pv, src_pv, sizeof (ClutterPaintVolume));
117   dst_pv->is_static = TRUE;
118 }
119 
120 /**
121  * clutter_paint_volume_copy:
122  * @pv: a #ClutterPaintVolume
123  *
124  * Copies @pv into a new #ClutterPaintVolume
125  *
126  * Return value: a newly allocated copy of a #ClutterPaintVolume
127  *
128  * Since: 1.6
129  */
130 ClutterPaintVolume *
clutter_paint_volume_copy(const ClutterPaintVolume * pv)131 clutter_paint_volume_copy (const ClutterPaintVolume *pv)
132 {
133   ClutterPaintVolume *copy;
134 
135   g_return_val_if_fail (pv != NULL, NULL);
136 
137   copy = g_slice_dup (ClutterPaintVolume, pv);
138   copy->is_static = FALSE;
139 
140   return copy;
141 }
142 
143 void
_clutter_paint_volume_set_from_volume(ClutterPaintVolume * pv,const ClutterPaintVolume * src)144 _clutter_paint_volume_set_from_volume (ClutterPaintVolume       *pv,
145                                        const ClutterPaintVolume *src)
146 {
147   gboolean is_static = pv->is_static;
148   memcpy (pv, src, sizeof (ClutterPaintVolume));
149   pv->is_static = is_static;
150 }
151 
152 /**
153  * clutter_paint_volume_free:
154  * @pv: a #ClutterPaintVolume
155  *
156  * Frees the resources allocated by @pv
157  *
158  * Since: 1.6
159  */
160 void
clutter_paint_volume_free(ClutterPaintVolume * pv)161 clutter_paint_volume_free (ClutterPaintVolume *pv)
162 {
163   g_return_if_fail (pv != NULL);
164 
165   if (G_LIKELY (pv->is_static))
166     return;
167 
168   g_slice_free (ClutterPaintVolume, pv);
169 }
170 
171 /**
172  * clutter_paint_volume_set_origin:
173  * @pv: a #ClutterPaintVolume
174  * @origin: a #ClutterVertex
175  *
176  * Sets the origin of the paint volume.
177  *
178  * The origin is defined as the X, Y and Z coordinates of the top-left
179  * corner of an actor's paint volume, in actor coordinates.
180  *
181  * The default is origin is assumed at: (0, 0, 0)
182  *
183  * Since: 1.6
184  */
185 void
clutter_paint_volume_set_origin(ClutterPaintVolume * pv,const ClutterVertex * origin)186 clutter_paint_volume_set_origin (ClutterPaintVolume  *pv,
187                                  const ClutterVertex *origin)
188 {
189   static const int key_vertices[4] = { 0, 1, 3, 4 };
190   float dx, dy, dz;
191   int i;
192 
193   g_return_if_fail (pv != NULL);
194 
195   dx = origin->x - pv->vertices[0].x;
196   dy = origin->y - pv->vertices[0].y;
197   dz = origin->z - pv->vertices[0].z;
198 
199   /* If we change the origin then all the key vertices of the paint
200    * volume need to be shifted too... */
201   for (i = 0; i < 4; i++)
202     {
203       pv->vertices[key_vertices[i]].x += dx;
204       pv->vertices[key_vertices[i]].y += dy;
205       pv->vertices[key_vertices[i]].z += dz;
206     }
207 
208   pv->is_complete = FALSE;
209 }
210 
211 /**
212  * clutter_paint_volume_get_origin:
213  * @pv: a #ClutterPaintVolume
214  * @vertex: (out): the return location for a #ClutterVertex
215  *
216  * Retrieves the origin of the #ClutterPaintVolume.
217  *
218  * Since: 1.6
219  */
220 void
clutter_paint_volume_get_origin(const ClutterPaintVolume * pv,ClutterVertex * vertex)221 clutter_paint_volume_get_origin (const ClutterPaintVolume *pv,
222                                  ClutterVertex            *vertex)
223 {
224   g_return_if_fail (pv != NULL);
225   g_return_if_fail (vertex != NULL);
226 
227   *vertex = pv->vertices[0];
228 }
229 
230 static void
_clutter_paint_volume_update_is_empty(ClutterPaintVolume * pv)231 _clutter_paint_volume_update_is_empty (ClutterPaintVolume *pv)
232 {
233   if (pv->vertices[0].x == pv->vertices[1].x &&
234       pv->vertices[0].y == pv->vertices[3].y &&
235       pv->vertices[0].z == pv->vertices[4].z)
236     pv->is_empty = TRUE;
237   else
238     pv->is_empty = FALSE;
239 }
240 
241 /**
242  * clutter_paint_volume_set_width:
243  * @pv: a #ClutterPaintVolume
244  * @width: the width of the paint volume, in pixels
245  *
246  * Sets the width of the paint volume. The width is measured along
247  * the x axis in the actor coordinates that @pv is associated with.
248  *
249  * Since: 1.6
250  */
251 void
clutter_paint_volume_set_width(ClutterPaintVolume * pv,gfloat width)252 clutter_paint_volume_set_width (ClutterPaintVolume *pv,
253                                 gfloat              width)
254 {
255   gfloat right_xpos;
256 
257   g_return_if_fail (pv != NULL);
258   g_return_if_fail (width >= 0.0f);
259 
260   /* If the volume is currently empty then only the origin is
261    * currently valid */
262   if (pv->is_empty)
263     pv->vertices[1] = pv->vertices[3] = pv->vertices[4] = pv->vertices[0];
264 
265   if (!pv->is_axis_aligned)
266     _clutter_paint_volume_axis_align (pv);
267 
268   right_xpos = pv->vertices[0].x + width;
269 
270   /* Move the right vertices of the paint box relative to the
271    * origin... */
272   pv->vertices[1].x = right_xpos;
273   /* pv->vertices[2].x = right_xpos; NB: updated lazily */
274   /* pv->vertices[5].x = right_xpos; NB: updated lazily */
275   /* pv->vertices[6].x = right_xpos; NB: updated lazily */
276 
277   pv->is_complete = FALSE;
278 
279   _clutter_paint_volume_update_is_empty (pv);
280 }
281 
282 /**
283  * clutter_paint_volume_get_width:
284  * @pv: a #ClutterPaintVolume
285  *
286  * Retrieves the width of the volume's, axis aligned, bounding box.
287  *
288  * In other words; this takes into account what actor's coordinate
289  * space @pv belongs too and conceptually fits an axis aligned box
290  * around the volume. It returns the size of that bounding box as
291  * measured along the x-axis.
292  *
293  * If, for example, clutter_actor_get_transformed_paint_volume()
294  * is used to transform a 2D child actor that is 100px wide, 100px
295  * high and 0px deep into container coordinates then the width might
296  * not simply be 100px if the child actor has a 3D rotation applied to
297  * it.
298  *
299  * Remember: if clutter_actor_get_transformed_paint_volume() is
300  * used then a transformed child volume will be defined relative to the
301  * ancestor container actor and so a 2D child actor can have a 3D
302  * bounding volume.
303  *
304  * There are no accuracy guarantees for the reported width,
305  * except that it must always be greater than, or equal to, the
306  * actor's width. This is because actors may report simple, loose
307  * fitting paint volumes for efficiency.
308 
309  * Return value: the width, in units of @pv's local coordinate system.
310  *
311  * Since: 1.6
312  */
313 gfloat
clutter_paint_volume_get_width(const ClutterPaintVolume * pv)314 clutter_paint_volume_get_width (const ClutterPaintVolume *pv)
315 {
316   g_return_val_if_fail (pv != NULL, 0.0);
317 
318   if (pv->is_empty)
319     return 0;
320   else if (!pv->is_axis_aligned)
321     {
322       ClutterPaintVolume tmp;
323       float width;
324       _clutter_paint_volume_copy_static (pv, &tmp);
325       _clutter_paint_volume_axis_align (&tmp);
326       width = tmp.vertices[1].x - tmp.vertices[0].x;
327       clutter_paint_volume_free (&tmp);
328       return width;
329     }
330   else
331     return pv->vertices[1].x - pv->vertices[0].x;
332 }
333 
334 /**
335  * clutter_paint_volume_set_height:
336  * @pv: a #ClutterPaintVolume
337  * @height: the height of the paint volume, in pixels
338  *
339  * Sets the height of the paint volume. The height is measured along
340  * the y axis in the actor coordinates that @pv is associated with.
341  *
342  * Since: 1.6
343  */
344 void
clutter_paint_volume_set_height(ClutterPaintVolume * pv,gfloat height)345 clutter_paint_volume_set_height (ClutterPaintVolume *pv,
346                                  gfloat              height)
347 {
348   gfloat height_ypos;
349 
350   g_return_if_fail (pv != NULL);
351   g_return_if_fail (height >= 0.0f);
352 
353   /* If the volume is currently empty then only the origin is
354    * currently valid */
355   if (pv->is_empty)
356     pv->vertices[1] = pv->vertices[3] = pv->vertices[4] = pv->vertices[0];
357 
358   if (!pv->is_axis_aligned)
359     _clutter_paint_volume_axis_align (pv);
360 
361   height_ypos = pv->vertices[0].y + height;
362 
363   /* Move the bottom vertices of the paint box relative to the
364    * origin... */
365   /* pv->vertices[2].y = height_ypos; NB: updated lazily */
366   pv->vertices[3].y = height_ypos;
367   /* pv->vertices[6].y = height_ypos; NB: updated lazily */
368   /* pv->vertices[7].y = height_ypos; NB: updated lazily */
369   pv->is_complete = FALSE;
370 
371   _clutter_paint_volume_update_is_empty (pv);
372 }
373 
374 /**
375  * clutter_paint_volume_get_height:
376  * @pv: a #ClutterPaintVolume
377  *
378  * Retrieves the height of the volume's, axis aligned, bounding box.
379  *
380  * In other words; this takes into account what actor's coordinate
381  * space @pv belongs too and conceptually fits an axis aligned box
382  * around the volume. It returns the size of that bounding box as
383  * measured along the y-axis.
384  *
385  * If, for example, clutter_actor_get_transformed_paint_volume()
386  * is used to transform a 2D child actor that is 100px wide, 100px
387  * high and 0px deep into container coordinates then the height might
388  * not simply be 100px if the child actor has a 3D rotation applied to
389  * it.
390  *
391  * Remember: if clutter_actor_get_transformed_paint_volume() is
392  * used then a transformed child volume will be defined relative to the
393  * ancestor container actor and so a 2D child actor
394  * can have a 3D bounding volume.
395  *
396  * There are no accuracy guarantees for the reported height,
397  * except that it must always be greater than, or equal to, the actor's
398  * height. This is because actors may report simple, loose fitting paint
399  * volumes for efficiency.
400  *
401  * Return value: the height, in units of @pv's local coordinate system.
402  *
403  * Since: 1.6
404  */
405 gfloat
clutter_paint_volume_get_height(const ClutterPaintVolume * pv)406 clutter_paint_volume_get_height (const ClutterPaintVolume *pv)
407 {
408   g_return_val_if_fail (pv != NULL, 0.0);
409 
410   if (pv->is_empty)
411     return 0;
412   else if (!pv->is_axis_aligned)
413     {
414       ClutterPaintVolume tmp;
415       float height;
416       _clutter_paint_volume_copy_static (pv, &tmp);
417       _clutter_paint_volume_axis_align (&tmp);
418       height = tmp.vertices[3].y - tmp.vertices[0].y;
419       clutter_paint_volume_free (&tmp);
420       return height;
421     }
422   else
423     return pv->vertices[3].y - pv->vertices[0].y;
424 }
425 
426 /**
427  * clutter_paint_volume_set_depth:
428  * @pv: a #ClutterPaintVolume
429  * @depth: the depth of the paint volume, in pixels
430  *
431  * Sets the depth of the paint volume. The depth is measured along
432  * the z axis in the actor coordinates that @pv is associated with.
433  *
434  * Since: 1.6
435  */
436 void
clutter_paint_volume_set_depth(ClutterPaintVolume * pv,gfloat depth)437 clutter_paint_volume_set_depth (ClutterPaintVolume *pv,
438                                 gfloat              depth)
439 {
440   gfloat depth_zpos;
441 
442   g_return_if_fail (pv != NULL);
443   g_return_if_fail (depth >= 0.0f);
444 
445   /* If the volume is currently empty then only the origin is
446    * currently valid */
447   if (pv->is_empty)
448     pv->vertices[1] = pv->vertices[3] = pv->vertices[4] = pv->vertices[0];
449 
450   if (!pv->is_axis_aligned)
451     _clutter_paint_volume_axis_align (pv);
452 
453   depth_zpos = pv->vertices[0].z + depth;
454 
455   /* Move the back vertices of the paint box relative to the
456    * origin... */
457   pv->vertices[4].z = depth_zpos;
458   /* pv->vertices[5].z = depth_zpos; NB: updated lazily */
459   /* pv->vertices[6].z = depth_zpos; NB: updated lazily */
460   /* pv->vertices[7].z = depth_zpos; NB: updated lazily */
461 
462   pv->is_complete = FALSE;
463   pv->is_2d = depth ? FALSE : TRUE;
464   _clutter_paint_volume_update_is_empty (pv);
465 }
466 
467 /**
468  * clutter_paint_volume_get_depth:
469  * @pv: a #ClutterPaintVolume
470  *
471  * Retrieves the depth of the volume's, axis aligned, bounding box.
472  *
473  * In other words; this takes into account what actor's coordinate
474  * space @pv belongs too and conceptually fits an axis aligned box
475  * around the volume. It returns the size of that bounding box as
476  * measured along the z-axis.
477  *
478  * If, for example, clutter_actor_get_transformed_paint_volume()
479  * is used to transform a 2D child actor that is 100px wide, 100px
480  * high and 0px deep into container coordinates then the depth might
481  * not simply be 0px if the child actor has a 3D rotation applied to
482  * it.
483  *
484  * Remember: if clutter_actor_get_transformed_paint_volume() is
485  * used then the transformed volume will be defined relative to the
486  * container actor and in container coordinates a 2D child actor
487  * can have a 3D bounding volume.
488  *
489  * There are no accuracy guarantees for the reported depth,
490  * except that it must always be greater than, or equal to, the actor's
491  * depth. This is because actors may report simple, loose fitting paint
492  * volumes for efficiency.
493  *
494  * Return value: the depth, in units of @pv's local coordinate system.
495  *
496  * Since: 1.6
497  */
498 gfloat
clutter_paint_volume_get_depth(const ClutterPaintVolume * pv)499 clutter_paint_volume_get_depth (const ClutterPaintVolume *pv)
500 {
501   g_return_val_if_fail (pv != NULL, 0.0);
502 
503   if (pv->is_empty)
504     return 0;
505   else if (!pv->is_axis_aligned)
506     {
507       ClutterPaintVolume tmp;
508       float depth;
509       _clutter_paint_volume_copy_static (pv, &tmp);
510       _clutter_paint_volume_axis_align (&tmp);
511       depth = tmp.vertices[4].z - tmp.vertices[0].z;
512       clutter_paint_volume_free (&tmp);
513       return depth;
514     }
515   else
516     return pv->vertices[4].z - pv->vertices[0].z;
517 }
518 
519 /**
520  * clutter_paint_volume_union:
521  * @pv: The first #ClutterPaintVolume and destination for resulting
522  *      union
523  * @another_pv: A second #ClutterPaintVolume to union with @pv
524  *
525  * Updates the geometry of @pv to encompass @pv and @another_pv.
526  *
527  * There are no guarantees about how precisely the two volumes
528  * will be unioned.
529  *
530  * Since: 1.6
531  */
532 void
clutter_paint_volume_union(ClutterPaintVolume * pv,const ClutterPaintVolume * another_pv)533 clutter_paint_volume_union (ClutterPaintVolume *pv,
534                             const ClutterPaintVolume *another_pv)
535 {
536   ClutterPaintVolume aligned_pv;
537 
538   g_return_if_fail (pv != NULL);
539   g_return_if_fail (another_pv != NULL);
540 
541   /* Both volumes have to belong to the same local coordinate space */
542   g_return_if_fail (pv->actor == another_pv->actor);
543 
544   /* NB: we only have to update vertices 0, 1, 3 and 4
545    * (See the ClutterPaintVolume typedef for more details) */
546 
547   /* We special case empty volumes because otherwise we'd end up
548    * calculating a bounding box that would enclose the origin of
549    * the empty volume which isn't desired.
550    */
551   if (another_pv->is_empty)
552     return;
553 
554   if (pv->is_empty)
555     {
556       _clutter_paint_volume_set_from_volume (pv, another_pv);
557       goto done;
558     }
559 
560   if (!pv->is_axis_aligned)
561     _clutter_paint_volume_axis_align (pv);
562 
563   if (!another_pv->is_axis_aligned)
564     {
565       _clutter_paint_volume_copy_static (another_pv, &aligned_pv);
566       _clutter_paint_volume_axis_align (&aligned_pv);
567       another_pv = &aligned_pv;
568     }
569 
570   /* grow left*/
571   /* left vertices 0, 3, 4, 7 */
572   if (another_pv->vertices[0].x < pv->vertices[0].x)
573     {
574       int min_x = another_pv->vertices[0].x;
575       pv->vertices[0].x = min_x;
576       pv->vertices[3].x = min_x;
577       pv->vertices[4].x = min_x;
578       /* pv->vertices[7].x = min_x; */
579     }
580 
581   /* grow right */
582   /* right vertices 1, 2, 5, 6 */
583   if (another_pv->vertices[1].x > pv->vertices[1].x)
584     {
585       int max_x = another_pv->vertices[1].x;
586       pv->vertices[1].x = max_x;
587       /* pv->vertices[2].x = max_x; */
588       /* pv->vertices[5].x = max_x; */
589       /* pv->vertices[6].x = max_x; */
590     }
591 
592   /* grow up */
593   /* top vertices 0, 1, 4, 5 */
594   if (another_pv->vertices[0].y < pv->vertices[0].y)
595     {
596       int min_y = another_pv->vertices[0].y;
597       pv->vertices[0].y = min_y;
598       pv->vertices[1].y = min_y;
599       pv->vertices[4].y = min_y;
600       /* pv->vertices[5].y = min_y; */
601     }
602 
603   /* grow down */
604   /* bottom vertices 2, 3, 6, 7 */
605   if (another_pv->vertices[3].y > pv->vertices[3].y)
606     {
607       int may_y = another_pv->vertices[3].y;
608       /* pv->vertices[2].y = may_y; */
609       pv->vertices[3].y = may_y;
610       /* pv->vertices[6].y = may_y; */
611       /* pv->vertices[7].y = may_y; */
612     }
613 
614   /* grow forward */
615   /* front vertices 0, 1, 2, 3 */
616   if (another_pv->vertices[0].z < pv->vertices[0].z)
617     {
618       int min_z = another_pv->vertices[0].z;
619       pv->vertices[0].z = min_z;
620       pv->vertices[1].z = min_z;
621       /* pv->vertices[2].z = min_z; */
622       pv->vertices[3].z = min_z;
623     }
624 
625   /* grow backward */
626   /* back vertices 4, 5, 6, 7 */
627   if (another_pv->vertices[4].z > pv->vertices[4].z)
628     {
629       int maz_z = another_pv->vertices[4].z;
630       pv->vertices[4].z = maz_z;
631       /* pv->vertices[5].z = maz_z; */
632       /* pv->vertices[6].z = maz_z; */
633       /* pv->vertices[7].z = maz_z; */
634     }
635 
636   if (pv->vertices[4].z == pv->vertices[0].z)
637     pv->is_2d = TRUE;
638   else
639     pv->is_2d = FALSE;
640 
641 done:
642   pv->is_empty = FALSE;
643   pv->is_complete = FALSE;
644 }
645 
646 /**
647  * clutter_paint_volume_union_box:
648  * @pv: a #ClutterPaintVolume
649  * @box: a #ClutterActorBox to union to @pv
650  *
651  * Unions the 2D region represented by @box to a #ClutterPaintVolume.
652  *
653  * This function is similar to clutter_paint_volume_union(), but it is
654  * specific for 2D regions.
655  *
656  * Since: 1.10
657  */
658 void
clutter_paint_volume_union_box(ClutterPaintVolume * pv,const ClutterActorBox * box)659 clutter_paint_volume_union_box (ClutterPaintVolume    *pv,
660                                 const ClutterActorBox *box)
661 {
662   ClutterPaintVolume volume;
663   ClutterVertex origin;
664 
665   g_return_if_fail (pv != NULL);
666   g_return_if_fail (box != NULL);
667 
668   _clutter_paint_volume_init_static (&volume, pv->actor);
669 
670   origin.x = box->x1;
671   origin.y = box->y1;
672   origin.z = 0.f;
673   clutter_paint_volume_set_origin (&volume, &origin);
674   clutter_paint_volume_set_width (&volume, box->x2 - box->x1);
675   clutter_paint_volume_set_height (&volume, box->y2 - box->y1);
676 
677   clutter_paint_volume_union (pv, &volume);
678 
679   clutter_paint_volume_free (&volume);
680 }
681 
682 /* The paint_volume setters only update vertices 0, 1, 3 and
683  * 4 since the others can be drived from them.
684  *
685  * This will set pv->completed = TRUE;
686  */
687 void
_clutter_paint_volume_complete(ClutterPaintVolume * pv)688 _clutter_paint_volume_complete (ClutterPaintVolume *pv)
689 {
690   float dx_l2r, dy_l2r, dz_l2r;
691   float dx_t2b, dy_t2b, dz_t2b;
692 
693   if (pv->is_empty)
694     return;
695 
696   if (pv->is_complete)
697     return;
698 
699   /* Find the vector that takes us from any vertex on the left face to
700    * the corresponding vertex on the right face. */
701   dx_l2r = pv->vertices[1].x - pv->vertices[0].x;
702   dy_l2r = pv->vertices[1].y - pv->vertices[0].y;
703   dz_l2r = pv->vertices[1].z - pv->vertices[0].z;
704 
705   /* Find the vector that takes us from any vertex on the top face to
706    * the corresponding vertex on the bottom face. */
707   dx_t2b = pv->vertices[3].x - pv->vertices[0].x;
708   dy_t2b = pv->vertices[3].y - pv->vertices[0].y;
709   dz_t2b = pv->vertices[3].z - pv->vertices[0].z;
710 
711   /* front-bottom-right */
712   pv->vertices[2].x = pv->vertices[3].x + dx_l2r;
713   pv->vertices[2].y = pv->vertices[3].y + dy_l2r;
714   pv->vertices[2].z = pv->vertices[3].z + dz_l2r;
715 
716   if (G_UNLIKELY (!pv->is_2d))
717     {
718       /* back-top-right */
719       pv->vertices[5].x = pv->vertices[4].x + dx_l2r;
720       pv->vertices[5].y = pv->vertices[4].y + dy_l2r;
721       pv->vertices[5].z = pv->vertices[4].z + dz_l2r;
722 
723       /* back-bottom-right */
724       pv->vertices[6].x = pv->vertices[5].x + dx_t2b;
725       pv->vertices[6].y = pv->vertices[5].y + dy_t2b;
726       pv->vertices[6].z = pv->vertices[5].z + dz_t2b;
727 
728       /* back-bottom-left */
729       pv->vertices[7].x = pv->vertices[4].x + dx_t2b;
730       pv->vertices[7].y = pv->vertices[4].y + dy_t2b;
731       pv->vertices[7].z = pv->vertices[4].z + dz_t2b;
732     }
733 
734   pv->is_complete = TRUE;
735 }
736 
737 /*<private>
738  * _clutter_paint_volume_get_box:
739  * @pv: a #ClutterPaintVolume
740  * @box: a pixel aligned #ClutterActorBox
741  *
742  * Transforms a 3D paint volume into a 2D bounding box in the
743  * same coordinate space as the 3D paint volume.
744  *
745  * To get an actors "paint box" you should first project
746  * the paint volume into window coordinates before getting
747  * the 2D bounding box.
748  *
749  * The coordinates of the returned box are not clamped to
750  * integer pixel values; if you need them to be rounded to the
751  * nearest integer pixel values, you can use the
752  * clutter_actor_box_clamp_to_pixel() function.
753  *
754  * Since: 1.6
755  */
756 void
_clutter_paint_volume_get_bounding_box(ClutterPaintVolume * pv,ClutterActorBox * box)757 _clutter_paint_volume_get_bounding_box (ClutterPaintVolume *pv,
758                                         ClutterActorBox *box)
759 {
760   gfloat x_min, y_min, x_max, y_max;
761   ClutterVertex *vertices;
762   int count;
763   gint i;
764 
765   g_return_if_fail (pv != NULL);
766   g_return_if_fail (box != NULL);
767 
768   if (pv->is_empty)
769     {
770       box->x1 = box->x2 = pv->vertices[0].x;
771       box->y1 = box->y2 = pv->vertices[0].y;
772       return;
773     }
774 
775   /* Updates the vertices we calculate lazily
776    * (See ClutterPaintVolume typedef for more details) */
777   _clutter_paint_volume_complete (pv);
778 
779   vertices = pv->vertices;
780 
781   x_min = x_max = vertices[0].x;
782   y_min = y_max = vertices[0].y;
783 
784   /* Most actors are 2D so we only have to look at the front 4
785    * vertices of the paint volume... */
786   if (G_LIKELY (pv->is_2d))
787     count = 4;
788   else
789     count = 8;
790 
791   for (i = 1; i < count; i++)
792     {
793       if (vertices[i].x < x_min)
794         x_min = vertices[i].x;
795       else if (vertices[i].x > x_max)
796         x_max = vertices[i].x;
797 
798       if (vertices[i].y < y_min)
799         y_min = vertices[i].y;
800       else if (vertices[i].y > y_max)
801         y_max = vertices[i].y;
802     }
803 
804   box->x1 = x_min;
805   box->y1 = y_min;
806   box->x2 = x_max;
807   box->y2 = y_max;
808 }
809 
810 void
_clutter_paint_volume_project(ClutterPaintVolume * pv,const CoglMatrix * modelview,const CoglMatrix * projection,const float * viewport)811 _clutter_paint_volume_project (ClutterPaintVolume *pv,
812                                const CoglMatrix *modelview,
813                                const CoglMatrix *projection,
814                                const float *viewport)
815 {
816   int transform_count;
817 
818   if (pv->is_empty)
819     {
820       /* Just transform the origin... */
821       _clutter_util_fully_transform_vertices (modelview,
822                                               projection,
823                                               viewport,
824                                               pv->vertices,
825                                               pv->vertices,
826                                               1);
827       return;
828     }
829 
830   /* All the vertices must be up to date, since after the projection
831    * it wont be trivial to derive the other vertices. */
832   _clutter_paint_volume_complete (pv);
833 
834   /* Most actors are 2D so we only have to transform the front 4
835    * vertices of the paint volume... */
836   if (G_LIKELY (pv->is_2d))
837     transform_count = 4;
838   else
839     transform_count = 8;
840 
841   _clutter_util_fully_transform_vertices (modelview,
842                                           projection,
843                                           viewport,
844                                           pv->vertices,
845                                           pv->vertices,
846                                           transform_count);
847 
848   pv->is_axis_aligned = FALSE;
849 }
850 
851 void
_clutter_paint_volume_transform(ClutterPaintVolume * pv,const CoglMatrix * matrix)852 _clutter_paint_volume_transform (ClutterPaintVolume *pv,
853                                  const CoglMatrix *matrix)
854 {
855   int transform_count;
856 
857   if (pv->is_empty)
858     {
859       gfloat w = 1;
860       /* Just transform the origin */
861       cogl_matrix_transform_point (matrix,
862                                    &pv->vertices[0].x,
863                                    &pv->vertices[0].y,
864                                    &pv->vertices[0].z,
865                                    &w);
866       return;
867     }
868 
869   /* All the vertices must be up to date, since after the transform
870    * it wont be trivial to derive the other vertices. */
871   _clutter_paint_volume_complete (pv);
872 
873   /* Most actors are 2D so we only have to transform the front 4
874    * vertices of the paint volume... */
875   if (G_LIKELY (pv->is_2d))
876     transform_count = 4;
877   else
878     transform_count = 8;
879 
880   cogl_matrix_transform_points (matrix,
881                                 3,
882                                 sizeof (ClutterVertex),
883                                 pv->vertices,
884                                 sizeof (ClutterVertex),
885                                 pv->vertices,
886                                 transform_count);
887 
888   pv->is_axis_aligned = FALSE;
889 }
890 
891 
892 /* Given a paint volume that has been transformed by an arbitrary
893  * modelview and is no longer axis aligned, this derives a replacement
894  * that is axis aligned. */
895 void
_clutter_paint_volume_axis_align(ClutterPaintVolume * pv)896 _clutter_paint_volume_axis_align (ClutterPaintVolume *pv)
897 {
898   int count;
899   int i;
900   ClutterVertex origin;
901   float max_x;
902   float max_y;
903   float max_z;
904 
905   g_return_if_fail (pv != NULL);
906 
907   if (pv->is_empty)
908     return;
909 
910   if (G_LIKELY (pv->is_axis_aligned))
911     return;
912 
913   if (G_LIKELY (pv->vertices[0].x == pv->vertices[1].x &&
914                 pv->vertices[0].y == pv->vertices[3].y &&
915                 pv->vertices[0].z == pv->vertices[4].z))
916     {
917       pv->is_axis_aligned = TRUE;
918       return;
919     }
920 
921   if (!pv->is_complete)
922     _clutter_paint_volume_complete (pv);
923 
924   origin = pv->vertices[0];
925   max_x = pv->vertices[0].x;
926   max_y = pv->vertices[0].y;
927   max_z = pv->vertices[0].z;
928 
929   count = pv->is_2d ? 4 : 8;
930   for (i = 1; i < count; i++)
931     {
932       if (pv->vertices[i].x < origin.x)
933         origin.x = pv->vertices[i].x;
934       else if (pv->vertices[i].x > max_x)
935         max_x = pv->vertices[i].x;
936 
937       if (pv->vertices[i].y < origin.y)
938         origin.y = pv->vertices[i].y;
939       else if (pv->vertices[i].y > max_y)
940         max_y = pv->vertices[i].y;
941 
942       if (pv->vertices[i].z < origin.z)
943         origin.z = pv->vertices[i].z;
944       else if (pv->vertices[i].z > max_z)
945         max_z = pv->vertices[i].z;
946     }
947 
948   pv->vertices[0] = origin;
949 
950   pv->vertices[1].x = max_x;
951   pv->vertices[1].y = origin.y;
952   pv->vertices[1].z = origin.z;
953 
954   pv->vertices[3].x = origin.x;
955   pv->vertices[3].y = max_y;
956   pv->vertices[3].z = origin.z;
957 
958   pv->vertices[4].x = origin.x;
959   pv->vertices[4].y = origin.y;
960   pv->vertices[4].z = max_z;
961 
962   pv->is_complete = FALSE;
963   pv->is_axis_aligned = TRUE;
964 
965   if (pv->vertices[4].z == pv->vertices[0].z)
966     pv->is_2d = TRUE;
967   else
968     pv->is_2d = FALSE;
969 }
970 
971 /*<private>
972  * _clutter_actor_set_default_paint_volume:
973  * @self: a #ClutterActor
974  * @check_gtype: if not %G_TYPE_INVALID, match the type of @self against
975  *   this type
976  * @volume: the #ClutterPaintVolume to set
977  *
978  * Sets the default paint volume for @self.
979  *
980  * This function should be called by #ClutterActor sub-classes that follow
981  * the default assumption that their paint volume is defined by their
982  * allocation.
983  *
984  * If @check_gtype is not %G_TYPE_INVALID, this function will check the
985  * type of @self and only compute the paint volume if the type matches;
986  * this can be used to avoid computing the paint volume for sub-classes
987  * of an actor class
988  *
989  * Return value: %TRUE if the paint volume was set, and %FALSE otherwise
990  */
991 gboolean
_clutter_actor_set_default_paint_volume(ClutterActor * self,GType check_gtype,ClutterPaintVolume * volume)992 _clutter_actor_set_default_paint_volume (ClutterActor       *self,
993                                          GType               check_gtype,
994                                          ClutterPaintVolume *volume)
995 {
996   ClutterActorBox box;
997 
998   if (check_gtype != G_TYPE_INVALID)
999     {
1000       if (G_OBJECT_TYPE (self) != check_gtype)
1001         return FALSE;
1002     }
1003 
1004   /* calling clutter_actor_get_allocation_* can potentially be very
1005    * expensive, as it can result in a synchronous full stage relayout
1006    * and redraw
1007    */
1008   if (!clutter_actor_has_allocation (self))
1009     return FALSE;
1010 
1011   clutter_actor_get_allocation_box (self, &box);
1012 
1013   /* we only set the width and height, as the paint volume is defined
1014    * to be relative to the actor's modelview, which means that the
1015    * allocation's origin has already been applied
1016    */
1017   clutter_paint_volume_set_width (volume, box.x2 - box.x1);
1018   clutter_paint_volume_set_height (volume, box.y2 - box.y1);
1019 
1020   return TRUE;
1021 }
1022 
1023 /**
1024  * clutter_paint_volume_set_from_allocation:
1025  * @pv: a #ClutterPaintVolume
1026  * @actor: a #ClutterActor
1027  *
1028  * Sets the #ClutterPaintVolume from the allocation of @actor.
1029  *
1030  * This function should be used when overriding the
1031  * #ClutterActorClass.get_paint_volume() by #ClutterActor sub-classes
1032  * that do not paint outside their allocation.
1033  *
1034  * A typical example is:
1035  *
1036  * |[
1037  * static gboolean
1038  * my_actor_get_paint_volume (ClutterActor       *self,
1039  *                            ClutterPaintVolume *volume)
1040  * {
1041  *   return clutter_paint_volume_set_from_allocation (volume, self);
1042  * }
1043  * ]|
1044  *
1045  * Return value: %TRUE if the paint volume was successfully set, and %FALSE
1046  *   otherwise
1047  *
1048  * Since: 1.6
1049  */
1050 gboolean
clutter_paint_volume_set_from_allocation(ClutterPaintVolume * pv,ClutterActor * actor)1051 clutter_paint_volume_set_from_allocation (ClutterPaintVolume *pv,
1052                                           ClutterActor       *actor)
1053 {
1054   g_return_val_if_fail (pv != NULL, FALSE);
1055   g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE);
1056 
1057   return _clutter_actor_set_default_paint_volume (actor, G_TYPE_INVALID, pv);
1058 }
1059 
1060 /* Currently paint volumes are defined relative to a given actor, but
1061  * in some cases it is desireable to be able to change the actor that
1062  * a volume relates too (For instance for ClutterClone actors where we
1063  * need to masquarade the source actors volume as the volume for the
1064  * clone). */
1065 void
_clutter_paint_volume_set_reference_actor(ClutterPaintVolume * pv,ClutterActor * actor)1066 _clutter_paint_volume_set_reference_actor (ClutterPaintVolume *pv,
1067                                            ClutterActor       *actor)
1068 {
1069   g_return_if_fail (pv != NULL);
1070 
1071   pv->actor = actor;
1072 }
1073 
1074 ClutterCullResult
_clutter_paint_volume_cull(ClutterPaintVolume * pv,const ClutterPlane * planes)1075 _clutter_paint_volume_cull (ClutterPaintVolume *pv,
1076                             const ClutterPlane *planes)
1077 {
1078   int vertex_count;
1079   ClutterVertex *vertices = pv->vertices;
1080   gboolean partial = FALSE;
1081   int i;
1082   int j;
1083 
1084   if (pv->is_empty)
1085     return CLUTTER_CULL_RESULT_OUT;
1086 
1087   /* We expect the volume to already be transformed into eye coordinates
1088    */
1089   g_return_val_if_fail (pv->is_complete == TRUE, CLUTTER_CULL_RESULT_IN);
1090   g_return_val_if_fail (pv->actor == NULL, CLUTTER_CULL_RESULT_IN);
1091 
1092   /* Most actors are 2D so we only have to transform the front 4
1093    * vertices of the paint volume... */
1094   if (G_LIKELY (pv->is_2d))
1095     vertex_count = 4;
1096   else
1097     vertex_count = 8;
1098 
1099   for (i = 0; i < 4; i++)
1100     {
1101       int out = 0;
1102       for (j = 0; j < vertex_count; j++)
1103         {
1104           ClutterVertex p;
1105           float distance;
1106 
1107           /* XXX: for perspective projections this can be optimized
1108            * out because all the planes should pass through the origin
1109            * so (0,0,0) is a valid v0. */
1110           p.x = vertices[j].x - planes[i].v0[0];
1111           p.y = vertices[j].y - planes[i].v0[1];
1112           p.z = vertices[j].z - planes[i].v0[2];
1113 
1114           distance = (planes[i].n[0] * p.x +
1115                       planes[i].n[1] * p.y +
1116                       planes[i].n[2] * p.z);
1117 
1118           if (distance < 0)
1119             out++;
1120         }
1121 
1122       if (out == vertex_count)
1123         return CLUTTER_CULL_RESULT_OUT;
1124       else if (out != 0)
1125         partial = TRUE;
1126     }
1127 
1128   if (partial)
1129     return CLUTTER_CULL_RESULT_PARTIAL;
1130   else
1131     return CLUTTER_CULL_RESULT_IN;
1132 }
1133 
1134 void
_clutter_paint_volume_get_stage_paint_box(ClutterPaintVolume * pv,ClutterStage * stage,ClutterActorBox * box)1135 _clutter_paint_volume_get_stage_paint_box (ClutterPaintVolume *pv,
1136                                            ClutterStage *stage,
1137                                            ClutterActorBox *box)
1138 {
1139   ClutterPaintVolume projected_pv;
1140   CoglMatrix modelview;
1141   CoglMatrix projection;
1142   float viewport[4];
1143   float width;
1144   float height;
1145 
1146   _clutter_paint_volume_copy_static (pv, &projected_pv);
1147 
1148   cogl_matrix_init_identity (&modelview);
1149 
1150   /* If the paint volume isn't already in eye coordinates... */
1151   if (pv->actor)
1152     _clutter_actor_apply_relative_transformation_matrix (pv->actor, NULL,
1153                                                          &modelview);
1154 
1155   _clutter_stage_get_projection_matrix (stage, &projection);
1156   _clutter_stage_get_viewport (stage,
1157                                &viewport[0],
1158                                &viewport[1],
1159                                &viewport[2],
1160                                &viewport[3]);
1161 
1162   _clutter_paint_volume_project (&projected_pv,
1163                                  &modelview,
1164                                  &projection,
1165                                  viewport);
1166 
1167   _clutter_paint_volume_get_bounding_box (&projected_pv, box);
1168 
1169   /* The aim here is that for a given rectangle defined with floating point
1170    * coordinates we want to determine a stable quantized size in pixels
1171    * that doesn't vary due to the original box's sub-pixel position.
1172    *
1173    * The reason this is important is because effects will use this
1174    * API to determine the size of offscreen framebuffers and so for
1175    * a fixed-size object that may be animated accross the screen we
1176    * want to make sure that the stage paint-box has an equally stable
1177    * size so that effects aren't made to continuously re-allocate
1178    * a corresponding fbo.
1179    *
1180    * The other thing we consider is that the calculation of this box is
1181    * subject to floating point precision issues that might be slightly
1182    * different to the precision issues involved with actually painting the
1183    * actor, which might result in painting slightly leaking outside the
1184    * user's calculated paint-volume. For this we simply aim to pad out the
1185    * paint-volume by at least half a pixel all the way around.
1186    */
1187   width = box->x2 - box->x1;
1188   height = box->y2 - box->y1;
1189   width = CLUTTER_NEARBYINT (width);
1190   height = CLUTTER_NEARBYINT (height);
1191   /* XXX: NB the width/height may now be up to 0.5px too small so we
1192    * must also pad by 0.25px all around to account for this. In total we
1193    * must padd by at least 0.75px around all sides. */
1194 
1195   /* XXX: The furthest that we can overshoot the bottom right corner by
1196    * here is 1.75px in total if you consider that the 0.75 padding could
1197    * just cross an integer boundary and so ceil will effectively add 1.
1198    */
1199   box->x2 = ceilf (box->x2 + 0.75);
1200   box->y2 = ceilf (box->y2 + 0.75);
1201 
1202   /* Now we redefine the top-left relative to the bottom right based on the
1203    * rounded width/height determined above + a constant so that the overall
1204    * size of the box will be stable and not dependant on the box's
1205    * position.
1206    *
1207    * Adding 3px to the width/height will ensure we cover the maximum of
1208    * 1.75px padding on the bottom/right and still ensure we have > 0.75px
1209    * padding on the top/left.
1210    */
1211   box->x1 = box->x2 - width - 3;
1212   box->y1 = box->y2 - height - 3;
1213 
1214   clutter_paint_volume_free (&projected_pv);
1215 }
1216 
1217 void
_clutter_paint_volume_transform_relative(ClutterPaintVolume * pv,ClutterActor * relative_to_ancestor)1218 _clutter_paint_volume_transform_relative (ClutterPaintVolume *pv,
1219                                           ClutterActor *relative_to_ancestor)
1220 {
1221   CoglMatrix matrix;
1222   ClutterActor *actor;
1223 
1224   actor = pv->actor;
1225 
1226   g_return_if_fail (actor != NULL);
1227 
1228   _clutter_paint_volume_set_reference_actor (pv, relative_to_ancestor);
1229 
1230   cogl_matrix_init_identity (&matrix);
1231   _clutter_actor_apply_relative_transformation_matrix (actor,
1232                                                        relative_to_ancestor,
1233                                                       &matrix);
1234 
1235   _clutter_paint_volume_transform (pv, &matrix);
1236 }
1237