1 #include "clutter-build-config.h"
2 
3 #include <math.h>
4 
5 #include "clutter-types.h"
6 #include "clutter-interval.h"
7 #include "clutter-private.h"
8 #include "clutter-actor-box-private.h"
9 
10 /**
11  * clutter_actor_box_new:
12  * @x_1: X coordinate of the top left point
13  * @y_1: Y coordinate of the top left point
14  * @x_2: X coordinate of the bottom right point
15  * @y_2: Y coordinate of the bottom right point
16  *
17  * Allocates a new #ClutterActorBox using the passed coordinates
18  * for the top left and bottom right points.
19  *
20  * This function is the logical equivalent of:
21  *
22  * |[
23  *   clutter_actor_box_init (clutter_actor_box_alloc (),
24  *                           x_1, y_1,
25  *                           x_2, y_2);
26  * ]|
27  *
28  * Return value: (transfer full): the newly allocated #ClutterActorBox.
29  *   Use clutter_actor_box_free() to free the resources
30  *
31  * Since: 1.0
32  */
33 ClutterActorBox *
clutter_actor_box_new(gfloat x_1,gfloat y_1,gfloat x_2,gfloat y_2)34 clutter_actor_box_new (gfloat x_1,
35                        gfloat y_1,
36                        gfloat x_2,
37                        gfloat y_2)
38 {
39   return clutter_actor_box_init (clutter_actor_box_alloc (),
40                                  x_1, y_1,
41                                  x_2, y_2);
42 }
43 
44 /**
45  * clutter_actor_box_alloc:
46  *
47  * Allocates a new #ClutterActorBox.
48  *
49  * Return value: (transfer full): the newly allocated #ClutterActorBox.
50  *   Use clutter_actor_box_free() to free its resources
51  *
52  * Since: 1.12
53  */
54 ClutterActorBox *
clutter_actor_box_alloc(void)55 clutter_actor_box_alloc (void)
56 {
57   return g_new0 (ClutterActorBox, 1);
58 }
59 
60 /**
61  * clutter_actor_box_init:
62  * @box: a #ClutterActorBox
63  * @x_1: X coordinate of the top left point
64  * @y_1: Y coordinate of the top left point
65  * @x_2: X coordinate of the bottom right point
66  * @y_2: Y coordinate of the bottom right point
67  *
68  * Initializes @box with the given coordinates.
69  *
70  * Return value: (transfer none): the initialized #ClutterActorBox
71  *
72  * Since: 1.10
73  */
74 ClutterActorBox *
clutter_actor_box_init(ClutterActorBox * box,gfloat x_1,gfloat y_1,gfloat x_2,gfloat y_2)75 clutter_actor_box_init (ClutterActorBox *box,
76                         gfloat           x_1,
77                         gfloat           y_1,
78                         gfloat           x_2,
79                         gfloat           y_2)
80 {
81   g_return_val_if_fail (box != NULL, NULL);
82 
83   box->x1 = x_1;
84   box->y1 = y_1;
85   box->x2 = x_2;
86   box->y2 = y_2;
87 
88   return box;
89 }
90 
91 /**
92  * clutter_actor_box_init_rect:
93  * @box: a #ClutterActorBox
94  * @x: X coordinate of the origin
95  * @y: Y coordinate of the origin
96  * @width: width of the box
97  * @height: height of the box
98  *
99  * Initializes @box with the given origin and size.
100  *
101  * Since: 1.10
102  */
103 void
clutter_actor_box_init_rect(ClutterActorBox * box,gfloat x,gfloat y,gfloat width,gfloat height)104 clutter_actor_box_init_rect (ClutterActorBox *box,
105                              gfloat           x,
106                              gfloat           y,
107                              gfloat           width,
108                              gfloat           height)
109 {
110   g_return_if_fail (box != NULL);
111 
112   box->x1 = x;
113   box->y1 = y;
114   box->x2 = box->x1 + width;
115   box->y2 = box->y1 + height;
116 }
117 
118 /**
119  * clutter_actor_box_copy:
120  * @box: a #ClutterActorBox
121  *
122  * Copies @box
123  *
124  * Return value: a newly allocated copy of #ClutterActorBox. Use
125  *   clutter_actor_box_free() to free the allocated resources
126  *
127  * Since: 1.0
128  */
129 ClutterActorBox *
clutter_actor_box_copy(const ClutterActorBox * box)130 clutter_actor_box_copy (const ClutterActorBox *box)
131 {
132   if (G_LIKELY (box != NULL))
133     return g_memdup2 (box, sizeof (ClutterActorBox));
134 
135   return NULL;
136 }
137 
138 /**
139  * clutter_actor_box_free:
140  * @box: a #ClutterActorBox
141  *
142  * Frees a #ClutterActorBox allocated using clutter_actor_box_new()
143  * or clutter_actor_box_copy()
144  *
145  * Since: 1.0
146  */
147 void
clutter_actor_box_free(ClutterActorBox * box)148 clutter_actor_box_free (ClutterActorBox *box)
149 {
150   if (G_LIKELY (box != NULL))
151     g_free (box);
152 }
153 
154 /**
155  * clutter_actor_box_equal:
156  * @box_a: a #ClutterActorBox
157  * @box_b: a #ClutterActorBox
158  *
159  * Checks @box_a and @box_b for equality
160  *
161  * Return value: %TRUE if the passed #ClutterActorBox are equal
162  *
163  * Since: 1.0
164  */
165 gboolean
clutter_actor_box_equal(const ClutterActorBox * box_a,const ClutterActorBox * box_b)166 clutter_actor_box_equal (const ClutterActorBox *box_a,
167                          const ClutterActorBox *box_b)
168 {
169   g_return_val_if_fail (box_a != NULL && box_b != NULL, FALSE);
170 
171   if (box_a == box_b)
172     return TRUE;
173 
174   return box_a->x1 == box_b->x1 && box_a->y1 == box_b->y1 &&
175          box_a->x2 == box_b->x2 && box_a->y2 == box_b->y2;
176 }
177 
178 /**
179  * clutter_actor_box_get_x:
180  * @box: a #ClutterActorBox
181  *
182  * Retrieves the X coordinate of the origin of @box
183  *
184  * Return value: the X coordinate of the origin
185  *
186  * Since: 1.0
187  */
188 gfloat
clutter_actor_box_get_x(const ClutterActorBox * box)189 clutter_actor_box_get_x (const ClutterActorBox *box)
190 {
191   g_return_val_if_fail (box != NULL, 0.);
192 
193   return box->x1;
194 }
195 
196 /**
197  * clutter_actor_box_get_y:
198  * @box: a #ClutterActorBox
199  *
200  * Retrieves the Y coordinate of the origin of @box
201  *
202  * Return value: the Y coordinate of the origin
203  *
204  * Since: 1.0
205  */
206 gfloat
clutter_actor_box_get_y(const ClutterActorBox * box)207 clutter_actor_box_get_y (const ClutterActorBox *box)
208 {
209   g_return_val_if_fail (box != NULL, 0.);
210 
211   return box->y1;
212 }
213 
214 /**
215  * clutter_actor_box_get_width:
216  * @box: a #ClutterActorBox
217  *
218  * Retrieves the width of the @box
219  *
220  * Return value: the width of the box
221  *
222  * Since: 1.0
223  */
224 gfloat
clutter_actor_box_get_width(const ClutterActorBox * box)225 clutter_actor_box_get_width (const ClutterActorBox *box)
226 {
227   g_return_val_if_fail (box != NULL, 0.);
228 
229   return box->x2 - box->x1;
230 }
231 
232 /**
233  * clutter_actor_box_get_height:
234  * @box: a #ClutterActorBox
235  *
236  * Retrieves the height of the @box
237  *
238  * Return value: the height of the box
239  *
240  * Since: 1.0
241  */
242 gfloat
clutter_actor_box_get_height(const ClutterActorBox * box)243 clutter_actor_box_get_height (const ClutterActorBox *box)
244 {
245   g_return_val_if_fail (box != NULL, 0.);
246 
247   return box->y2 - box->y1;
248 }
249 
250 /**
251  * clutter_actor_box_get_origin:
252  * @box: a #ClutterActorBox
253  * @x: (out) (allow-none): return location for the X coordinate, or %NULL
254  * @y: (out) (allow-none): return location for the Y coordinate, or %NULL
255  *
256  * Retrieves the origin of @box
257  *
258  * Since: 1.0
259  */
260 void
clutter_actor_box_get_origin(const ClutterActorBox * box,gfloat * x,gfloat * y)261 clutter_actor_box_get_origin (const ClutterActorBox *box,
262                               gfloat                *x,
263                               gfloat                *y)
264 {
265   g_return_if_fail (box != NULL);
266 
267   if (x)
268     *x = box->x1;
269 
270   if (y)
271     *y = box->y1;
272 }
273 
274 /**
275  * clutter_actor_box_get_size:
276  * @box: a #ClutterActorBox
277  * @width: (out) (allow-none): return location for the width, or %NULL
278  * @height: (out) (allow-none): return location for the height, or %NULL
279  *
280  * Retrieves the size of @box
281  *
282  * Since: 1.0
283  */
284 void
clutter_actor_box_get_size(const ClutterActorBox * box,gfloat * width,gfloat * height)285 clutter_actor_box_get_size (const ClutterActorBox *box,
286                             gfloat                *width,
287                             gfloat                *height)
288 {
289   g_return_if_fail (box != NULL);
290 
291   if (width)
292     *width = box->x2 - box->x1;
293 
294   if (height)
295     *height = box->y2 - box->y1;
296 }
297 
298 /**
299  * clutter_actor_box_get_area:
300  * @box: a #ClutterActorBox
301  *
302  * Retrieves the area of @box
303  *
304  * Return value: the area of a #ClutterActorBox, in pixels
305  *
306  * Since: 1.0
307  */
308 gfloat
clutter_actor_box_get_area(const ClutterActorBox * box)309 clutter_actor_box_get_area (const ClutterActorBox *box)
310 {
311   g_return_val_if_fail (box != NULL, 0.);
312 
313   return (box->x2 - box->x1) * (box->y2 - box->y1);
314 }
315 
316 /**
317  * clutter_actor_box_contains:
318  * @box: a #ClutterActorBox
319  * @x: X coordinate of the point
320  * @y: Y coordinate of the point
321  *
322  * Checks whether a point with @x, @y coordinates is contained
323  * within @box
324  *
325  * Return value: %TRUE if the point is contained by the #ClutterActorBox
326  *
327  * Since: 1.0
328  */
329 gboolean
clutter_actor_box_contains(const ClutterActorBox * box,gfloat x,gfloat y)330 clutter_actor_box_contains (const ClutterActorBox *box,
331                             gfloat                 x,
332                             gfloat                 y)
333 {
334   g_return_val_if_fail (box != NULL, FALSE);
335 
336   return (x > box->x1 && x < box->x2) &&
337          (y > box->y1 && y < box->y2);
338 }
339 
340 /**
341  * clutter_actor_box_from_vertices:
342  * @box: a #ClutterActorBox
343  * @verts: (array fixed-size=4): array of four #graphene_point3d_t
344  *
345  * Calculates the bounding box represented by the four vertices; for details
346  * of the vertex array see clutter_actor_get_abs_allocation_vertices().
347  *
348  * Since: 1.0
349  */
350 void
clutter_actor_box_from_vertices(ClutterActorBox * box,const graphene_point3d_t verts[])351 clutter_actor_box_from_vertices (ClutterActorBox          *box,
352                                  const graphene_point3d_t  verts[])
353 {
354   gfloat x_1, x_2, y_1, y_2;
355 
356   g_return_if_fail (box != NULL);
357   g_return_if_fail (verts != NULL);
358 
359   /* 4-way min/max */
360   x_1 = verts[0].x;
361   y_1 = verts[0].y;
362 
363   if (verts[1].x < x_1)
364     x_1 = verts[1].x;
365 
366   if (verts[2].x < x_1)
367     x_1 = verts[2].x;
368 
369   if (verts[3].x < x_1)
370     x_1 = verts[3].x;
371 
372   if (verts[1].y < y_1)
373     y_1 = verts[1].y;
374 
375   if (verts[2].y < y_1)
376     y_1 = verts[2].y;
377 
378   if (verts[3].y < y_1)
379     y_1 = verts[3].y;
380 
381   x_2 = verts[0].x;
382   y_2 = verts[0].y;
383 
384   if (verts[1].x > x_2)
385     x_2 = verts[1].x;
386 
387   if (verts[2].x > x_2)
388     x_2 = verts[2].x;
389 
390   if (verts[3].x > x_2)
391     x_2 = verts[3].x;
392 
393   if (verts[1].y > y_2)
394     y_2 = verts[1].y;
395 
396   if (verts[2].y > y_2)
397     y_2 = verts[2].y;
398 
399   if (verts[3].y > y_2)
400     y_2 = verts[3].y;
401 
402   box->x1 = x_1;
403   box->x2 = x_2;
404   box->y1 = y_1;
405   box->y2 = y_2;
406 }
407 
408 /**
409  * clutter_actor_box_interpolate:
410  * @initial: the initial #ClutterActorBox
411  * @final: the final #ClutterActorBox
412  * @progress: the interpolation progress
413  * @result: (out): return location for the interpolation
414  *
415  * Interpolates between @initial and @final #ClutterActorBox<!-- -->es
416  * using @progress
417  *
418  * Since: 1.2
419  */
420 void
clutter_actor_box_interpolate(const ClutterActorBox * initial,const ClutterActorBox * final,gdouble progress,ClutterActorBox * result)421 clutter_actor_box_interpolate (const ClutterActorBox *initial,
422                                const ClutterActorBox *final,
423                                gdouble                progress,
424                                ClutterActorBox       *result)
425 {
426   g_return_if_fail (initial != NULL);
427   g_return_if_fail (final != NULL);
428   g_return_if_fail (result != NULL);
429 
430   result->x1 = initial->x1 + (final->x1 - initial->x1) * progress;
431   result->y1 = initial->y1 + (final->y1 - initial->y1) * progress;
432   result->x2 = initial->x2 + (final->x2 - initial->x2) * progress;
433   result->y2 = initial->y2 + (final->y2 - initial->y2) * progress;
434 }
435 
436 /**
437  * clutter_actor_box_clamp_to_pixel:
438  * @box: (inout): the #ClutterActorBox to clamp
439  *
440  * Clamps the components of @box to the nearest integer
441  *
442  * Since: 1.2
443  */
444 void
clutter_actor_box_clamp_to_pixel(ClutterActorBox * box)445 clutter_actor_box_clamp_to_pixel (ClutterActorBox *box)
446 {
447   g_return_if_fail (box != NULL);
448 
449   box->x1 = floorf (box->x1);
450   box->y1 = floorf (box->y1);
451   box->x2 = ceilf (box->x2);
452   box->y2 = ceilf (box->y2);
453 }
454 
455 /**
456  * clutter_actor_box_union:
457  * @a: (in): the first #ClutterActorBox
458  * @b: (in): the second #ClutterActorBox
459  * @result: (out): the #ClutterActorBox representing a union
460  *   of @a and @b
461  *
462  * Unions the two boxes @a and @b and stores the result in @result.
463  *
464  * Since: 1.4
465  */
466 void
clutter_actor_box_union(const ClutterActorBox * a,const ClutterActorBox * b,ClutterActorBox * result)467 clutter_actor_box_union (const ClutterActorBox *a,
468                          const ClutterActorBox *b,
469                          ClutterActorBox       *result)
470 {
471   g_return_if_fail (a != NULL);
472   g_return_if_fail (b != NULL);
473   g_return_if_fail (result != NULL);
474 
475   result->x1 = MIN (a->x1, b->x1);
476   result->y1 = MIN (a->y1, b->y1);
477 
478   result->x2 = MAX (a->x2, b->x2);
479   result->y2 = MAX (a->y2, b->y2);
480 }
481 
482 static gboolean
clutter_actor_box_progress(const GValue * a,const GValue * b,gdouble factor,GValue * retval)483 clutter_actor_box_progress (const GValue *a,
484                             const GValue *b,
485                             gdouble       factor,
486                             GValue       *retval)
487 {
488   ClutterActorBox res = { 0, };
489 
490   clutter_actor_box_interpolate (g_value_get_boxed (a),
491                                  g_value_get_boxed (b),
492                                  factor,
493                                  &res);
494 
495   g_value_set_boxed (retval, &res);
496 
497   return TRUE;
498 }
499 
500 /**
501  * clutter_actor_box_set_origin:
502  * @box: a #ClutterActorBox
503  * @x: the X coordinate of the new origin
504  * @y: the Y coordinate of the new origin
505  *
506  * Changes the origin of @box, maintaining the size of the #ClutterActorBox.
507  *
508  * Since: 1.6
509  */
510 void
clutter_actor_box_set_origin(ClutterActorBox * box,gfloat x,gfloat y)511 clutter_actor_box_set_origin (ClutterActorBox *box,
512                               gfloat           x,
513                               gfloat           y)
514 {
515   gfloat width, height;
516 
517   g_return_if_fail (box != NULL);
518 
519   width = box->x2 - box->x1;
520   height = box->y2 - box->y1;
521 
522   clutter_actor_box_init_rect (box, x, y, width, height);
523 }
524 
525 /**
526  * clutter_actor_box_set_size:
527  * @box: a #ClutterActorBox
528  * @width: the new width
529  * @height: the new height
530  *
531  * Sets the size of @box, maintaining the origin of the #ClutterActorBox.
532  *
533  * Since: 1.6
534  */
535 void
clutter_actor_box_set_size(ClutterActorBox * box,gfloat width,gfloat height)536 clutter_actor_box_set_size (ClutterActorBox *box,
537                             gfloat           width,
538                             gfloat           height)
539 {
540   g_return_if_fail (box != NULL);
541 
542   box->x2 = box->x1 + width;
543   box->y2 = box->y1 + height;
544 }
545 
546 void
_clutter_actor_box_enlarge_for_effects(ClutterActorBox * box)547 _clutter_actor_box_enlarge_for_effects (ClutterActorBox *box)
548 {
549   float width, height;
550 
551   /* The aim here is that for a given rectangle defined with floating point
552    * coordinates we want to determine a stable quantized size in pixels
553    * that doesn't vary due to the original box's sub-pixel position.
554    *
555    * The reason this is important is because effects will use this
556    * API to determine the size of offscreen framebuffers and so for
557    * a fixed-size object that may be animated across the screen we
558    * want to make sure that the stage paint-box has an equally stable
559    * size so that effects aren't made to continuously re-allocate
560    * a corresponding fbo.
561    *
562    * The other thing we consider is that the calculation of this box is
563    * subject to floating point precision issues that might be slightly
564    * different to the precision issues involved with actually painting the
565    * actor, which might result in painting slightly leaking outside the
566    * user's calculated paint-volume. For this we simply aim to pad out the
567    * paint-volume by at least half a pixel all the way around.
568    */
569   width = box->x2 - box->x1;
570   height = box->y2 - box->y1;
571   width = CLUTTER_NEARBYINT (width);
572   height = CLUTTER_NEARBYINT (height);
573   /* XXX: NB the width/height may now be up to 0.5px too small so we
574    * must also pad by 0.25px all around to account for this. In total we
575    * must padd by at least 0.75px around all sides. */
576 
577   /* XXX: The furthest that we can overshoot the bottom right corner by
578    * here is 1.75px in total if you consider that the 0.75 padding could
579    * just cross an integer boundary and so ceil will effectively add 1.
580    */
581   box->x2 = ceilf (box->x2 + 0.75);
582   box->y2 = ceilf (box->y2 + 0.75);
583 
584   /* Now we redefine the top-left relative to the bottom right based on the
585    * rounded width/height determined above + a constant so that the overall
586    * size of the box will be stable and not dependent on the box's
587    * position.
588    *
589    * Adding 3px to the width/height will ensure we cover the maximum of
590    * 1.75px padding on the bottom/right and still ensure we have > 0.75px
591    * padding on the top/left.
592    */
593   box->x1 = box->x2 - width - 3;
594   box->y1 = box->y2 - height - 3;
595 }
596 
597 /**
598  * clutter_actor_box_scale:
599  * @box: a #ClutterActorBox
600  * @scale: scale factor for resizing this box
601  *
602  * Rescale the @box by provided @scale factor.
603  *
604  * Since: 1.6
605  */
606 void
clutter_actor_box_scale(ClutterActorBox * box,gfloat scale)607 clutter_actor_box_scale (ClutterActorBox *box,
608                          gfloat           scale)
609 {
610   g_return_if_fail (box != NULL);
611 
612   box->x1 *= scale;
613   box->x2 *= scale;
614   box->y1 *= scale;
615   box->y2 *= scale;
616 }
617 
618 /**
619  * clutter_actor_box_is_initialized:
620  * @box: a #ClutterActorBox
621  *
622  * Checks if @box has been initialized, a #ClutterActorBox is uninitialized
623  * if it has a size of -1 at an origin of 0, 0.
624  *
625  * Returns: %TRUE if the box is uninitialized, %FALSE if it isn't
626  */
627 gboolean
clutter_actor_box_is_initialized(ClutterActorBox * box)628 clutter_actor_box_is_initialized (ClutterActorBox *box)
629 {
630   gboolean x1_uninitialized, x2_uninitialized;
631   gboolean y1_uninitialized, y2_uninitialized;
632 
633   g_return_val_if_fail (box != NULL, TRUE);
634 
635   x1_uninitialized = isinf (box->x1);
636   x2_uninitialized = isinf (box->x2) && signbit (box->x2);
637   y1_uninitialized = isinf (box->y1);
638   y2_uninitialized = isinf (box->y2) && signbit (box->y2);
639 
640   return !x1_uninitialized || !x2_uninitialized ||
641          !y1_uninitialized || !y2_uninitialized;
642 }
643 
644 G_DEFINE_BOXED_TYPE_WITH_CODE (ClutterActorBox, clutter_actor_box,
645                                clutter_actor_box_copy,
646                                clutter_actor_box_free,
647                                CLUTTER_REGISTER_INTERVAL_PROGRESS (clutter_actor_box_progress));
648