Release Clutter 1.11.4 (snapshot)
[profile/ivi/clutter.git] / clutter / clutter-paint-volume.c
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 *
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
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
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 *
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
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
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
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
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
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
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  * <note><para>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.</para>
298  * <para>Remember; after clutter_actor_get_transformed_paint_volume() is
299  * used then a transformed child volume will be defined relative to the
300  * ancestor container actor and so a 2D child actor
301  * can have a 3D bounding volume.</para></note>
302  *
303  * <note>There are no accuracy guarantees for the reported width,
304  * except that it must always be >= to the true width. This is
305  * because actors may report simple, loose fitting paint-volumes
306  * for efficiency</note>
307
308  * Return value: the width, in units of @pv's local coordinate system.
309  *
310  * Since: 1.6
311  */
312 gfloat
313 clutter_paint_volume_get_width (const ClutterPaintVolume *pv)
314 {
315   g_return_val_if_fail (pv != NULL, 0.0);
316
317   if (pv->is_empty)
318     return 0;
319   else if (!pv->is_axis_aligned)
320     {
321       ClutterPaintVolume tmp;
322       float width;
323       _clutter_paint_volume_copy_static (pv, &tmp);
324       _clutter_paint_volume_axis_align (&tmp);
325       width = tmp.vertices[1].x - tmp.vertices[0].x;
326       clutter_paint_volume_free (&tmp);
327       return width;
328     }
329   else
330     return pv->vertices[1].x - pv->vertices[0].x;
331 }
332
333 /**
334  * clutter_paint_volume_set_height:
335  * @pv: a #ClutterPaintVolume
336  * @height: the height of the paint volume, in pixels
337  *
338  * Sets the height of the paint volume. The height is measured along
339  * the y axis in the actor coordinates that @pv is associated with.
340  *
341  * Since: 1.6
342  */
343 void
344 clutter_paint_volume_set_height (ClutterPaintVolume *pv,
345                                  gfloat              height)
346 {
347   gfloat height_ypos;
348
349   g_return_if_fail (pv != NULL);
350   g_return_if_fail (height >= 0.0f);
351
352   /* If the volume is currently empty then only the origin is
353    * currently valid */
354   if (pv->is_empty)
355     pv->vertices[1] = pv->vertices[3] = pv->vertices[4] = pv->vertices[0];
356
357   if (!pv->is_axis_aligned)
358     _clutter_paint_volume_axis_align (pv);
359
360   height_ypos = pv->vertices[0].y + height;
361
362   /* Move the bottom vertices of the paint box relative to the
363    * origin... */
364   /* pv->vertices[2].y = height_ypos; NB: updated lazily */
365   pv->vertices[3].y = height_ypos;
366   /* pv->vertices[6].y = height_ypos; NB: updated lazily */
367   /* pv->vertices[7].y = height_ypos; NB: updated lazily */
368   pv->is_complete = FALSE;
369
370   _clutter_paint_volume_update_is_empty (pv);
371 }
372
373 /**
374  * clutter_paint_volume_get_height:
375  * @pv: a #ClutterPaintVolume
376  *
377  * Retrieves the height of the volume's, axis aligned, bounding box.
378  *
379  * In other words; this takes into account what actor's coordinate
380  * space @pv belongs too and conceptually fits an axis aligned box
381  * around the volume. It returns the size of that bounding box as
382  * measured along the y-axis.
383  *
384  * <note><para>If, for example, clutter_actor_get_transformed_paint_volume()
385  * is used to transform a 2D child actor that is 100px wide, 100px
386  * high and 0px deep into container coordinates then the height might
387  * not simply be 100px if the child actor has a 3D rotation applied to
388  * it.</para>
389  * <para>Remember; after clutter_actor_get_transformed_paint_volume() is
390  * used then a transformed child volume will be defined relative to the
391  * ancestor container actor and so a 2D child actor
392  * can have a 3D bounding volume.</para></note>
393  *
394  * <note>There are no accuracy guarantees for the reported height,
395  * except that it must always be >= to the true height. This is
396  * because actors may report simple, loose fitting paint-volumes
397  * for efficiency</note>
398  *
399  * Return value: the height, in units of @pv's local coordinate system.
400  *
401  * Since: 1.6
402  */
403 gfloat
404 clutter_paint_volume_get_height (const ClutterPaintVolume *pv)
405 {
406   g_return_val_if_fail (pv != NULL, 0.0);
407
408   if (pv->is_empty)
409     return 0;
410   else if (!pv->is_axis_aligned)
411     {
412       ClutterPaintVolume tmp;
413       float height;
414       _clutter_paint_volume_copy_static (pv, &tmp);
415       _clutter_paint_volume_axis_align (&tmp);
416       height = tmp.vertices[3].y - tmp.vertices[0].y;
417       clutter_paint_volume_free (&tmp);
418       return height;
419     }
420   else
421     return pv->vertices[3].y - pv->vertices[0].y;
422 }
423
424 /**
425  * clutter_paint_volume_set_depth:
426  * @pv: a #ClutterPaintVolume
427  * @depth: the depth of the paint volume, in pixels
428  *
429  * Sets the depth of the paint volume. The depth is measured along
430  * the z axis in the actor coordinates that @pv is associated with.
431  *
432  * Since: 1.6
433  */
434 void
435 clutter_paint_volume_set_depth (ClutterPaintVolume *pv,
436                                 gfloat              depth)
437 {
438   gfloat depth_zpos;
439
440   g_return_if_fail (pv != NULL);
441   g_return_if_fail (depth >= 0.0f);
442
443   /* If the volume is currently empty then only the origin is
444    * currently valid */
445   if (pv->is_empty)
446     pv->vertices[1] = pv->vertices[3] = pv->vertices[4] = pv->vertices[0];
447
448   if (!pv->is_axis_aligned)
449     _clutter_paint_volume_axis_align (pv);
450
451   depth_zpos = pv->vertices[0].z + depth;
452
453   /* Move the back vertices of the paint box relative to the
454    * origin... */
455   pv->vertices[4].z = depth_zpos;
456   /* pv->vertices[5].z = depth_zpos; NB: updated lazily */
457   /* pv->vertices[6].z = depth_zpos; NB: updated lazily */
458   /* pv->vertices[7].z = depth_zpos; NB: updated lazily */
459
460   pv->is_complete = FALSE;
461   pv->is_2d = depth ? FALSE : TRUE;
462   _clutter_paint_volume_update_is_empty (pv);
463 }
464
465 /**
466  * clutter_paint_volume_get_depth:
467  * @pv: a #ClutterPaintVolume
468  *
469  * Retrieves the depth of the volume's, axis aligned, bounding box.
470  *
471  * In other words; this takes into account what actor's coordinate
472  * space @pv belongs too and conceptually fits an axis aligned box
473  * around the volume. It returns the size of that bounding box as
474  * measured along the z-axis.
475  *
476  * <note><para>If, for example, clutter_actor_get_transformed_paint_volume()
477  * is used to transform a 2D child actor that is 100px wide, 100px
478  * high and 0px deep into container coordinates then the depth might
479  * not simply be 0px if the child actor has a 3D rotation applied to
480  * it.</para>
481  * <para>Remember; after clutter_actor_get_transformed_paint_volume() is
482  * used then the transformed volume will be defined relative to the
483  * container actor and in container coordinates a 2D child actor
484  * can have a 3D bounding volume.</para></note>
485  *
486  * <note>There are no accuracy guarantees for the reported depth,
487  * except that it must always be >= to the true depth. This is
488  * because actors may report simple, loose fitting paint-volumes
489  * for efficiency.</note>
490  *
491  * Return value: the depth, in units of @pv's local coordinate system.
492  *
493  * Since: 1.6
494  */
495 gfloat
496 clutter_paint_volume_get_depth (const ClutterPaintVolume *pv)
497 {
498   g_return_val_if_fail (pv != NULL, 0.0);
499
500   if (pv->is_empty)
501     return 0;
502   else if (!pv->is_axis_aligned)
503     {
504       ClutterPaintVolume tmp;
505       float depth;
506       _clutter_paint_volume_copy_static (pv, &tmp);
507       _clutter_paint_volume_axis_align (&tmp);
508       depth = tmp.vertices[4].z - tmp.vertices[0].z;
509       clutter_paint_volume_free (&tmp);
510       return depth;
511     }
512   else
513     return pv->vertices[4].z - pv->vertices[0].z;
514 }
515
516 /**
517  * clutter_paint_volume_union:
518  * @pv: The first #ClutterPaintVolume and destination for resulting
519  *      union
520  * @another_pv: A second #ClutterPaintVolume to union with @pv
521  *
522  * Updates the geometry of @pv to encompass @pv and @another_pv.
523  *
524  * <note>There are no guarantees about how precisely the two volumes
525  * will be encompassed.</note>
526  *
527  * Since: 1.6
528  */
529 void
530 clutter_paint_volume_union (ClutterPaintVolume *pv,
531                             const ClutterPaintVolume *another_pv)
532 {
533   ClutterPaintVolume aligned_pv;
534
535   g_return_if_fail (pv != NULL);
536   g_return_if_fail (another_pv != NULL);
537
538   /* Both volumes have to belong to the same local coordinate space */
539   g_return_if_fail (pv->actor == another_pv->actor);
540
541   /* NB: we only have to update vertices 0, 1, 3 and 4
542    * (See the ClutterPaintVolume typedef for more details) */
543
544   /* We special case empty volumes because otherwise we'd end up
545    * calculating a bounding box that would enclose the origin of
546    * the empty volume which isn't desired.
547    */
548   if (another_pv->is_empty)
549     return;
550
551   if (pv->is_empty)
552     {
553       _clutter_paint_volume_set_from_volume (pv, another_pv);
554       goto done;
555     }
556
557   if (!pv->is_axis_aligned)
558     _clutter_paint_volume_axis_align (pv);
559
560   if (!another_pv->is_axis_aligned)
561     {
562       _clutter_paint_volume_copy_static (another_pv, &aligned_pv);
563       _clutter_paint_volume_axis_align (&aligned_pv);
564       another_pv = &aligned_pv;
565     }
566
567   /* grow left*/
568   /* left vertices 0, 3, 4, 7 */
569   if (another_pv->vertices[0].x < pv->vertices[0].x)
570     {
571       int min_x = another_pv->vertices[0].x;
572       pv->vertices[0].x = min_x;
573       pv->vertices[3].x = min_x;
574       pv->vertices[4].x = min_x;
575       /* pv->vertices[7].x = min_x; */
576     }
577
578   /* grow right */
579   /* right vertices 1, 2, 5, 6 */
580   if (another_pv->vertices[1].x > pv->vertices[1].x)
581     {
582       int max_x = another_pv->vertices[1].x;
583       pv->vertices[1].x = max_x;
584       /* pv->vertices[2].x = max_x; */
585       /* pv->vertices[5].x = max_x; */
586       /* pv->vertices[6].x = max_x; */
587     }
588
589   /* grow up */
590   /* top vertices 0, 1, 4, 5 */
591   if (another_pv->vertices[0].y < pv->vertices[0].y)
592     {
593       int min_y = another_pv->vertices[0].y;
594       pv->vertices[0].y = min_y;
595       pv->vertices[1].y = min_y;
596       pv->vertices[4].y = min_y;
597       /* pv->vertices[5].y = min_y; */
598     }
599
600   /* grow down */
601   /* bottom vertices 2, 3, 6, 7 */
602   if (another_pv->vertices[3].y > pv->vertices[3].y)
603     {
604       int may_y = another_pv->vertices[3].y;
605       /* pv->vertices[2].y = may_y; */
606       pv->vertices[3].y = may_y;
607       /* pv->vertices[6].y = may_y; */
608       /* pv->vertices[7].y = may_y; */
609     }
610
611   /* grow forward */
612   /* front vertices 0, 1, 2, 3 */
613   if (another_pv->vertices[0].z < pv->vertices[0].z)
614     {
615       int min_z = another_pv->vertices[0].z;
616       pv->vertices[0].z = min_z;
617       pv->vertices[1].z = min_z;
618       /* pv->vertices[2].z = min_z; */
619       pv->vertices[3].z = min_z;
620     }
621
622   /* grow backward */
623   /* back vertices 4, 5, 6, 7 */
624   if (another_pv->vertices[4].z > pv->vertices[4].z)
625     {
626       int maz_z = another_pv->vertices[4].z;
627       pv->vertices[4].z = maz_z;
628       /* pv->vertices[5].z = maz_z; */
629       /* pv->vertices[6].z = maz_z; */
630       /* pv->vertices[7].z = maz_z; */
631     }
632
633   if (pv->vertices[4].z == pv->vertices[0].z)
634     pv->is_2d = TRUE;
635   else
636     pv->is_2d = FALSE;
637
638 done:
639   pv->is_empty = FALSE;
640   pv->is_complete = FALSE;
641 }
642
643 /**
644  * clutter_paint_volume_union_box:
645  * @pv: a #ClutterPaintVolume
646  * @box: a #ClutterActorBox to union to @pv
647  *
648  * Unions the 2D region represented by @box to a #ClutterPaintVolume.
649  *
650  * This function is similar to clutter_paint_volume_union(), but it is
651  * specific for 2D regions.
652  *
653  * Since: 1.10
654  */
655 void
656 clutter_paint_volume_union_box (ClutterPaintVolume    *pv,
657                                 const ClutterActorBox *box)
658 {
659   ClutterPaintVolume volume;
660   ClutterVertex origin;
661
662   g_return_if_fail (pv != NULL);
663   g_return_if_fail (box != NULL);
664
665   _clutter_paint_volume_init_static (&volume, pv->actor);
666
667   origin.x = box->x1;
668   origin.y = box->y1;
669   origin.z = 0.f;
670   clutter_paint_volume_set_origin (&volume, &origin);
671   clutter_paint_volume_set_width (&volume, box->x2 - box->x1);
672   clutter_paint_volume_set_height (&volume, box->y2 - box->y1);
673
674   clutter_paint_volume_union (pv, &volume);
675
676   clutter_paint_volume_free (&volume);
677 }
678
679 /* The paint_volume setters only update vertices 0, 1, 3 and
680  * 4 since the others can be drived from them.
681  *
682  * This will set pv->completed = TRUE;
683  */
684 void
685 _clutter_paint_volume_complete (ClutterPaintVolume *pv)
686 {
687   float dx_l2r, dy_l2r, dz_l2r;
688   float dx_t2b, dy_t2b, dz_t2b;
689
690   if (pv->is_empty)
691     return;
692
693   if (pv->is_complete)
694     return;
695
696   /* Find the vector that takes us from any vertex on the left face to
697    * the corresponding vertex on the right face. */
698   dx_l2r = pv->vertices[1].x - pv->vertices[0].x;
699   dy_l2r = pv->vertices[1].y - pv->vertices[0].y;
700   dz_l2r = pv->vertices[1].z - pv->vertices[0].z;
701
702   /* Find the vector that takes us from any vertex on the top face to
703    * the corresponding vertex on the bottom face. */
704   dx_t2b = pv->vertices[3].x - pv->vertices[0].x;
705   dy_t2b = pv->vertices[3].y - pv->vertices[0].y;
706   dz_t2b = pv->vertices[3].z - pv->vertices[0].z;
707
708   /* front-bottom-right */
709   pv->vertices[2].x = pv->vertices[3].x + dx_l2r;
710   pv->vertices[2].y = pv->vertices[3].y + dy_l2r;
711   pv->vertices[2].z = pv->vertices[3].z + dz_l2r;
712
713   if (G_UNLIKELY (!pv->is_2d))
714     {
715       /* back-top-right */
716       pv->vertices[5].x = pv->vertices[4].x + dx_l2r;
717       pv->vertices[5].y = pv->vertices[4].y + dy_l2r;
718       pv->vertices[5].z = pv->vertices[4].z + dz_l2r;
719
720       /* back-bottom-right */
721       pv->vertices[6].x = pv->vertices[5].x + dx_t2b;
722       pv->vertices[6].y = pv->vertices[5].y + dy_t2b;
723       pv->vertices[6].z = pv->vertices[5].z + dz_t2b;
724
725       /* back-bottom-left */
726       pv->vertices[7].x = pv->vertices[4].x + dx_t2b;
727       pv->vertices[7].y = pv->vertices[4].y + dy_t2b;
728       pv->vertices[7].z = pv->vertices[4].z + dz_t2b;
729     }
730
731   pv->is_complete = TRUE;
732 }
733
734 /*<private>
735  * _clutter_paint_volume_get_box:
736  * @pv: a #ClutterPaintVolume
737  * @box: a pixel aligned #ClutterGeometry
738  *
739  * Transforms a 3D paint volume into a 2D bounding box in the
740  * same coordinate space as the 3D paint volume.
741  *
742  * To get an actors "paint box" you should first project
743  * the paint volume into window coordinates before getting
744  * the 2D bounding box.
745  *
746  * <note>The coordinates of the returned box are not clamped to
747  * integer pixel values, if you need them to be clamped you can use
748  * clutter_actor_box_clamp_to_pixel()</note>
749  *
750  * Since: 1.6
751  */
752 void
753 _clutter_paint_volume_get_bounding_box (ClutterPaintVolume *pv,
754                                         ClutterActorBox *box)
755 {
756   gfloat x_min, y_min, x_max, y_max;
757   ClutterVertex *vertices;
758   int count;
759   gint i;
760
761   g_return_if_fail (pv != NULL);
762   g_return_if_fail (box != NULL);
763
764   if (pv->is_empty)
765     {
766       box->x1 = box->x2 = pv->vertices[0].x;
767       box->y1 = box->y2 = pv->vertices[0].y;
768       return;
769     }
770
771   /* Updates the vertices we calculate lazily
772    * (See ClutterPaintVolume typedef for more details) */
773   _clutter_paint_volume_complete (pv);
774
775   vertices = pv->vertices;
776
777   x_min = x_max = vertices[0].x;
778   y_min = y_max = vertices[0].y;
779
780   /* Most actors are 2D so we only have to look at the front 4
781    * vertices of the paint volume... */
782   if (G_LIKELY (pv->is_2d))
783     count = 4;
784   else
785     count = 8;
786
787   for (i = 1; i < count; i++)
788     {
789       if (vertices[i].x < x_min)
790         x_min = vertices[i].x;
791       else if (vertices[i].x > x_max)
792         x_max = vertices[i].x;
793
794       if (vertices[i].y < y_min)
795         y_min = vertices[i].y;
796       else if (vertices[i].y > y_max)
797         y_max = vertices[i].y;
798     }
799
800   box->x1 = x_min;
801   box->y1 = y_min;
802   box->x2 = x_max;
803   box->y2 = y_max;
804 }
805
806 void
807 _clutter_paint_volume_project (ClutterPaintVolume *pv,
808                                const CoglMatrix *modelview,
809                                const CoglMatrix *projection,
810                                const float *viewport)
811 {
812   int transform_count;
813
814   if (pv->is_empty)
815     {
816       /* Just transform the origin... */
817       _clutter_util_fully_transform_vertices (modelview,
818                                               projection,
819                                               viewport,
820                                               pv->vertices,
821                                               pv->vertices,
822                                               1);
823       return;
824     }
825
826   /* All the vertices must be up to date, since after the projection
827    * it wont be trivial to derive the other vertices. */
828   _clutter_paint_volume_complete (pv);
829
830   /* Most actors are 2D so we only have to transform the front 4
831    * vertices of the paint volume... */
832   if (G_LIKELY (pv->is_2d))
833     transform_count = 4;
834   else
835     transform_count = 8;
836
837   _clutter_util_fully_transform_vertices (modelview,
838                                           projection,
839                                           viewport,
840                                           pv->vertices,
841                                           pv->vertices,
842                                           transform_count);
843
844   pv->is_axis_aligned = FALSE;
845 }
846
847 void
848 _clutter_paint_volume_transform (ClutterPaintVolume *pv,
849                                  const CoglMatrix *matrix)
850 {
851   int transform_count;
852
853   if (pv->is_empty)
854     {
855       gfloat w = 1;
856       /* Just transform the origin */
857       cogl_matrix_transform_point (matrix,
858                                    &pv->vertices[0].x,
859                                    &pv->vertices[0].y,
860                                    &pv->vertices[0].z,
861                                    &w);
862       return;
863     }
864
865   /* All the vertices must be up to date, since after the transform
866    * it wont be trivial to derive the other vertices. */
867   _clutter_paint_volume_complete (pv);
868
869   /* Most actors are 2D so we only have to transform the front 4
870    * vertices of the paint volume... */
871   if (G_LIKELY (pv->is_2d))
872     transform_count = 4;
873   else
874     transform_count = 8;
875
876   cogl_matrix_transform_points (matrix,
877                                 3,
878                                 sizeof (ClutterVertex),
879                                 pv->vertices,
880                                 sizeof (ClutterVertex),
881                                 pv->vertices,
882                                 transform_count);
883
884   pv->is_axis_aligned = FALSE;
885 }
886
887
888 /* Given a paint volume that has been transformed by an arbitrary
889  * modelview and is no longer axis aligned, this derives a replacement
890  * that is axis aligned. */
891 void
892 _clutter_paint_volume_axis_align (ClutterPaintVolume *pv)
893 {
894   int count;
895   int i;
896   ClutterVertex origin;
897   float max_x;
898   float max_y;
899   float max_z;
900
901   g_return_if_fail (pv != NULL);
902
903   if (pv->is_empty)
904     return;
905
906   if (G_LIKELY (pv->is_axis_aligned))
907     return;
908
909   if (G_LIKELY (pv->vertices[0].x == pv->vertices[1].x &&
910                 pv->vertices[0].y == pv->vertices[3].y &&
911                 pv->vertices[0].z == pv->vertices[4].z))
912     {
913       pv->is_axis_aligned = TRUE;
914       return;
915     }
916
917   if (!pv->is_complete)
918     _clutter_paint_volume_complete (pv);
919
920   origin = pv->vertices[0];
921   max_x = pv->vertices[0].x;
922   max_y = pv->vertices[0].y;
923   max_z = pv->vertices[0].z;
924
925   count = pv->is_2d ? 4 : 8;
926   for (i = 1; i < count; i++)
927     {
928       if (pv->vertices[i].x < origin.x)
929         origin.x = pv->vertices[i].x;
930       else if (pv->vertices[i].x > max_x)
931         max_x = pv->vertices[i].x;
932
933       if (pv->vertices[i].y < origin.y)
934         origin.y = pv->vertices[i].y;
935       else if (pv->vertices[i].y > max_y)
936         max_y = pv->vertices[i].y;
937
938       if (pv->vertices[i].z < origin.z)
939         origin.z = pv->vertices[i].z;
940       else if (pv->vertices[i].z > max_z)
941         max_z = pv->vertices[i].z;
942     }
943
944   pv->vertices[0] = origin;
945
946   pv->vertices[1].x = max_x;
947   pv->vertices[1].y = origin.y;
948   pv->vertices[1].z = origin.z;
949
950   pv->vertices[3].x = origin.x;
951   pv->vertices[3].y = max_y;
952   pv->vertices[3].z = origin.z;
953
954   pv->vertices[4].x = origin.x;
955   pv->vertices[4].y = origin.y;
956   pv->vertices[4].z = max_z;
957
958   pv->is_complete = FALSE;
959   pv->is_axis_aligned = TRUE;
960
961   if (pv->vertices[4].z == pv->vertices[0].z)
962     pv->is_2d = TRUE;
963   else
964     pv->is_2d = FALSE;
965 }
966
967 /*<private>
968  * _clutter_actor_set_default_paint_volume:
969  * @self: a #ClutterActor
970  * @check_gtype: if not %G_TYPE_INVALID, match the type of @self against
971  *   this type
972  * @volume: the #ClutterPaintVolume to set
973  *
974  * Sets the default paint volume for @self.
975  *
976  * This function should be called by #ClutterActor sub-classes that follow
977  * the default assumption that their paint volume is defined by their
978  * allocation.
979  *
980  * If @check_gtype is not %G_TYPE_INVALID, this function will check the
981  * type of @self and only compute the paint volume if the type matches;
982  * this can be used to avoid computing the paint volume for sub-classes
983  * of an actor class
984  *
985  * Return value: %TRUE if the paint volume was set, and %FALSE otherwise
986  */
987 gboolean
988 _clutter_actor_set_default_paint_volume (ClutterActor       *self,
989                                          GType               check_gtype,
990                                          ClutterPaintVolume *volume)
991 {
992   ClutterActorBox box;
993
994   if (check_gtype != G_TYPE_INVALID)
995     {
996       if (G_OBJECT_TYPE (self) != check_gtype)
997         return FALSE;
998     }
999
1000   /* calling clutter_actor_get_allocation_* can potentially be very
1001    * expensive, as it can result in a synchronous full stage relayout
1002    * and redraw
1003    */
1004   if (!clutter_actor_has_allocation (self))
1005     return FALSE;
1006
1007   clutter_actor_get_allocation_box (self, &box);
1008
1009   /* we only set the width and height, as the paint volume is defined
1010    * to be relative to the actor's modelview, which means that the
1011    * allocation's origin has already been applied
1012    */
1013   clutter_paint_volume_set_width (volume, box.x2 - box.x1);
1014   clutter_paint_volume_set_height (volume, box.y2 - box.y1);
1015
1016   return TRUE;
1017 }
1018
1019 /**
1020  * clutter_paint_volume_set_from_allocation:
1021  * @pv: a #ClutterPaintVolume
1022  * @actor: a #ClutterActor
1023  *
1024  * Sets the #ClutterPaintVolume from the allocation of @actor.
1025  *
1026  * This function should be used when overriding the
1027  * #ClutterActorClass.get_paint_volume() by #ClutterActor sub-classes
1028  * that do not paint outside their allocation.
1029  *
1030  * A typical example is:
1031  *
1032  * |[
1033  * static gboolean
1034  * my_actor_get_paint_volume (ClutterActor       *self,
1035  *                            ClutterPaintVolume *volume)
1036  * {
1037  *   return clutter_paint_volume_set_from_allocation (volume, self);
1038  * }
1039  * ]|
1040  *
1041  * Return value: %TRUE if the paint volume was successfully set, and %FALSE
1042  *   otherwise
1043  *
1044  * Since: 1.6
1045  */
1046 gboolean
1047 clutter_paint_volume_set_from_allocation (ClutterPaintVolume *pv,
1048                                           ClutterActor       *actor)
1049 {
1050   g_return_val_if_fail (pv != NULL, FALSE);
1051   g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE);
1052
1053   return _clutter_actor_set_default_paint_volume (actor, G_TYPE_INVALID, pv);
1054 }
1055
1056 /* Currently paint volumes are defined relative to a given actor, but
1057  * in some cases it is desireable to be able to change the actor that
1058  * a volume relates too (For instance for ClutterClone actors where we
1059  * need to masquarade the source actors volume as the volume for the
1060  * clone). */
1061 void
1062 _clutter_paint_volume_set_reference_actor (ClutterPaintVolume *pv,
1063                                            ClutterActor       *actor)
1064 {
1065   g_return_if_fail (pv != NULL);
1066
1067   pv->actor = actor;
1068 }
1069
1070 ClutterCullResult
1071 _clutter_paint_volume_cull (ClutterPaintVolume *pv,
1072                             const ClutterPlane *planes)
1073 {
1074   int vertex_count;
1075   ClutterVertex *vertices = pv->vertices;
1076   gboolean partial = FALSE;
1077   int i;
1078   int j;
1079
1080   if (pv->is_empty)
1081     return CLUTTER_CULL_RESULT_OUT;
1082
1083   /* We expect the volume to already be transformed into eye coordinates
1084    */
1085   g_return_val_if_fail (pv->is_complete == TRUE, CLUTTER_CULL_RESULT_IN);
1086   g_return_val_if_fail (pv->actor == NULL, CLUTTER_CULL_RESULT_IN);
1087
1088   /* Most actors are 2D so we only have to transform the front 4
1089    * vertices of the paint volume... */
1090   if (G_LIKELY (pv->is_2d))
1091     vertex_count = 4;
1092   else
1093     vertex_count = 8;
1094
1095   for (i = 0; i < 4; i++)
1096     {
1097       int out = 0;
1098       for (j = 0; j < vertex_count; j++)
1099         {
1100           ClutterVertex p;
1101           float distance;
1102
1103           /* XXX: for perspective projections this can be optimized
1104            * out because all the planes should pass through the origin
1105            * so (0,0,0) is a valid v0. */
1106           p.x = vertices[j].x - planes[i].v0[0];
1107           p.y = vertices[j].y - planes[i].v0[1];
1108           p.z = vertices[j].z - planes[i].v0[2];
1109
1110           distance = (planes[i].n[0] * p.x +
1111                       planes[i].n[1] * p.y +
1112                       planes[i].n[2] * p.z);
1113
1114           if (distance < 0)
1115             out++;
1116         }
1117
1118       if (out == vertex_count)
1119         return CLUTTER_CULL_RESULT_OUT;
1120       else if (out != 0)
1121         partial = TRUE;
1122     }
1123
1124   if (partial)
1125     return CLUTTER_CULL_RESULT_PARTIAL;
1126   else
1127     return CLUTTER_CULL_RESULT_IN;
1128 }
1129
1130 void
1131 _clutter_paint_volume_get_stage_paint_box (ClutterPaintVolume *pv,
1132                                            ClutterStage *stage,
1133                                            ClutterActorBox *box)
1134 {
1135   ClutterPaintVolume projected_pv;
1136   CoglMatrix modelview;
1137   CoglMatrix projection;
1138   float viewport[4];
1139   float width;
1140   float height;
1141
1142   _clutter_paint_volume_copy_static (pv, &projected_pv);
1143
1144   cogl_matrix_init_identity (&modelview);
1145
1146   /* If the paint volume isn't already in eye coordinates... */
1147   if (pv->actor)
1148     _clutter_actor_apply_relative_transformation_matrix (pv->actor, NULL,
1149                                                          &modelview);
1150
1151   _clutter_stage_get_projection_matrix (stage, &projection);
1152   _clutter_stage_get_viewport (stage,
1153                                &viewport[0],
1154                                &viewport[1],
1155                                &viewport[2],
1156                                &viewport[3]);
1157
1158   _clutter_paint_volume_project (&projected_pv,
1159                                  &modelview,
1160                                  &projection,
1161                                  viewport);
1162
1163   _clutter_paint_volume_get_bounding_box (&projected_pv, box);
1164
1165   /* The aim here is that for a given rectangle defined with floating point
1166    * coordinates we want to determine a stable quantized size in pixels
1167    * that doesn't vary due to the original box's sub-pixel position.
1168    *
1169    * The reason this is important is because effects will use this
1170    * API to determine the size of offscreen framebuffers and so for
1171    * a fixed-size object that may be animated accross the screen we
1172    * want to make sure that the stage paint-box has an equally stable
1173    * size so that effects aren't made to continuously re-allocate
1174    * a corresponding fbo.
1175    *
1176    * The other thing we consider is that the calculation of this box is
1177    * subject to floating point precision issues that might be slightly
1178    * different to the precision issues involved with actually painting the
1179    * actor, which might result in painting slightly leaking outside the
1180    * user's calculated paint-volume. For this we simply aim to pad out the
1181    * paint-volume by at least half a pixel all the way around.
1182    */
1183   width = box->x2 - box->x1;
1184   height = box->y2 - box->y1;
1185   width = CLUTTER_NEARBYINT (width);
1186   height = CLUTTER_NEARBYINT (height);
1187   /* XXX: NB the width/height may now be up to 0.5px too small so we
1188    * must also pad by 0.25px all around to account for this. In total we
1189    * must padd by at least 0.75px around all sides. */
1190
1191   /* XXX: The furthest that we can overshoot the bottom right corner by
1192    * here is 1.75px in total if you consider that the 0.75 padding could
1193    * just cross an integer boundary and so ceil will effectively add 1.
1194    */
1195   box->x2 = ceilf (box->x2 + 0.75);
1196   box->y2 = ceilf (box->y2 + 0.75);
1197
1198   /* Now we redefine the top-left relative to the bottom right based on the
1199    * rounded width/height determined above + a constant so that the overall
1200    * size of the box will be stable and not dependant on the box's
1201    * position.
1202    *
1203    * Adding 3px to the width/height will ensure we cover the maximum of
1204    * 1.75px padding on the bottom/right and still ensure we have > 0.75px
1205    * padding on the top/left.
1206    */
1207   box->x1 = box->x2 - width - 3;
1208   box->y1 = box->y2 - height - 3;
1209
1210   clutter_paint_volume_free (&projected_pv);
1211 }
1212
1213 void
1214 _clutter_paint_volume_transform_relative (ClutterPaintVolume *pv,
1215                                           ClutterActor *relative_to_ancestor)
1216 {
1217   CoglMatrix matrix;
1218   ClutterActor *actor;
1219
1220   actor = pv->actor;
1221
1222   g_return_if_fail (actor != NULL);
1223
1224   _clutter_paint_volume_set_reference_actor (pv, relative_to_ancestor);
1225
1226   cogl_matrix_init_identity (&matrix);
1227   _clutter_actor_apply_relative_transformation_matrix (actor,
1228                                                        relative_to_ancestor,
1229                                                       &matrix);
1230
1231   _clutter_paint_volume_transform (pv, &matrix);
1232 }