1/*
2 * Copyright © 2019 Benjamin Otte
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Authors: Benjamin Otte <otte@gnome.org>
18 */
19
20#include "config.h"
21
22#include <gtk/gtk.h>
23
24#define EPSILON (1.f / 1024 / 32) /* 2^-15 */
25
26/* macros stolen from graphene testsuite, so they get to keep their names */
27
28#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9))
29/* Use typeof on GCC */
30# define graphene_fuzzy_equals(n1,n2,epsilon) \
31 G_GNUC_EXTENSION({ \
32 __auto_type _n1 = (n1); \
33 __auto_type _n2 = (n2); \
34 __auto_type _epsilon = (epsilon); \
35 ((_n1 > _n2 ? (_n1 - _n2 ) : (_n2 - _n1)) <= _epsilon); \
36 })
37
38#else
39/* fallback for Visual Studio, typeof not supported */
40# define graphene_fuzzy_equals(n1,n2,epsilon) \
41 (((n1) > (n2) ? ((n1) - (n2)) : ((n2) - (n1))) <= (epsilon))
42
43#endif /* __GNUC__ */
44
45#define graphene_assert_fuzzy_matrix_cell_equal(row,col,n1,n2,epsilon) \
46 G_STMT_START { \
47 if (graphene_fuzzy_equals (n1, n2, epsilon)) ; else { \
48 char *s = g_strdup_printf ("[%d][%d]: " #n1 " == " #n2 " (+/- " #epsilon "): (%.7g == %.7g)", \
49 row, col, n1, n2); \
50 g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, s); \
51 g_free (s); \
52 } \
53 } G_STMT_END
54
55#define graphene_assert_fuzzy_matrix_equal(m1,m2,epsilon) \
56 G_STMT_START { \
57 unsigned int __i, __j; \
58 float __m1[16], __m2[16]; \
59 graphene_matrix_to_float ((m1), __m1); \
60 graphene_matrix_to_float ((m2), __m2); \
61 for (__i = 0; __i < 4; __i++) { \
62 for (__j = 0; __j < 4; __j++) { \
63 unsigned int __idx = __i * 4 + __j; \
64 graphene_assert_fuzzy_matrix_cell_equal (__i, __j, __m1[__idx], __m2[__idx], epsilon); \
65 } \
66 } \
67 } G_STMT_END
68
69#define graphene_assert_fuzzy_transform_equal(t1,t2,epsilon) \
70 G_STMT_START { \
71 graphene_matrix_t __mat1, __mat2; \
72 gsk_transform_to_matrix ((t1), &__mat1); \
73 gsk_transform_to_matrix ((t2), &__mat2); \
74 graphene_assert_fuzzy_matrix_equal (&__mat1, &__mat2, (epsilon)); \
75 } G_STMT_END
76
77static struct {
78 GskTransformCategory category;
79} test_transforms[] = {
80 { GSK_TRANSFORM_CATEGORY_IDENTITY },
81 { GSK_TRANSFORM_CATEGORY_IDENTITY },
82 { GSK_TRANSFORM_CATEGORY_2D_TRANSLATE },
83 { GSK_TRANSFORM_CATEGORY_3D },
84 { GSK_TRANSFORM_CATEGORY_2D },
85 { GSK_TRANSFORM_CATEGORY_3D },
86 { GSK_TRANSFORM_CATEGORY_2D_AFFINE },
87 { GSK_TRANSFORM_CATEGORY_3D },
88 { GSK_TRANSFORM_CATEGORY_ANY },
89};
90
91static GskTransform *
92apply_test_transform (GskTransform *transform,
93 guint i)
94{
95 switch (i)
96 {
97 case 0:
98 return transform ? transform : gsk_transform_new ();
99
100 case 1:
101 return gsk_transform_transform (next: transform, NULL);
102
103 case 2:
104 return gsk_transform_translate (next: transform, point: &GRAPHENE_POINT_INIT (3, 5));
105
106 case 3:
107 return gsk_transform_translate_3d (next: transform, point: &GRAPHENE_POINT3D_INIT (3, 5, 7));
108
109 case 4:
110 return gsk_transform_rotate (next: transform, angle: 90);
111
112 case 5:
113 return gsk_transform_rotate_3d (next: transform, angle: 90, axis: graphene_vec3_y_axis ());
114
115 case 6:
116 return gsk_transform_scale (next: transform, factor_x: 2, factor_y: 3);
117
118 case 7:
119 return gsk_transform_scale_3d (next: transform, factor_x: 2, factor_y: 3, factor_z: 5);
120
121 case 8:
122 return gsk_transform_perspective (next: transform, depth: 5);
123
124 default:
125 g_assert_not_reached ();
126 return NULL;
127 }
128}
129
130static GskTransformCategory
131categorize_matrix (const graphene_matrix_t *matrix)
132{
133 if (!graphene_fuzzy_equals (graphene_matrix_get_value (matrix, 0, 3), 0, EPSILON) ||
134 !graphene_fuzzy_equals (graphene_matrix_get_value (matrix, 1, 3), 0, EPSILON) ||
135 !graphene_fuzzy_equals (graphene_matrix_get_value (matrix, 2, 3), 0, EPSILON) ||
136 !graphene_fuzzy_equals (graphene_matrix_get_value (matrix, 3, 3), 1, EPSILON))
137 return GSK_TRANSFORM_CATEGORY_ANY;
138
139 if (!graphene_fuzzy_equals (graphene_matrix_get_value (matrix, 0, 2), 0, EPSILON) ||
140 !graphene_fuzzy_equals (graphene_matrix_get_value (matrix, 1, 2), 0, EPSILON) ||
141 !graphene_fuzzy_equals (graphene_matrix_get_value (matrix, 2, 2), 1, EPSILON) ||
142 !graphene_fuzzy_equals (graphene_matrix_get_value (matrix, 3, 2), 0, EPSILON) ||
143 !graphene_fuzzy_equals (graphene_matrix_get_value (matrix, 2, 0), 0, EPSILON) ||
144 !graphene_fuzzy_equals (graphene_matrix_get_value (matrix, 2, 1), 0, EPSILON))
145 return GSK_TRANSFORM_CATEGORY_3D;
146
147 if (!graphene_fuzzy_equals (graphene_matrix_get_value (matrix, 0, 1), 0, EPSILON) ||
148 !graphene_fuzzy_equals (graphene_matrix_get_value (matrix, 1, 0), 0, EPSILON))
149 return GSK_TRANSFORM_CATEGORY_2D;
150
151 if (!graphene_fuzzy_equals (graphene_matrix_get_value (matrix, 0, 0), 1, EPSILON) ||
152 !graphene_fuzzy_equals (graphene_matrix_get_value (matrix, 1, 1), 1, EPSILON))
153 return GSK_TRANSFORM_CATEGORY_2D_AFFINE;
154
155 if (!graphene_fuzzy_equals (graphene_matrix_get_value (matrix, 3, 0), 0, EPSILON) ||
156 !graphene_fuzzy_equals (graphene_matrix_get_value (matrix, 3, 1), 0, EPSILON))
157 return GSK_TRANSFORM_CATEGORY_2D_TRANSLATE;
158
159 return GSK_TRANSFORM_CATEGORY_IDENTITY;
160}
161
162static void
163check_conversions (GskTransform *transform,
164 GskTransformCategory expected_category)
165{
166 graphene_matrix_t matrix, test;
167 float f[16] = { 1, 0, 0, 0,
168 0, 1, 0, 0,
169 0, 0, 1, 0,
170 0, 0, 0, 1 };
171
172 g_assert_cmpint (gsk_transform_get_category (transform), ==, expected_category);
173 gsk_transform_to_matrix (self: transform, out_matrix: &matrix);
174 /* we don't insist on getting simplifications right.
175 * The matrix "scale(2) scale(0.5)" would be categorized as identity,
176 * but the transform might not do that.
177 */
178 g_assert_cmpint (gsk_transform_get_category (transform), <=, categorize_matrix (&matrix));
179
180 switch (expected_category)
181 {
182 case GSK_TRANSFORM_CATEGORY_UNKNOWN:
183 case GSK_TRANSFORM_CATEGORY_ANY:
184 case GSK_TRANSFORM_CATEGORY_3D:
185 break;
186
187 case GSK_TRANSFORM_CATEGORY_IDENTITY:
188 case GSK_TRANSFORM_CATEGORY_2D_TRANSLATE:
189 gsk_transform_to_translate (self: transform,
190 out_dx: &f[4 * 3 + 0], out_dy: &f[4 * 3 + 1]);
191 graphene_matrix_init_from_float (m: &test, v: f);
192 graphene_assert_fuzzy_matrix_equal (&matrix, &test, EPSILON);
193
194 G_GNUC_FALLTHROUGH;
195 case GSK_TRANSFORM_CATEGORY_2D_AFFINE:
196 gsk_transform_to_affine (self: transform,
197 out_scale_x: &f[4 * 0 + 0], out_scale_y: &f[4 * 1 + 1],
198 out_dx: &f[4 * 3 + 0], out_dy: &f[4 * 3 + 1]);
199 graphene_matrix_init_from_float (m: &test, v: f);
200 graphene_assert_fuzzy_matrix_equal (&matrix, &test, EPSILON);
201
202 G_GNUC_FALLTHROUGH;
203 case GSK_TRANSFORM_CATEGORY_2D:
204 gsk_transform_to_2d (self: transform,
205 out_xx: &f[4 * 0 + 0], out_yx: &f[4 * 0 + 1],
206 out_xy: &f[4 * 1 + 0], out_yy: &f[4 * 1 + 1],
207 out_dx: &f[4 * 3 + 0], out_dy: &f[4 * 3 + 1]);
208 graphene_matrix_init_from_float (m: &test, v: f);
209 graphene_assert_fuzzy_matrix_equal (&matrix, &test, EPSILON);
210 break;
211
212 default:
213 g_assert_not_reached ();
214 }
215}
216
217static void
218test_conversions_simple (void)
219{
220 GskTransform *transform;
221 guint i;
222
223 for (i = 0; i < G_N_ELEMENTS (test_transforms); i++)
224 {
225 transform = apply_test_transform (NULL, i);
226 check_conversions (transform, expected_category: test_transforms[i].category);
227 gsk_transform_unref (self: transform);
228 }
229}
230
231static void
232test_conversions_transformed (void)
233{
234 GskTransform *transform;
235 guint i, j, k;
236
237 for (i = 0; i < G_N_ELEMENTS (test_transforms); i++)
238 {
239 for (j = 0; j < G_N_ELEMENTS (test_transforms); j++)
240 {
241 for (k = 0; k < G_N_ELEMENTS (test_transforms); k++)
242 {
243 transform = apply_test_transform (NULL, i);
244 transform = apply_test_transform (transform, i: j);
245 transform = apply_test_transform (transform, i: k);
246 check_conversions (transform, MIN (test_transforms[i].category, MIN (test_transforms[j].category, test_transforms[k].category)));
247 gsk_transform_unref (self: transform);
248 }
249 }
250 }
251}
252
253static void
254test_invert (void)
255{
256 GskTransform *transform, *inverse, *identity;
257 guint i, j, k;
258
259 for (i = 0; i < G_N_ELEMENTS (test_transforms); i++)
260 {
261 for (j = 0; j < G_N_ELEMENTS (test_transforms); j++)
262 {
263 for (k = 0; k < G_N_ELEMENTS (test_transforms); k++)
264 {
265 transform = apply_test_transform (NULL, i);
266 transform = apply_test_transform (transform, i: j);
267 transform = apply_test_transform (transform, i: k);
268 inverse = gsk_transform_invert (self: gsk_transform_ref (self: transform));
269 g_assert_true (inverse != NULL || transform == NULL);
270
271 identity = gsk_transform_transform (next: gsk_transform_ref (self: transform), other: inverse);
272 graphene_assert_fuzzy_transform_equal (identity, NULL, EPSILON);
273 gsk_transform_unref (self: identity);
274
275 inverse = gsk_transform_invert (self: inverse);
276 graphene_assert_fuzzy_transform_equal (transform, inverse, EPSILON);
277
278 gsk_transform_unref (self: transform);
279 gsk_transform_unref (self: inverse);
280 }
281 }
282 }
283}
284
285/* some trivialities around identity transforms */
286static void
287test_identity (void)
288{
289 GskTransform *s, *t, *u, *v, *w, *x;
290 char *string;
291 float a, b, c, d, tx, ty;
292 gboolean res;
293
294 s = gsk_transform_new ();
295 t = gsk_transform_new ();
296 u = gsk_transform_transform (next: gsk_transform_ref (self: s), NULL);
297
298 g_assert_cmpint (gsk_transform_get_category (s), ==, GSK_TRANSFORM_CATEGORY_IDENTITY);
299 g_assert_cmpint (gsk_transform_get_category (t), ==, GSK_TRANSFORM_CATEGORY_IDENTITY);
300 g_assert_cmpint (gsk_transform_get_category (u), ==, GSK_TRANSFORM_CATEGORY_IDENTITY);
301
302 g_assert_true (gsk_transform_equal (s, t));
303 g_assert_true (gsk_transform_equal (t, u));
304 g_assert_true (gsk_transform_equal (s, u));
305
306 v = gsk_transform_transform (next: gsk_transform_ref (self: s), other: t);
307
308 g_assert_cmpint (gsk_transform_get_category (v), ==, GSK_TRANSFORM_CATEGORY_IDENTITY);
309
310 w = gsk_transform_invert (self: gsk_transform_ref (self: v));
311 g_assert_cmpint (gsk_transform_get_category (w), ==, GSK_TRANSFORM_CATEGORY_IDENTITY);
312
313 string = gsk_transform_to_string (self: s);
314 res = gsk_transform_parse (string, out_transform: &x);
315
316 g_assert_true (res);
317 g_assert_cmpint (gsk_transform_get_category (x), ==, GSK_TRANSFORM_CATEGORY_IDENTITY);
318
319 gsk_transform_to_2d (self: s, out_xx: &a, out_yx: &b, out_xy: &c, out_yy: &d, out_dx: &tx, out_dy: &ty);
320
321 g_assert_cmpfloat (a, ==, 1.0f);
322 g_assert_cmpfloat (b, ==, 0.0f);
323 g_assert_cmpfloat (c, ==, 0.0f);
324 g_assert_cmpfloat (d, ==, 1.0f);
325 g_assert_cmpfloat (tx, ==, 0.0f);
326 g_assert_cmpfloat (ty, ==, 0.f);
327
328 gsk_transform_unref (self: s);
329 gsk_transform_unref (self: t);
330 gsk_transform_unref (self: u);
331 gsk_transform_unref (self: v);
332 gsk_transform_unref (self: w);
333 gsk_transform_unref (self: x);
334
335 g_free (mem: string);
336}
337
338static void
339test_identity_equal (void)
340{
341 GskTransform *id = gsk_transform_new ();
342 GskTransform *t;
343
344 g_assert_true (gsk_transform_equal (NULL, NULL));
345 g_assert_true (gsk_transform_equal (id, NULL));
346 g_assert_true (gsk_transform_equal (NULL, id));
347 g_assert_true (gsk_transform_equal (id, id));
348
349 t = gsk_transform_transform (NULL, NULL);
350 g_assert_true (gsk_transform_equal (t, NULL));
351 gsk_transform_unref (self: t);
352 t = gsk_transform_transform (next: gsk_transform_new (), NULL);
353 g_assert_true (gsk_transform_equal (t, NULL));
354 gsk_transform_unref (self: t);
355 t = gsk_transform_transform (NULL, other: id);
356 g_assert_true (gsk_transform_equal (t, NULL));
357 gsk_transform_unref (self: t);
358 t = gsk_transform_transform (next: gsk_transform_new (), other: id);
359 g_assert_true (gsk_transform_equal (t, NULL));
360 gsk_transform_unref (self: t);
361 t = gsk_transform_new ();
362 t = gsk_transform_transform (next: t, other: t);
363 g_assert_true (gsk_transform_equal (t, NULL));
364 gsk_transform_unref (self: t);
365
366 gsk_transform_unref (self: id);
367}
368
369static void
370test_print_parse (void)
371{
372 GskTransform *transform, *parsed;
373 guint i, j, k;
374 char *str1, *str2;
375 gboolean ret;
376
377 for (i = 0; i < G_N_ELEMENTS (test_transforms); i++)
378 {
379 for (j = 0; j < G_N_ELEMENTS (test_transforms); j++)
380 {
381 for (k = 0; k < G_N_ELEMENTS (test_transforms); k++)
382 {
383 transform = apply_test_transform (NULL, i);
384 transform = apply_test_transform (transform, i: j);
385 transform = apply_test_transform (transform, i: k);
386
387 str1 = gsk_transform_to_string (self: transform);
388 g_assert_nonnull (str1);
389 g_assert_true (strlen (str1) > 0);
390
391 str2 = gsk_transform_to_string (self: transform);
392 g_assert_cmpstr (str1, ==, str2);
393 g_free (mem: str2);
394
395 ret = gsk_transform_parse (string: str1, out_transform: &parsed);
396 g_assert_true (ret);
397 graphene_assert_fuzzy_transform_equal (parsed, transform, EPSILON);
398
399 str2 = gsk_transform_to_string (self: parsed);
400 g_assert_cmpstr (str1, ==, str2);
401 g_free (mem: str2);
402
403 g_free (mem: str1);
404 gsk_transform_unref (self: parsed);
405 gsk_transform_unref (self: transform);
406 }
407 }
408 }
409}
410
411static void
412gsk_matrix_transform_rect (const graphene_matrix_t *m,
413 const graphene_rect_t *r,
414 graphene_quad_t *res)
415{
416 graphene_point_t ret[4];
417 graphene_rect_t rr;
418
419 graphene_rect_normalize_r (r, res: &rr);
420
421#define TRANSFORM_POINT(matrix, rect, corner, out_p) do {\
422 graphene_vec4_t __s; \
423 graphene_point_t __p; \
424 float w; \
425 graphene_rect_get_ ## corner (rect, &__p); \
426 graphene_vec4_init (&__s, __p.x, __p.y, 0.f, 1.f); \
427 graphene_matrix_transform_vec4 (matrix, &__s, &__s); \
428 w = graphene_vec4_get_w (&__s); \
429 out_p.x = graphene_vec4_get_x (&__s) / w; \
430 out_p.y = graphene_vec4_get_y (&__s) / w; } while (0)
431
432 TRANSFORM_POINT (m, &rr, top_left, ret[0]);
433 TRANSFORM_POINT (m, &rr, top_right, ret[1]);
434 TRANSFORM_POINT (m, &rr, bottom_right, ret[2]);
435 TRANSFORM_POINT (m, &rr, bottom_left, ret[3]);
436
437#undef TRANSFORM_POINT
438
439 graphene_quad_init (q: res, p1: &ret[0], p2: &ret[1], p3: &ret[2], p4: &ret[3]);
440}
441
442/* This is an auxiliary function used in the GL renderer to
443 * determine if transforming an axis-aligned rectangle produces
444 * axis-aligned output, to decide whether to use linear
445 * interpolation or not.
446 */
447static gboolean
448result_is_axis_aligned (GskTransform *transform,
449 const graphene_rect_t *bounds)
450{
451 graphene_matrix_t m;
452 graphene_quad_t q;
453 graphene_rect_t b;
454 graphene_point_t b1, b2;
455 const graphene_point_t *p;
456 int i;
457
458 gsk_transform_to_matrix (self: transform, out_matrix: &m);
459 gsk_matrix_transform_rect (m: &m, r: bounds, res: &q);
460 graphene_quad_bounds (q: &q, r: &b);
461 graphene_rect_get_top_left (r: &b, p: &b1);
462 graphene_rect_get_bottom_right (r: &b, p: &b2);
463
464 for (i = 0; i < 4; i++)
465 {
466 p = graphene_quad_get_point (q: &q, index_: i);
467 if (fabs (x: p->x - b1.x) > FLT_EPSILON && fabs (x: p->x - b2.x) > FLT_EPSILON)
468 return FALSE;
469 if (fabs (x: p->y - b1.y) > FLT_EPSILON && fabs (x: p->y - b2.y) > FLT_EPSILON)
470 return FALSE;
471 }
472
473 return TRUE;
474}
475
476static void
477test_axis_aligned (void)
478{
479 graphene_rect_t r = GRAPHENE_RECT_INIT (0, 0, 10, 10);
480 GskTransform *transform = NULL;
481
482 transform = gsk_transform_translate (NULL, point: &GRAPHENE_POINT_INIT (10, 10));
483 g_assert_true (result_is_axis_aligned (transform, &r));
484 gsk_transform_unref (self: transform);
485
486 transform = gsk_transform_translate_3d (NULL, point: &GRAPHENE_POINT3D_INIT(0, 10, 10));
487 g_assert_true (result_is_axis_aligned (transform, &r));
488 gsk_transform_unref (self: transform);
489
490 transform = gsk_transform_rotate (NULL, angle: 90);
491 g_assert_true (result_is_axis_aligned (transform, &r));
492 gsk_transform_unref (self: transform);
493
494 transform = gsk_transform_scale (NULL, factor_x: 2, factor_y: 3);
495 g_assert_true (result_is_axis_aligned (transform, &r));
496 gsk_transform_unref (self: transform);
497
498 /* rotating around the y axis does not affect axis alignedness,
499 * as long as we don't involve perspective
500 */
501 transform = gsk_transform_rotate_3d (NULL, angle: 45, axis: graphene_vec3_y_axis ());
502 g_assert_true (result_is_axis_aligned (transform, &r));
503 gsk_transform_unref (self: transform);
504
505 /* rotating by 45 around the z axis, not axis aligned */
506 transform = gsk_transform_rotate (NULL, angle: 45);
507 g_assert_false (result_is_axis_aligned (transform, &r));
508 gsk_transform_unref (self: transform);
509
510 /* perspective is harmless as long as we stay in the z=0 plane */
511 transform = gsk_transform_perspective (NULL, depth: 100);
512 g_assert_true (result_is_axis_aligned (transform, &r));
513 gsk_transform_unref (self: transform);
514
515 /* a complex transform that makes things look '3d' */
516 transform = gsk_transform_translate_3d (NULL, point: &GRAPHENE_POINT3D_INIT (0, 0, 50));
517 transform = gsk_transform_perspective (next: transform, depth: 170);
518 transform = gsk_transform_translate_3d (next: transform, point: &GRAPHENE_POINT3D_INIT (50, 0, 50));
519 transform = gsk_transform_rotate (next: transform, angle: 20);
520 transform = gsk_transform_rotate_3d (next: transform, angle: 20, axis: graphene_vec3_y_axis ());
521 g_assert_false (result_is_axis_aligned (transform, &r));
522 gsk_transform_unref (self: transform);
523}
524
525static void
526test_to_affine (void)
527{
528 GskTransform *transform;
529 float sx, sy, dx, dy;
530
531 transform = gsk_transform_scale (NULL, factor_x: 10.0, factor_y: 5.0);
532 gsk_transform_to_affine (self: transform, out_scale_x: &sx, out_scale_y: &sy, out_dx: &dx, out_dy: &dy);
533 gsk_transform_unref (self: transform);
534
535 g_assert_cmpfloat (sx, ==, 10.0);
536 g_assert_cmpfloat (sy, ==, 5.0);
537 g_assert_cmpfloat (dx, ==, 0.0);
538 g_assert_cmpfloat (dy, ==, 0.0);
539
540 transform = gsk_transform_translate (NULL, point: &GRAPHENE_POINT_INIT (10.0, 5.0));
541 gsk_transform_to_affine (self: transform, out_scale_x: &sx, out_scale_y: &sy, out_dx: &dx, out_dy: &dy);
542 gsk_transform_unref (self: transform);
543
544 g_assert_cmpfloat (sx, ==, 1.0);
545 g_assert_cmpfloat (sy, ==, 1.0);
546 g_assert_cmpfloat (dx, ==, 10.0);
547 g_assert_cmpfloat (dy, ==, 5.0);
548
549 transform = gsk_transform_translate (next: gsk_transform_scale (NULL, factor_x: 2.0, factor_y: 3.0), point: &GRAPHENE_POINT_INIT (10.0, 5.0));
550 gsk_transform_to_affine (self: transform, out_scale_x: &sx, out_scale_y: &sy, out_dx: &dx, out_dy: &dy);
551 gsk_transform_unref (self: transform);
552
553 g_assert_cmpfloat (sx, ==, 2.0);
554 g_assert_cmpfloat (sy, ==, 3.0);
555 g_assert_cmpfloat (dx, ==, 2.0 * 10.0);
556 g_assert_cmpfloat (dy, ==, 3.0 * 5.0);
557
558 transform = gsk_transform_scale (next: gsk_transform_translate (NULL, point: &GRAPHENE_POINT_INIT (10.0, 5.0)), factor_x: 2.0, factor_y: 3.0);
559 gsk_transform_to_affine (self: transform, out_scale_x: &sx, out_scale_y: &sy, out_dx: &dx, out_dy: &dy);
560 gsk_transform_unref (self: transform);
561
562 g_assert_cmpfloat (sx, ==, 2.0);
563 g_assert_cmpfloat (sy, ==, 3.0);
564 g_assert_cmpfloat (dx, ==, 10.0);
565 g_assert_cmpfloat (dy, ==, 5.0);
566}
567
568static void
569test_transform_bounds (void)
570{
571 GskTransform *t = gsk_transform_translate (NULL, point: &GRAPHENE_POINT_INIT (50, 50));
572 graphene_rect_t bounds = GRAPHENE_RECT_INIT (0, 0, 100, 100);
573 graphene_rect_t out;
574
575 gsk_transform_transform_bounds (self: t, rect: &bounds, out_rect: &out);
576 g_assert_true (graphene_rect_equal (&out, &GRAPHENE_RECT_INIT(50, 50, 100, 100)));
577
578 t = gsk_transform_rotate (next: t, angle: 180);
579 gsk_transform_transform_bounds (self: t, rect: &bounds, out_rect: &out);
580 g_assert_true (graphene_rect_equal (&out, &GRAPHENE_RECT_INIT(-50, -50, 100, 100)));
581
582 t = gsk_transform_translate (next: t, point: &GRAPHENE_POINT_INIT (-50, -50));
583 gsk_transform_transform_bounds (self: t, rect: &bounds, out_rect: &out);
584 g_assert_true (graphene_rect_equal (&out, &GRAPHENE_RECT_INIT(0, 0, 100, 100)));
585}
586
587#define DEG_TO_RAD(x) ((x) / 180.0 * G_PI)
588
589static void
590test_to_2d (void)
591{
592 GskTransform *transform;
593 float xx, yx, xy, yy, dx, dy;
594 float s, c;
595 float tx,ty;
596
597 transform = gsk_transform_scale (NULL, factor_x: 10.0, factor_y: 5.0);
598 gsk_transform_to_2d (self: transform, out_xx: &xx, out_yx: &yx, out_xy: &xy, out_yy: &yy, out_dx: &dx, out_dy: &dy);
599 gsk_transform_unref (self: transform);
600
601 g_assert_cmpfloat (xx, ==, 10.0);
602 g_assert_cmpfloat (yx, ==, 0.0);
603 g_assert_cmpfloat (xy, ==, 0.0);
604 g_assert_cmpfloat (yy, ==, 5.0);
605 g_assert_cmpfloat (dx, ==, 0.0);
606 g_assert_cmpfloat (dy, ==, 0.0);
607
608 transform = gsk_transform_translate (NULL, point: &GRAPHENE_POINT_INIT (10.0, 5.0));
609 gsk_transform_to_2d (self: transform, out_xx: &xx, out_yx: &yx, out_xy: &xy, out_yy: &yy, out_dx: &dx, out_dy: &dy);
610 gsk_transform_unref (self: transform);
611
612 g_assert_cmpfloat (xx, ==, 1.0);
613 g_assert_cmpfloat (yx, ==, 0.0);
614 g_assert_cmpfloat (xy, ==, 0.0);
615 g_assert_cmpfloat (yy, ==, 1.0);
616 g_assert_cmpfloat (dx, ==, 10.0);
617 g_assert_cmpfloat (dy, ==, 5.0);
618
619 transform = gsk_transform_rotate (NULL, angle: 33.0);
620 gsk_transform_to_2d (self: transform, out_xx: &xx, out_yx: &yx, out_xy: &xy, out_yy: &yy, out_dx: &dx, out_dy: &dy);
621 gsk_transform_unref (self: transform);
622
623 c = cosf (DEG_TO_RAD (33.0));
624 s = sinf (DEG_TO_RAD (33.0));
625
626 g_assert_cmpfloat (xx, ==, c);
627 g_assert_cmpfloat (yx, ==, s);
628 g_assert_cmpfloat (xy, ==, -s);
629 g_assert_cmpfloat (yy, ==, c);
630 g_assert_cmpfloat (dx, ==, 0.0);
631 g_assert_cmpfloat (dy, ==, 0.0);
632
633 transform = gsk_transform_skew (NULL, skew_x: 33.0, skew_y: 0);
634 gsk_transform_to_2d (self: transform, out_xx: &xx, out_yx: &yx, out_xy: &xy, out_yy: &yy, out_dx: &dx, out_dy: &dy);
635 gsk_transform_unref (self: transform);
636
637 tx = tanf (DEG_TO_RAD (33.0));
638
639 g_assert_cmpfloat (xx, ==, 1);
640 g_assert_cmpfloat (yx, ==, 0);
641 g_assert_cmpfloat (xy, ==, tx);
642 g_assert_cmpfloat (yy, ==, 1);
643 g_assert_cmpfloat (dx, ==, 0.0);
644 g_assert_cmpfloat (dy, ==, 0.0);
645
646 transform = gsk_transform_skew (NULL, skew_x: 0, skew_y: 66.0);
647 gsk_transform_to_2d (self: transform, out_xx: &xx, out_yx: &yx, out_xy: &xy, out_yy: &yy, out_dx: &dx, out_dy: &dy);
648 gsk_transform_unref (self: transform);
649
650 ty = tanf (DEG_TO_RAD (66.0));
651
652 g_assert_cmpfloat (xx, ==, 1);
653 g_assert_cmpfloat (yx, ==, ty);
654 g_assert_cmpfloat (xy, ==, 0);
655 g_assert_cmpfloat (yy, ==, 1);
656 g_assert_cmpfloat (dx, ==, 0.0);
657 g_assert_cmpfloat (dy, ==, 0.0);
658}
659
660static void
661test_to_2d_components (void)
662{
663 GskTransform *transform, *transform2;
664 float skew_x, skew_y, scale_x, scale_y, angle, dx, dy;
665 graphene_matrix_t m, m2;
666
667 transform = gsk_transform_scale (
668 next: gsk_transform_rotate (
669 next: gsk_transform_translate (NULL, point: &GRAPHENE_POINT_INIT (10, 20)),
670 angle: 22),
671 factor_x: 3, factor_y: 3);
672 gsk_transform_to_2d_components (self: transform,
673 out_skew_x: &skew_x, out_skew_y: &skew_y,
674 out_scale_x: &scale_x, out_scale_y: &scale_y,
675 out_angle: &angle,
676 out_dx: &dx, out_dy: &dy);
677 g_assert_cmpfloat_with_epsilon (skew_x, 0, 0.0001);
678 g_assert_cmpfloat_with_epsilon (skew_y, 0, 0.0001);
679 g_assert_cmpfloat_with_epsilon (scale_x, 3, 0.0001);
680 g_assert_cmpfloat_with_epsilon (scale_y, 3, 0.0001);
681 g_assert_cmpfloat_with_epsilon (angle, 22, 0.0001);
682 g_assert_cmpfloat_with_epsilon (dx, 10, 0.0001);
683 g_assert_cmpfloat_with_epsilon (dy, 20, 0.0001);
684
685 gsk_transform_unref (self: transform);
686
687 transform = gsk_transform_skew (
688 next: gsk_transform_scale (
689 next: gsk_transform_rotate (
690 next: gsk_transform_translate (NULL, point: &GRAPHENE_POINT_INIT (10, 20)),
691 angle: 22),
692 factor_x: 3, factor_y: 6),
693 skew_x: 33, skew_y: 0);
694
695 g_assert_true (gsk_transform_get_category (transform) >= GSK_TRANSFORM_CATEGORY_2D);
696
697 gsk_transform_to_2d_components (self: transform,
698 out_skew_x: &skew_x, out_skew_y: &skew_y,
699 out_scale_x: &scale_x, out_scale_y: &scale_y,
700 out_angle: &angle,
701 out_dx: &dx, out_dy: &dy);
702
703 transform2 = gsk_transform_skew (
704 next: gsk_transform_scale (
705 next: gsk_transform_rotate (
706 next: gsk_transform_translate (NULL, point: &GRAPHENE_POINT_INIT (dx, dy)),
707 angle),
708 factor_x: scale_x, factor_y: scale_y),
709 skew_x, skew_y);
710
711 gsk_transform_to_matrix (self: transform, out_matrix: &m);
712 gsk_transform_to_matrix (self: transform2, out_matrix: &m2);
713 g_assert_true (graphene_matrix_near (&m, &m2, 0.001));
714
715 gsk_transform_unref (self: transform);
716 gsk_transform_unref (self: transform2);
717}
718
719static void
720test_transform_point (void)
721{
722 GskTransform *t, *t2;
723 graphene_point_t p;
724
725 t = gsk_transform_scale (next: gsk_transform_translate (NULL, point: &GRAPHENE_POINT_INIT (1, 2)), factor_x: 2, factor_y: 2);
726 t2 = gsk_transform_translate (next: gsk_transform_scale (NULL, factor_x: 2, factor_y: 2), point: &GRAPHENE_POINT_INIT (1, 2));
727
728 gsk_transform_transform_point (self: t, point: &GRAPHENE_POINT_INIT (1,1), out_point: &p);
729 g_assert_true (graphene_point_equal (&p, &GRAPHENE_POINT_INIT (3, 4)));
730
731 gsk_transform_transform_point (self: t2, point: &GRAPHENE_POINT_INIT (1,1), out_point: &p);
732 g_assert_true (graphene_point_equal (&p, &GRAPHENE_POINT_INIT (4, 6)));
733
734 gsk_transform_unref (self: t);
735 gsk_transform_unref (self: t2);
736}
737
738int
739main (int argc,
740 char *argv[])
741{
742 gtk_test_init (argcp: &argc, argvp: &argv, NULL);
743
744 g_test_add_func (testpath: "/transform/conversions/simple", test_func: test_conversions_simple);
745 g_test_add_func (testpath: "/transform/conversions/transformed", test_func: test_conversions_transformed);
746 g_test_add_func (testpath: "/transform/identity", test_func: test_identity);
747 g_test_add_func (testpath: "/transform/identity-equal", test_func: test_identity_equal);
748 g_test_add_func (testpath: "/transform/invert", test_func: test_invert);
749 g_test_add_func (testpath: "/transform/print-parse", test_func: test_print_parse);
750 g_test_add_func (testpath: "/transform/check-axis-aligneness", test_func: test_axis_aligned);
751 g_test_add_func (testpath: "/transform/to-affine", test_func: test_to_affine);
752 g_test_add_func (testpath: "/transform/bounds", test_func: test_transform_bounds);
753 g_test_add_func (testpath: "/transform/point", test_func: test_transform_point);
754 g_test_add_func (testpath: "/transform/to-2d", test_func: test_to_2d);
755 g_test_add_func (testpath: "/transform/to-2d-components", test_func: test_to_2d_components);
756
757 return g_test_run ();
758}
759

source code of gtk/testsuite/gsk/transform.c