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