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
21 /**
22 * GskTransform: (ref-func gsk_transform_ref) (unref-func gsk_transform_unref)
23 *
24 * `GskTransform` is an object to describe transform matrices.
25 *
26 * Unlike `graphene_matrix_t`, `GskTransform` retains the steps in how
27 * a transform was constructed, and allows inspecting them. It is modeled
28 * after the way CSS describes transforms.
29 *
30 * `GskTransform` objects are immutable and cannot be changed after creation.
31 * This means code can safely expose them as properties of objects without
32 * having to worry about others changing them.
33 */
34
35 #include "config.h"
36
37 #include "gsktransformprivate.h"
38
39 struct _GskTransformClass
40 {
41 gsize struct_size;
42 const char *type_name;
43
44 void (* finalize) (GskTransform *transform);
45 void (* to_matrix) (GskTransform *transform,
46 graphene_matrix_t *out_matrix);
47 void (* apply_2d) (GskTransform *transform,
48 float *out_xx,
49 float *out_yx,
50 float *out_xy,
51 float *out_yy,
52 float *out_dx,
53 float *out_dy);
54 void (* apply_affine) (GskTransform *transform,
55 float *out_scale_x,
56 float *out_scale_y,
57 float *out_dx,
58 float *out_dy);
59 void (* apply_translate) (GskTransform *transform,
60 float *out_dx,
61 float *out_dy);
62 void (* print) (GskTransform *transform,
63 GString *string);
64 GskTransform * (* apply) (GskTransform *transform,
65 GskTransform *apply_to);
66 GskTransform * (* invert) (GskTransform *transform,
67 GskTransform *next);
68 /* both matrices have the same type */
69 gboolean (* equal) (GskTransform *first_transform,
70 GskTransform *second_transform);
71 };
72
73
74 G_DEFINE_BOXED_TYPE (GskTransform, gsk_transform,
75 gsk_transform_ref,
76 gsk_transform_unref)
77
78 static gboolean
79 gsk_transform_is_identity (GskTransform *self);
80 static GskTransform *
81 gsk_transform_matrix_with_category (GskTransform *next,
82 const graphene_matrix_t*matrix,
83 GskTransformCategory category);
84
85 static inline gboolean
gsk_transform_has_class(GskTransform * self,const GskTransformClass * transform_class)86 gsk_transform_has_class (GskTransform *self,
87 const GskTransformClass *transform_class)
88 {
89 return self != NULL && self->transform_class == transform_class;
90 }
91
92 /*< private >
93 * gsk_transform_alloc:
94 * @transform_class: class structure for this self
95 * @category: The category of this transform. Will be used to initialize
96 * the result's category together with &next's category
97 * @next: (transfer full) (nullable): Next transform to multiply with
98 *
99 * Returns: (transfer full): the newly created `GskTransform`
100 */
101 static gpointer
gsk_transform_alloc(const GskTransformClass * transform_class,GskTransformCategory category,GskTransform * next)102 gsk_transform_alloc (const GskTransformClass *transform_class,
103 GskTransformCategory category,
104 GskTransform *next)
105 {
106 GskTransform *self;
107
108 g_return_val_if_fail (transform_class != NULL, NULL);
109
110 self = g_atomic_rc_box_alloc0 (transform_class->struct_size);
111
112 self->transform_class = transform_class;
113 self->category = next ? MIN (category, next->category) : category;
114 if (gsk_transform_is_identity (next))
115 gsk_transform_unref (next);
116 else
117 self->next = next;
118
119 return self;
120 }
121
122 /*** IDENTITY ***/
123
124 static void
gsk_identity_transform_finalize(GskTransform * transform)125 gsk_identity_transform_finalize (GskTransform *transform)
126 {
127 }
128
129 static void
gsk_identity_transform_to_matrix(GskTransform * transform,graphene_matrix_t * out_matrix)130 gsk_identity_transform_to_matrix (GskTransform *transform,
131 graphene_matrix_t *out_matrix)
132 {
133 graphene_matrix_init_identity (out_matrix);
134 }
135
136 static void
gsk_identity_transform_apply_2d(GskTransform * transform,float * out_xx,float * out_yx,float * out_xy,float * out_yy,float * out_dx,float * out_dy)137 gsk_identity_transform_apply_2d (GskTransform *transform,
138 float *out_xx,
139 float *out_yx,
140 float *out_xy,
141 float *out_yy,
142 float *out_dx,
143 float *out_dy)
144 {
145 }
146
147 static void
gsk_identity_transform_apply_affine(GskTransform * transform,float * out_scale_x,float * out_scale_y,float * out_dx,float * out_dy)148 gsk_identity_transform_apply_affine (GskTransform *transform,
149 float *out_scale_x,
150 float *out_scale_y,
151 float *out_dx,
152 float *out_dy)
153 {
154 }
155
156 static void
gsk_identity_transform_apply_translate(GskTransform * transform,float * out_dx,float * out_dy)157 gsk_identity_transform_apply_translate (GskTransform *transform,
158 float *out_dx,
159 float *out_dy)
160 {
161 }
162
163 static void
gsk_identity_transform_print(GskTransform * transform,GString * string)164 gsk_identity_transform_print (GskTransform *transform,
165 GString *string)
166 {
167 g_string_append (string, "none");
168 }
169
170 static GskTransform *
gsk_identity_transform_apply(GskTransform * transform,GskTransform * apply_to)171 gsk_identity_transform_apply (GskTransform *transform,
172 GskTransform *apply_to)
173 {
174 /* We do the following to make sure inverting a non-NULL transform
175 * will return a non-NULL transform.
176 */
177 if (apply_to)
178 return apply_to;
179 else
180 return gsk_transform_new ();
181 }
182
183 static GskTransform *
gsk_identity_transform_invert(GskTransform * transform,GskTransform * next)184 gsk_identity_transform_invert (GskTransform *transform,
185 GskTransform *next)
186 {
187 /* We do the following to make sure inverting a non-NULL transform
188 * will return a non-NULL transform.
189 */
190 if (next)
191 return next;
192 else
193 return gsk_transform_new ();
194 }
195
196 static gboolean
gsk_identity_transform_equal(GskTransform * first_transform,GskTransform * second_transform)197 gsk_identity_transform_equal (GskTransform *first_transform,
198 GskTransform *second_transform)
199 {
200 return TRUE;
201 }
202
203 static const GskTransformClass GSK_IDENTITY_TRANSFORM_CLASS =
204 {
205 sizeof (GskTransform),
206 "GskIdentityTransform",
207 gsk_identity_transform_finalize,
208 gsk_identity_transform_to_matrix,
209 gsk_identity_transform_apply_2d,
210 gsk_identity_transform_apply_affine,
211 gsk_identity_transform_apply_translate,
212 gsk_identity_transform_print,
213 gsk_identity_transform_apply,
214 gsk_identity_transform_invert,
215 gsk_identity_transform_equal,
216 };
217
218 /*<private>
219 * gsk_transform_is_identity:
220 * @transform: (nullable): A transform
221 *
222 * Checks if the transform is a representation of the identity
223 * transform.
224 *
225 * This is different from a transform like `scale(2) scale(0.5)`
226 * which just results in an identity transform when simplified.
227 *
228 * Returns: %TRUE if this transform is a representation of
229 * the identity transform
230 **/
231 static gboolean
gsk_transform_is_identity(GskTransform * self)232 gsk_transform_is_identity (GskTransform *self)
233 {
234 return self == NULL ||
235 (self->transform_class == &GSK_IDENTITY_TRANSFORM_CLASS && gsk_transform_is_identity (self->next));
236 }
237
238 /*** MATRIX ***/
239
240 typedef struct _GskMatrixTransform GskMatrixTransform;
241
242 struct _GskMatrixTransform
243 {
244 GskTransform parent;
245
246 graphene_matrix_t matrix;
247 };
248
249 static void
gsk_matrix_transform_finalize(GskTransform * self)250 gsk_matrix_transform_finalize (GskTransform *self)
251 {
252 }
253
254 static void
gsk_matrix_transform_to_matrix(GskTransform * transform,graphene_matrix_t * out_matrix)255 gsk_matrix_transform_to_matrix (GskTransform *transform,
256 graphene_matrix_t *out_matrix)
257 {
258 GskMatrixTransform *self = (GskMatrixTransform *) transform;
259
260 graphene_matrix_init_from_matrix (out_matrix, &self->matrix);
261 }
262
263 static void
gsk_matrix_transform_apply_2d(GskTransform * transform,float * out_xx,float * out_yx,float * out_xy,float * out_yy,float * out_dx,float * out_dy)264 gsk_matrix_transform_apply_2d (GskTransform *transform,
265 float *out_xx,
266 float *out_yx,
267 float *out_xy,
268 float *out_yy,
269 float *out_dx,
270 float *out_dy)
271 {
272 GskMatrixTransform *self = (GskMatrixTransform *) transform;
273 graphene_matrix_t mat;
274
275 graphene_matrix_init_from_2d (&mat,
276 *out_xx, *out_yx,
277 *out_xy, *out_yy,
278 *out_dx, *out_dy);
279 graphene_matrix_multiply (&self->matrix, &mat, &mat);
280
281 /* not using graphene_matrix_to_2d() because it may
282 * fail the is_2d() check due to improper rounding */
283 *out_xx = graphene_matrix_get_value (&mat, 0, 0);
284 *out_yx = graphene_matrix_get_value (&mat, 0, 1);
285 *out_xy = graphene_matrix_get_value (&mat, 1, 0);
286 *out_yy = graphene_matrix_get_value (&mat, 1, 1);
287 *out_dx = graphene_matrix_get_value (&mat, 3, 0);
288 *out_dy = graphene_matrix_get_value (&mat, 3, 1);
289 }
290
291 static void
gsk_matrix_transform_apply_affine(GskTransform * transform,float * out_scale_x,float * out_scale_y,float * out_dx,float * out_dy)292 gsk_matrix_transform_apply_affine (GskTransform *transform,
293 float *out_scale_x,
294 float *out_scale_y,
295 float *out_dx,
296 float *out_dy)
297 {
298 GskMatrixTransform *self = (GskMatrixTransform *) transform;
299
300 switch (transform->category)
301 {
302 case GSK_TRANSFORM_CATEGORY_UNKNOWN:
303 case GSK_TRANSFORM_CATEGORY_ANY:
304 case GSK_TRANSFORM_CATEGORY_3D:
305 case GSK_TRANSFORM_CATEGORY_2D:
306 default:
307 g_assert_not_reached ();
308 break;
309
310 case GSK_TRANSFORM_CATEGORY_2D_AFFINE:
311 *out_dx += *out_scale_x * graphene_matrix_get_x_translation (&self->matrix);
312 *out_dy += *out_scale_y * graphene_matrix_get_y_translation (&self->matrix);
313 *out_scale_x *= graphene_matrix_get_x_scale (&self->matrix);
314 *out_scale_y *= graphene_matrix_get_y_scale (&self->matrix);
315 break;
316
317 case GSK_TRANSFORM_CATEGORY_2D_TRANSLATE:
318 *out_dx += *out_scale_x * graphene_matrix_get_x_translation (&self->matrix);
319 *out_dy += *out_scale_y * graphene_matrix_get_y_translation (&self->matrix);
320 break;
321
322 case GSK_TRANSFORM_CATEGORY_IDENTITY:
323 break;
324 }
325 }
326
327 static void
gsk_matrix_transform_apply_translate(GskTransform * transform,float * out_dx,float * out_dy)328 gsk_matrix_transform_apply_translate (GskTransform *transform,
329 float *out_dx,
330 float *out_dy)
331 {
332 GskMatrixTransform *self = (GskMatrixTransform *) transform;
333
334 switch (transform->category)
335 {
336 case GSK_TRANSFORM_CATEGORY_UNKNOWN:
337 case GSK_TRANSFORM_CATEGORY_ANY:
338 case GSK_TRANSFORM_CATEGORY_3D:
339 case GSK_TRANSFORM_CATEGORY_2D:
340 case GSK_TRANSFORM_CATEGORY_2D_AFFINE:
341 default:
342 g_assert_not_reached ();
343 break;
344
345 case GSK_TRANSFORM_CATEGORY_2D_TRANSLATE:
346 *out_dx += graphene_matrix_get_x_translation (&self->matrix);
347 *out_dy += graphene_matrix_get_y_translation (&self->matrix);
348 break;
349
350 case GSK_TRANSFORM_CATEGORY_IDENTITY:
351 break;
352 }
353 }
354
355 static void
string_append_double(GString * string,double d)356 string_append_double (GString *string,
357 double d)
358 {
359 char buf[G_ASCII_DTOSTR_BUF_SIZE];
360
361 g_ascii_formatd (buf, G_ASCII_DTOSTR_BUF_SIZE, "%g", d);
362 g_string_append (string, buf);
363 }
364
365 static void
gsk_matrix_transform_print(GskTransform * transform,GString * string)366 gsk_matrix_transform_print (GskTransform *transform,
367 GString *string)
368 {
369 GskMatrixTransform *self = (GskMatrixTransform *) transform;
370 guint i;
371 float f[16];
372
373 g_string_append (string, "matrix3d(");
374 graphene_matrix_to_float (&self->matrix, f);
375 for (i = 0; i < 16; i++)
376 {
377 if (i > 0)
378 g_string_append (string, ", ");
379 string_append_double (string, f[i]);
380 }
381 g_string_append (string, ")");
382 }
383
384 static GskTransform *
gsk_matrix_transform_apply(GskTransform * transform,GskTransform * apply_to)385 gsk_matrix_transform_apply (GskTransform *transform,
386 GskTransform *apply_to)
387 {
388 GskMatrixTransform *self = (GskMatrixTransform *) transform;
389
390 return gsk_transform_matrix_with_category (apply_to,
391 &self->matrix,
392 transform->category);
393 }
394
395 static GskTransform *
gsk_matrix_transform_invert(GskTransform * transform,GskTransform * next)396 gsk_matrix_transform_invert (GskTransform *transform,
397 GskTransform *next)
398 {
399 GskMatrixTransform *self = (GskMatrixTransform *) transform;
400 graphene_matrix_t inverse;
401
402 if (!graphene_matrix_inverse (&self->matrix, &inverse))
403 {
404 gsk_transform_unref (next);
405 return NULL;
406 }
407
408 return gsk_transform_matrix_with_category (next,
409 &inverse,
410 transform->category);
411 }
412
413 static gboolean
gsk_matrix_transform_equal(GskTransform * first_transform,GskTransform * second_transform)414 gsk_matrix_transform_equal (GskTransform *first_transform,
415 GskTransform *second_transform)
416 {
417 GskMatrixTransform *first = (GskMatrixTransform *) first_transform;
418 GskMatrixTransform *second = (GskMatrixTransform *) second_transform;
419
420 if (graphene_matrix_equal_fast (&first->matrix, &second->matrix))
421 return TRUE;
422
423 return graphene_matrix_equal (&first->matrix, &second->matrix);
424 }
425
426 static const GskTransformClass GSK_TRANSFORM_TRANSFORM_CLASS =
427 {
428 sizeof (GskMatrixTransform),
429 "GskMatrixTransform",
430 gsk_matrix_transform_finalize,
431 gsk_matrix_transform_to_matrix,
432 gsk_matrix_transform_apply_2d,
433 gsk_matrix_transform_apply_affine,
434 gsk_matrix_transform_apply_translate,
435 gsk_matrix_transform_print,
436 gsk_matrix_transform_apply,
437 gsk_matrix_transform_invert,
438 gsk_matrix_transform_equal,
439 };
440
441 static GskTransform *
gsk_transform_matrix_with_category(GskTransform * next,const graphene_matrix_t * matrix,GskTransformCategory category)442 gsk_transform_matrix_with_category (GskTransform *next,
443 const graphene_matrix_t *matrix,
444 GskTransformCategory category)
445 {
446 GskMatrixTransform *result = gsk_transform_alloc (&GSK_TRANSFORM_TRANSFORM_CLASS, category, next);
447
448 graphene_matrix_init_from_matrix (&result->matrix, matrix);
449
450 return &result->parent;
451 }
452
453 /**
454 * gsk_transform_matrix:
455 * @next: (nullable) (transfer full): the next transform
456 * @matrix: the matrix to multiply @next with
457 *
458 * Multiplies @next with the given @matrix.
459 *
460 * Returns: The new transform
461 **/
462 GskTransform *
gsk_transform_matrix(GskTransform * next,const graphene_matrix_t * matrix)463 gsk_transform_matrix (GskTransform *next,
464 const graphene_matrix_t *matrix)
465 {
466 return gsk_transform_matrix_with_category (next, matrix, GSK_TRANSFORM_CATEGORY_UNKNOWN);
467 }
468
469 /*** TRANSLATE ***/
470
471 typedef struct _GskTranslateTransform GskTranslateTransform;
472
473 struct _GskTranslateTransform
474 {
475 GskTransform parent;
476
477 graphene_point3d_t point;
478 };
479
480 static void
gsk_translate_transform_finalize(GskTransform * self)481 gsk_translate_transform_finalize (GskTransform *self)
482 {
483 }
484
485 static void
gsk_translate_transform_to_matrix(GskTransform * transform,graphene_matrix_t * out_matrix)486 gsk_translate_transform_to_matrix (GskTransform *transform,
487 graphene_matrix_t *out_matrix)
488 {
489 GskTranslateTransform *self = (GskTranslateTransform *) transform;
490
491 graphene_matrix_init_translate (out_matrix, &self->point);
492 }
493
494 static void
gsk_translate_transform_apply_2d(GskTransform * transform,float * out_xx,float * out_yx,float * out_xy,float * out_yy,float * out_dx,float * out_dy)495 gsk_translate_transform_apply_2d (GskTransform *transform,
496 float *out_xx,
497 float *out_yx,
498 float *out_xy,
499 float *out_yy,
500 float *out_dx,
501 float *out_dy)
502 {
503 GskTranslateTransform *self = (GskTranslateTransform *) transform;
504
505 g_assert (self->point.z == 0.0);
506
507 *out_dx += *out_xx * self->point.x + *out_xy * self->point.y;
508 *out_dy += *out_yx * self->point.x + *out_yy * self->point.y;
509 }
510
511 static void
gsk_translate_transform_apply_affine(GskTransform * transform,float * out_scale_x,float * out_scale_y,float * out_dx,float * out_dy)512 gsk_translate_transform_apply_affine (GskTransform *transform,
513 float *out_scale_x,
514 float *out_scale_y,
515 float *out_dx,
516 float *out_dy)
517 {
518 GskTranslateTransform *self = (GskTranslateTransform *) transform;
519
520 g_assert (self->point.z == 0.0);
521
522 *out_dx += *out_scale_x * self->point.x;
523 *out_dy += *out_scale_y * self->point.y;
524 }
525
526 static void
gsk_translate_transform_apply_translate(GskTransform * transform,float * out_dx,float * out_dy)527 gsk_translate_transform_apply_translate (GskTransform *transform,
528 float *out_dx,
529 float *out_dy)
530 {
531 GskTranslateTransform *self = (GskTranslateTransform *) transform;
532
533 g_assert (self->point.z == 0.0);
534
535 *out_dx += self->point.x;
536 *out_dy += self->point.y;
537 }
538
539 static GskTransform *
gsk_translate_transform_apply(GskTransform * transform,GskTransform * apply_to)540 gsk_translate_transform_apply (GskTransform *transform,
541 GskTransform *apply_to)
542 {
543 GskTranslateTransform *self = (GskTranslateTransform *) transform;
544
545 return gsk_transform_translate_3d (apply_to, &self->point);
546 }
547
548 static GskTransform *
gsk_translate_transform_invert(GskTransform * transform,GskTransform * next)549 gsk_translate_transform_invert (GskTransform *transform,
550 GskTransform *next)
551 {
552 GskTranslateTransform *self = (GskTranslateTransform *) transform;
553
554 return gsk_transform_translate_3d (next, &GRAPHENE_POINT3D_INIT (-self->point.x, -self->point.y, -self->point.z));
555 }
556
557 static gboolean
gsk_translate_transform_equal(GskTransform * first_transform,GskTransform * second_transform)558 gsk_translate_transform_equal (GskTransform *first_transform,
559 GskTransform *second_transform)
560 {
561 GskTranslateTransform *first = (GskTranslateTransform *) first_transform;
562 GskTranslateTransform *second = (GskTranslateTransform *) second_transform;
563
564 return G_APPROX_VALUE (first->point.x, second->point.x, FLT_EPSILON) &&
565 G_APPROX_VALUE (first->point.y, second->point.y, FLT_EPSILON) &&
566 G_APPROX_VALUE (first->point.z, second->point.z, FLT_EPSILON);
567 }
568
569 static void
gsk_translate_transform_print(GskTransform * transform,GString * string)570 gsk_translate_transform_print (GskTransform *transform,
571 GString *string)
572 {
573 GskTranslateTransform *self = (GskTranslateTransform *) transform;
574
575 if (self->point.z == 0)
576 g_string_append (string, "translate(");
577 else
578 g_string_append (string, "translate3d(");
579
580 string_append_double (string, self->point.x);
581 g_string_append (string, ", ");
582 string_append_double (string, self->point.y);
583 if (self->point.z != 0)
584 {
585 g_string_append (string, ", ");
586 string_append_double (string, self->point.z);
587 }
588 g_string_append (string, ")");
589 }
590
591 static const GskTransformClass GSK_TRANSLATE_TRANSFORM_CLASS =
592 {
593 sizeof (GskTranslateTransform),
594 "GskTranslateTransform",
595 gsk_translate_transform_finalize,
596 gsk_translate_transform_to_matrix,
597 gsk_translate_transform_apply_2d,
598 gsk_translate_transform_apply_affine,
599 gsk_translate_transform_apply_translate,
600 gsk_translate_transform_print,
601 gsk_translate_transform_apply,
602 gsk_translate_transform_invert,
603 gsk_translate_transform_equal,
604 };
605
606 /**
607 * gsk_transform_translate:
608 * @next: (nullable) (transfer full): the next transform
609 * @point: the point to translate the transform by
610 *
611 * Translates @next in 2-dimensional space by @point.
612 *
613 * Returns: The new transform
614 **/
615 GskTransform *
gsk_transform_translate(GskTransform * next,const graphene_point_t * point)616 gsk_transform_translate (GskTransform *next,
617 const graphene_point_t *point)
618 {
619 graphene_point3d_t point3d;
620
621 graphene_point3d_init (&point3d, point->x, point->y, 0);
622
623 return gsk_transform_translate_3d (next, &point3d);
624 }
625
626 /**
627 * gsk_transform_translate_3d:
628 * @next: (nullable) (transfer full): the next transform
629 * @point: the point to translate the transform by
630 *
631 * Translates @next by @point.
632 *
633 * Returns: The new transform
634 **/
635 GskTransform *
gsk_transform_translate_3d(GskTransform * next,const graphene_point3d_t * point)636 gsk_transform_translate_3d (GskTransform *next,
637 const graphene_point3d_t *point)
638 {
639 GskTranslateTransform *result;
640
641 if (graphene_point3d_equal (point, graphene_point3d_zero ()))
642 return next;
643
644 if (gsk_transform_has_class (next, &GSK_TRANSLATE_TRANSFORM_CLASS))
645 {
646 GskTranslateTransform *t = (GskTranslateTransform *) next;
647 GskTransform *r = gsk_transform_translate_3d (gsk_transform_ref (next->next),
648 &GRAPHENE_POINT3D_INIT(t->point.x + point->x,
649 t->point.y + point->y,
650 t->point.z + point->z));
651 gsk_transform_unref (next);
652 return r;
653 }
654
655 result = gsk_transform_alloc (&GSK_TRANSLATE_TRANSFORM_CLASS,
656 point->z == 0.0 ? GSK_TRANSFORM_CATEGORY_2D_TRANSLATE
657 : GSK_TRANSFORM_CATEGORY_3D,
658 next);
659
660 graphene_point3d_init_from_point (&result->point, point);
661
662 return &result->parent;
663 }
664
665 /*** ROTATE ***/
666
667 typedef struct _GskRotateTransform GskRotateTransform;
668
669 struct _GskRotateTransform
670 {
671 GskTransform parent;
672
673 float angle;
674 };
675
676 static void
gsk_rotate_transform_finalize(GskTransform * self)677 gsk_rotate_transform_finalize (GskTransform *self)
678 {
679 }
680
681 static inline void
_sincos(float deg,float * out_s,float * out_c)682 _sincos (float deg,
683 float *out_s,
684 float *out_c)
685 {
686 if (deg == 90.0)
687 {
688 *out_c = 0.0;
689 *out_s = 1.0;
690 }
691 else if (deg == 180.0)
692 {
693 *out_c = -1.0;
694 *out_s = 0.0;
695 }
696 else if (deg == 270.0)
697 {
698 *out_c = 0.0;
699 *out_s = -1.0;
700 }
701 else if (deg == 0.0)
702 {
703 *out_c = 1.0;
704 *out_s = 0.0;
705 }
706 else
707 {
708 float angle = deg * M_PI / 180.0;
709
710 #ifdef HAVE_SINCOSF
711 sincosf (angle, out_s, out_c);
712 #else
713 *out_s = sinf (angle);
714 *out_c = cosf (angle);
715 #endif
716
717 }
718 }
719
720 static void
gsk_rotate_transform_to_matrix(GskTransform * transform,graphene_matrix_t * out_matrix)721 gsk_rotate_transform_to_matrix (GskTransform *transform,
722 graphene_matrix_t *out_matrix)
723 {
724 GskRotateTransform *self = (GskRotateTransform *) transform;
725 float c, s;
726
727 _sincos (self->angle, &s, &c);
728
729 graphene_matrix_init_from_2d (out_matrix,
730 c, s,
731 -s, c,
732 0, 0);
733 }
734
735 static void
gsk_rotate_transform_apply_2d(GskTransform * transform,float * out_xx,float * out_yx,float * out_xy,float * out_yy,float * out_dx,float * out_dy)736 gsk_rotate_transform_apply_2d (GskTransform *transform,
737 float *out_xx,
738 float *out_yx,
739 float *out_xy,
740 float *out_yy,
741 float *out_dx,
742 float *out_dy)
743 {
744 GskRotateTransform *self = (GskRotateTransform *) transform;
745 float s, c, xx, xy, yx, yy;
746
747 _sincos (self->angle, &s, &c);
748
749 xx = c * *out_xx + s * *out_xy;
750 yx = c * *out_yx + s * *out_yy;
751 xy = -s * *out_xx + c * *out_xy;
752 yy = -s * *out_yx + c * *out_yy;
753
754 *out_xx = xx;
755 *out_yx = yx;
756 *out_xy = xy;
757 *out_yy = yy;
758 }
759
760 static GskTransform *
gsk_rotate_transform_apply(GskTransform * transform,GskTransform * apply_to)761 gsk_rotate_transform_apply (GskTransform *transform,
762 GskTransform *apply_to)
763 {
764 GskRotateTransform *self = (GskRotateTransform *) transform;
765
766 return gsk_transform_rotate (apply_to, self->angle);
767 }
768
769 static GskTransform *
gsk_rotate_transform_invert(GskTransform * transform,GskTransform * next)770 gsk_rotate_transform_invert (GskTransform *transform,
771 GskTransform *next)
772 {
773 GskRotateTransform *self = (GskRotateTransform *) transform;
774
775 return gsk_transform_rotate (next, - self->angle);
776 }
777
778 static gboolean
gsk_rotate_transform_equal(GskTransform * first_transform,GskTransform * second_transform)779 gsk_rotate_transform_equal (GskTransform *first_transform,
780 GskTransform *second_transform)
781 {
782 GskRotateTransform *first = (GskRotateTransform *) first_transform;
783 GskRotateTransform *second = (GskRotateTransform *) second_transform;
784
785 return G_APPROX_VALUE (first->angle, second->angle, 0.01f);
786 }
787
788 static void
gsk_rotate_transform_print(GskTransform * transform,GString * string)789 gsk_rotate_transform_print (GskTransform *transform,
790 GString *string)
791 {
792 GskRotateTransform *self = (GskRotateTransform *) transform;
793
794 g_string_append (string, "rotate(");
795 string_append_double (string, self->angle);
796 g_string_append (string, ")");
797 }
798
799 static const GskTransformClass GSK_ROTATE_TRANSFORM_CLASS =
800 {
801 sizeof (GskRotateTransform),
802 "GskRotateTransform",
803 gsk_rotate_transform_finalize,
804 gsk_rotate_transform_to_matrix,
805 gsk_rotate_transform_apply_2d,
806 NULL,
807 NULL,
808 gsk_rotate_transform_print,
809 gsk_rotate_transform_apply,
810 gsk_rotate_transform_invert,
811 gsk_rotate_transform_equal,
812 };
813
814 static inline float
normalize_angle(float angle)815 normalize_angle (float angle)
816 {
817
818 if (angle >= 0 && angle < 360)
819 return angle;
820
821 while (angle >= 360)
822 angle -= 360;
823 while (angle < 0)
824 angle += 360;
825
826 /* Due to precision issues we may end up with a result that is just
827 * past the allowed range when rounded. For example, something like
828 * -epsilon + 360 when rounded to a float may end up with 360.
829 * So, we handle these cases by returning the exact value 0.
830 */
831
832 if (angle >= 360)
833 angle = 0;
834
835 g_assert (angle < 360.0);
836 g_assert (angle >= 0.0);
837
838 return angle;
839 }
840
841 /**
842 * gsk_transform_rotate:
843 * @next: (nullable) (transfer full): the next transform
844 * @angle: the rotation angle, in degrees (clockwise)
845 *
846 * Rotates @next @angle degrees in 2D - or in 3D-speak, around the z axis.
847 *
848 * Returns: The new transform
849 */
850 GskTransform *
gsk_transform_rotate(GskTransform * next,float angle)851 gsk_transform_rotate (GskTransform *next,
852 float angle)
853 {
854 GskRotateTransform *result;
855
856 if (angle == 0.0f)
857 return next;
858
859 if (gsk_transform_has_class (next, &GSK_ROTATE_TRANSFORM_CLASS))
860 {
861 GskTransform *r = gsk_transform_rotate (gsk_transform_ref (next->next),
862 ((GskRotateTransform *) next)->angle + angle);
863 gsk_transform_unref (next);
864 return r;
865 }
866
867 result = gsk_transform_alloc (&GSK_ROTATE_TRANSFORM_CLASS,
868 GSK_TRANSFORM_CATEGORY_2D,
869 next);
870
871 result->angle = normalize_angle (angle);
872
873 return &result->parent;
874 }
875
876 /*** ROTATE 3D ***/
877
878 typedef struct _GskRotate3dTransform GskRotate3dTransform;
879
880 struct _GskRotate3dTransform
881 {
882 GskTransform parent;
883
884 float angle;
885 graphene_vec3_t axis;
886 };
887
888 static void
gsk_rotate3d_transform_finalize(GskTransform * self)889 gsk_rotate3d_transform_finalize (GskTransform *self)
890 {
891 }
892
893 static void
gsk_rotate3d_transform_to_matrix(GskTransform * transform,graphene_matrix_t * out_matrix)894 gsk_rotate3d_transform_to_matrix (GskTransform *transform,
895 graphene_matrix_t *out_matrix)
896 {
897 GskRotate3dTransform *self = (GskRotate3dTransform *) transform;
898
899 graphene_matrix_init_rotate (out_matrix, self->angle, &self->axis);
900 }
901
902 static GskTransform *
gsk_rotate3d_transform_apply(GskTransform * transform,GskTransform * apply_to)903 gsk_rotate3d_transform_apply (GskTransform *transform,
904 GskTransform *apply_to)
905 {
906 GskRotate3dTransform *self = (GskRotate3dTransform *) transform;
907
908 return gsk_transform_rotate_3d (apply_to, self->angle, &self->axis);
909 }
910
911 static GskTransform *
gsk_rotate3d_transform_invert(GskTransform * transform,GskTransform * next)912 gsk_rotate3d_transform_invert (GskTransform *transform,
913 GskTransform *next)
914 {
915 GskRotate3dTransform *self = (GskRotate3dTransform *) transform;
916
917 return gsk_transform_rotate_3d (next, - self->angle, &self->axis);
918 }
919
920 static gboolean
gsk_rotate3d_transform_equal(GskTransform * first_transform,GskTransform * second_transform)921 gsk_rotate3d_transform_equal (GskTransform *first_transform,
922 GskTransform *second_transform)
923 {
924 GskRotate3dTransform *first = (GskRotate3dTransform *) first_transform;
925 GskRotate3dTransform *second = (GskRotate3dTransform *) second_transform;
926
927 return G_APPROX_VALUE (first->angle, second->angle, 0.01f) &&
928 graphene_vec3_equal (&first->axis, &second->axis);
929 }
930
931 static void
gsk_rotate3d_transform_print(GskTransform * transform,GString * string)932 gsk_rotate3d_transform_print (GskTransform *transform,
933 GString *string)
934 {
935 GskRotate3dTransform *self = (GskRotate3dTransform *) transform;
936 float f[3];
937 guint i;
938
939 g_string_append (string, "rotate3d(");
940 graphene_vec3_to_float (&self->axis, f);
941 for (i = 0; i < 3; i++)
942 {
943 string_append_double (string, f[i]);
944 g_string_append (string, ", ");
945 }
946 string_append_double (string, self->angle);
947 g_string_append (string, ")");
948 }
949
950 static const GskTransformClass GSK_ROTATE3D_TRANSFORM_CLASS =
951 {
952 sizeof (GskRotate3dTransform),
953 "GskRotate3dTransform",
954 gsk_rotate3d_transform_finalize,
955 gsk_rotate3d_transform_to_matrix,
956 NULL,
957 NULL,
958 NULL,
959 gsk_rotate3d_transform_print,
960 gsk_rotate3d_transform_apply,
961 gsk_rotate3d_transform_invert,
962 gsk_rotate3d_transform_equal,
963 };
964
965 /**
966 * gsk_transform_rotate_3d:
967 * @next: (nullable) (transfer full): the next transform
968 * @angle: the rotation angle, in degrees (clockwise)
969 * @axis: The rotation axis
970 *
971 * Rotates @next @angle degrees around @axis.
972 *
973 * For a rotation in 2D space, use [method@Gsk.Transform.rotate]
974 *
975 * Returns: The new transform
976 */
977 GskTransform *
gsk_transform_rotate_3d(GskTransform * next,float angle,const graphene_vec3_t * axis)978 gsk_transform_rotate_3d (GskTransform *next,
979 float angle,
980 const graphene_vec3_t *axis)
981 {
982 GskRotate3dTransform *result;
983
984 if (graphene_vec3_get_x (axis) == 0.0 && graphene_vec3_get_y (axis) == 0.0)
985 return gsk_transform_rotate (next, angle);
986
987 if (angle == 0.0f)
988 return next;
989
990 result = gsk_transform_alloc (&GSK_ROTATE3D_TRANSFORM_CLASS,
991 GSK_TRANSFORM_CATEGORY_3D,
992 next);
993
994 result->angle = normalize_angle (angle);
995 graphene_vec3_init_from_vec3 (&result->axis, axis);
996
997 return &result->parent;
998 }
999
1000 /*** SCALE ***/
1001
1002 typedef struct _GskScaleTransform GskScaleTransform;
1003
1004 struct _GskScaleTransform
1005 {
1006 GskTransform parent;
1007
1008 float factor_x;
1009 float factor_y;
1010 float factor_z;
1011 };
1012
1013 static void
gsk_scale_transform_finalize(GskTransform * self)1014 gsk_scale_transform_finalize (GskTransform *self)
1015 {
1016 }
1017
1018 static void
gsk_scale_transform_to_matrix(GskTransform * transform,graphene_matrix_t * out_matrix)1019 gsk_scale_transform_to_matrix (GskTransform *transform,
1020 graphene_matrix_t *out_matrix)
1021 {
1022 GskScaleTransform *self = (GskScaleTransform *) transform;
1023
1024 graphene_matrix_init_scale (out_matrix, self->factor_x, self->factor_y, self->factor_z);
1025 }
1026
1027 static void
gsk_scale_transform_apply_2d(GskTransform * transform,float * out_xx,float * out_yx,float * out_xy,float * out_yy,float * out_dx,float * out_dy)1028 gsk_scale_transform_apply_2d (GskTransform *transform,
1029 float *out_xx,
1030 float *out_yx,
1031 float *out_xy,
1032 float *out_yy,
1033 float *out_dx,
1034 float *out_dy)
1035 {
1036 GskScaleTransform *self = (GskScaleTransform *) transform;
1037
1038 g_assert (self->factor_z == 1.0);
1039
1040 *out_xx *= self->factor_x;
1041 *out_yx *= self->factor_x;
1042 *out_xy *= self->factor_y;
1043 *out_yy *= self->factor_y;
1044 }
1045
1046 static void
gsk_scale_transform_apply_affine(GskTransform * transform,float * out_scale_x,float * out_scale_y,float * out_dx,float * out_dy)1047 gsk_scale_transform_apply_affine (GskTransform *transform,
1048 float *out_scale_x,
1049 float *out_scale_y,
1050 float *out_dx,
1051 float *out_dy)
1052 {
1053 GskScaleTransform *self = (GskScaleTransform *) transform;
1054
1055 g_assert (self->factor_z == 1.0);
1056
1057 *out_scale_x *= self->factor_x;
1058 *out_scale_y *= self->factor_y;
1059 }
1060
1061 static GskTransform *
gsk_scale_transform_apply(GskTransform * transform,GskTransform * apply_to)1062 gsk_scale_transform_apply (GskTransform *transform,
1063 GskTransform *apply_to)
1064 {
1065 GskScaleTransform *self = (GskScaleTransform *) transform;
1066
1067 return gsk_transform_scale_3d (apply_to, self->factor_x, self->factor_y, self->factor_z);
1068 }
1069
1070 static GskTransform *
gsk_scale_transform_invert(GskTransform * transform,GskTransform * next)1071 gsk_scale_transform_invert (GskTransform *transform,
1072 GskTransform *next)
1073 {
1074 GskScaleTransform *self = (GskScaleTransform *) transform;
1075
1076 return gsk_transform_scale_3d (next,
1077 1.f / self->factor_x,
1078 1.f / self->factor_y,
1079 1.f / self->factor_z);
1080 }
1081
1082 static gboolean
gsk_scale_transform_equal(GskTransform * first_transform,GskTransform * second_transform)1083 gsk_scale_transform_equal (GskTransform *first_transform,
1084 GskTransform *second_transform)
1085 {
1086 GskScaleTransform *first = (GskScaleTransform *) first_transform;
1087 GskScaleTransform *second = (GskScaleTransform *) second_transform;
1088
1089 return G_APPROX_VALUE (first->factor_x, second->factor_x, FLT_EPSILON) &&
1090 G_APPROX_VALUE (first->factor_y, second->factor_y, FLT_EPSILON) &&
1091 G_APPROX_VALUE (first->factor_z, second->factor_z, FLT_EPSILON);
1092 }
1093
1094 static void
gsk_scale_transform_print(GskTransform * transform,GString * string)1095 gsk_scale_transform_print (GskTransform *transform,
1096 GString *string)
1097 {
1098 GskScaleTransform *self = (GskScaleTransform *) transform;
1099
1100 if (self->factor_z == 1.0)
1101 {
1102 g_string_append (string, "scale(");
1103 string_append_double (string, self->factor_x);
1104 if (self->factor_x != self->factor_y)
1105 {
1106 g_string_append (string, ", ");
1107 string_append_double (string, self->factor_y);
1108 }
1109 g_string_append (string, ")");
1110 }
1111 else
1112 {
1113 g_string_append (string, "scale3d(");
1114 string_append_double (string, self->factor_x);
1115 g_string_append (string, ", ");
1116 string_append_double (string, self->factor_y);
1117 g_string_append (string, ", ");
1118 string_append_double (string, self->factor_z);
1119 g_string_append (string, ")");
1120 }
1121 }
1122
1123 static const GskTransformClass GSK_SCALE_TRANSFORM_CLASS =
1124 {
1125 sizeof (GskScaleTransform),
1126 "GskScaleTransform",
1127 gsk_scale_transform_finalize,
1128 gsk_scale_transform_to_matrix,
1129 gsk_scale_transform_apply_2d,
1130 gsk_scale_transform_apply_affine,
1131 NULL,
1132 gsk_scale_transform_print,
1133 gsk_scale_transform_apply,
1134 gsk_scale_transform_invert,
1135 gsk_scale_transform_equal,
1136 };
1137
1138 /**
1139 * gsk_transform_scale:
1140 * @next: (nullable) (transfer full): the next transform
1141 * @factor_x: scaling factor on the X axis
1142 * @factor_y: scaling factor on the Y axis
1143 *
1144 * Scales @next in 2-dimensional space by the given factors.
1145 *
1146 * Use [method@Gsk.Transform.scale_3d] to scale in all 3 dimensions.
1147 *
1148 * Returns: The new transform
1149 **/
1150 GskTransform *
gsk_transform_scale(GskTransform * next,float factor_x,float factor_y)1151 gsk_transform_scale (GskTransform *next,
1152 float factor_x,
1153 float factor_y)
1154 {
1155 return gsk_transform_scale_3d (next, factor_x, factor_y, 1.0);
1156 }
1157
1158 /**
1159 * gsk_transform_scale_3d:
1160 * @next: (nullable) (transfer full): the next transform
1161 * @factor_x: scaling factor on the X axis
1162 * @factor_y: scaling factor on the Y axis
1163 * @factor_z: scaling factor on the Z axis
1164 *
1165 * Scales @next by the given factors.
1166 *
1167 * Returns: The new transform
1168 **/
1169 GskTransform *
gsk_transform_scale_3d(GskTransform * next,float factor_x,float factor_y,float factor_z)1170 gsk_transform_scale_3d (GskTransform *next,
1171 float factor_x,
1172 float factor_y,
1173 float factor_z)
1174 {
1175 GskScaleTransform *result;
1176
1177 if (factor_x == 1 && factor_y == 1 && factor_z == 1)
1178 return next;
1179
1180 if (gsk_transform_has_class (next, &GSK_SCALE_TRANSFORM_CLASS))
1181 {
1182 GskScaleTransform *scale = (GskScaleTransform *) next;
1183 GskTransform *r = gsk_transform_scale_3d (gsk_transform_ref (next->next),
1184 scale->factor_x * factor_x,
1185 scale->factor_y * factor_y,
1186 scale->factor_z * factor_z);
1187 gsk_transform_unref (next);
1188 return r;
1189 }
1190
1191 result = gsk_transform_alloc (&GSK_SCALE_TRANSFORM_CLASS,
1192 factor_z != 1.0 ? GSK_TRANSFORM_CATEGORY_3D
1193 : GSK_TRANSFORM_CATEGORY_2D_AFFINE,
1194 next);
1195
1196 result->factor_x = factor_x;
1197 result->factor_y = factor_y;
1198 result->factor_z = factor_z;
1199
1200 return &result->parent;
1201 }
1202
1203 /*** PERSPECTIVE ***/
1204
1205 typedef struct _GskPerspectiveTransform GskPerspectiveTransform;
1206
1207 struct _GskPerspectiveTransform
1208 {
1209 GskTransform parent;
1210
1211 float depth;
1212 };
1213
1214 static void
gsk_perspective_transform_finalize(GskTransform * self)1215 gsk_perspective_transform_finalize (GskTransform *self)
1216 {
1217 }
1218
1219 static void
gsk_perspective_transform_to_matrix(GskTransform * transform,graphene_matrix_t * out_matrix)1220 gsk_perspective_transform_to_matrix (GskTransform *transform,
1221 graphene_matrix_t *out_matrix)
1222 {
1223 GskPerspectiveTransform *self = (GskPerspectiveTransform *) transform;
1224 float f[16] = { 1.f, 0.f, 0.f, 0.f,
1225 0.f, 1.f, 0.f, 0.f,
1226 0.f, 0.f, 1.f, self->depth ? -1.f / self->depth : 0.f,
1227 0.f, 0.f, 0.f, 1.f };
1228
1229 graphene_matrix_init_from_float (out_matrix, f);
1230 }
1231
1232
1233 static GskTransform *
gsk_perspective_transform_apply(GskTransform * transform,GskTransform * apply_to)1234 gsk_perspective_transform_apply (GskTransform *transform,
1235 GskTransform *apply_to)
1236 {
1237 GskPerspectiveTransform *self = (GskPerspectiveTransform *) transform;
1238
1239 return gsk_transform_perspective (apply_to, self->depth);
1240 }
1241
1242 static GskTransform *
gsk_perspective_transform_invert(GskTransform * transform,GskTransform * next)1243 gsk_perspective_transform_invert (GskTransform *transform,
1244 GskTransform *next)
1245 {
1246 GskPerspectiveTransform *self = (GskPerspectiveTransform *) transform;
1247
1248 return gsk_transform_perspective (next, - self->depth);
1249 }
1250
1251 static gboolean
gsk_perspective_transform_equal(GskTransform * first_transform,GskTransform * second_transform)1252 gsk_perspective_transform_equal (GskTransform *first_transform,
1253 GskTransform *second_transform)
1254 {
1255 GskPerspectiveTransform *first = (GskPerspectiveTransform *) first_transform;
1256 GskPerspectiveTransform *second = (GskPerspectiveTransform *) second_transform;
1257
1258 return G_APPROX_VALUE (first->depth, second->depth, 0.001f);
1259 }
1260
1261 static void
gsk_perspective_transform_print(GskTransform * transform,GString * string)1262 gsk_perspective_transform_print (GskTransform *transform,
1263 GString *string)
1264 {
1265 GskPerspectiveTransform *self = (GskPerspectiveTransform *) transform;
1266
1267 g_string_append (string, "perspective(");
1268 string_append_double (string, self->depth);
1269 g_string_append (string, ")");
1270 }
1271
1272 static const GskTransformClass GSK_PERSPECTIVE_TRANSFORM_CLASS =
1273 {
1274 sizeof (GskPerspectiveTransform),
1275 "GskPerspectiveTransform",
1276 gsk_perspective_transform_finalize,
1277 gsk_perspective_transform_to_matrix,
1278 NULL,
1279 NULL,
1280 NULL,
1281 gsk_perspective_transform_print,
1282 gsk_perspective_transform_apply,
1283 gsk_perspective_transform_invert,
1284 gsk_perspective_transform_equal,
1285 };
1286
1287 /**
1288 * gsk_transform_perspective:
1289 * @next: (nullable) (transfer full): the next transform
1290 * @depth: distance of the z=0 plane. Lower values give a more
1291 * flattened pyramid and therefore a more pronounced
1292 * perspective effect.
1293 *
1294 * Applies a perspective projection transform.
1295 *
1296 * This transform scales points in X and Y based on their Z value,
1297 * scaling points with positive Z values away from the origin, and
1298 * those with negative Z values towards the origin. Points
1299 * on the z=0 plane are unchanged.
1300 *
1301 * Returns: The new transform
1302 */
1303 GskTransform *
gsk_transform_perspective(GskTransform * next,float depth)1304 gsk_transform_perspective (GskTransform *next,
1305 float depth)
1306 {
1307 GskPerspectiveTransform *result;
1308
1309 if (gsk_transform_has_class (next, &GSK_PERSPECTIVE_TRANSFORM_CLASS))
1310 {
1311 GskTransform *r = gsk_transform_perspective (gsk_transform_ref (next->next),
1312 ((GskPerspectiveTransform *) next)->depth + depth);
1313 gsk_transform_unref (next);
1314 return r;
1315 }
1316
1317 result = gsk_transform_alloc (&GSK_PERSPECTIVE_TRANSFORM_CLASS,
1318 GSK_TRANSFORM_CATEGORY_ANY,
1319 next);
1320
1321 result->depth = depth;
1322
1323 return &result->parent;
1324 }
1325
1326 /*** PUBLIC API ***/
1327
1328 static void
gsk_transform_finalize(GskTransform * self)1329 gsk_transform_finalize (GskTransform *self)
1330 {
1331 self->transform_class->finalize (self);
1332
1333 gsk_transform_unref (self->next);
1334 }
1335
1336 /**
1337 * gsk_transform_ref:
1338 * @self: (nullable): a `GskTransform`
1339 *
1340 * Acquires a reference on the given `GskTransform`.
1341 *
1342 * Returns: (transfer none): the `GskTransform` with an additional reference
1343 */
1344 GskTransform *
gsk_transform_ref(GskTransform * self)1345 gsk_transform_ref (GskTransform *self)
1346 {
1347 if (self == NULL)
1348 return NULL;
1349
1350 return g_atomic_rc_box_acquire (self);
1351 }
1352
1353 /**
1354 * gsk_transform_unref:
1355 * @self: (nullable): a `GskTransform`
1356 *
1357 * Releases a reference on the given `GskTransform`.
1358 *
1359 * If the reference was the last, the resources associated to the @self are
1360 * freed.
1361 */
1362 void
gsk_transform_unref(GskTransform * self)1363 gsk_transform_unref (GskTransform *self)
1364 {
1365 if (self == NULL)
1366 return;
1367
1368 g_atomic_rc_box_release_full (self, (GDestroyNotify) gsk_transform_finalize);
1369 }
1370
1371 /**
1372 * gsk_transform_print:
1373 * @self: (nullable): a `GskTransform`
1374 * @string: The string to print into
1375 *
1376 * Converts @self into a human-readable string representation suitable
1377 * for printing.
1378 *
1379 * The result of this function can later be parsed with
1380 * [func@Gsk.Transform.parse].
1381 */
1382 void
gsk_transform_print(GskTransform * self,GString * string)1383 gsk_transform_print (GskTransform *self,
1384 GString *string)
1385 {
1386 g_return_if_fail (string != NULL);
1387
1388 if (self == NULL)
1389 {
1390 g_string_append (string, "none");
1391 return;
1392 }
1393
1394 if (self->next != NULL)
1395 {
1396 gsk_transform_print (self->next, string);
1397 g_string_append (string, " ");
1398 }
1399
1400 self->transform_class->print (self, string);
1401 }
1402
1403 /**
1404 * gsk_transform_to_string:
1405 * @self: (nullable): a `GskTransform`
1406 *
1407 * Converts a matrix into a string that is suitable for printing.
1408 *
1409 * The resulting string can be parsed with [func@Gsk.Transform.parse].
1410 *
1411 * This is a wrapper around [method@Gsk.Transform.print].
1412 *
1413 * Returns: A new string for @self
1414 */
1415 char *
gsk_transform_to_string(GskTransform * self)1416 gsk_transform_to_string (GskTransform *self)
1417 {
1418 GString *string;
1419
1420 string = g_string_new ("");
1421
1422 gsk_transform_print (self, string);
1423
1424 return g_string_free (string, FALSE);
1425 }
1426
1427 /**
1428 * gsk_transform_to_matrix:
1429 * @self: (nullable): a `GskTransform`
1430 * @out_matrix: (out caller-allocates): The matrix to set
1431 *
1432 * Computes the actual value of @self and stores it in @out_matrix.
1433 *
1434 * The previous value of @out_matrix will be ignored.
1435 */
1436 void
gsk_transform_to_matrix(GskTransform * self,graphene_matrix_t * out_matrix)1437 gsk_transform_to_matrix (GskTransform *self,
1438 graphene_matrix_t *out_matrix)
1439 {
1440 graphene_matrix_t m;
1441
1442 if (self == NULL)
1443 {
1444 graphene_matrix_init_identity (out_matrix);
1445 return;
1446 }
1447
1448 gsk_transform_to_matrix (self->next, out_matrix);
1449 self->transform_class->to_matrix (self, &m);
1450 graphene_matrix_multiply (&m, out_matrix, out_matrix);
1451 }
1452
1453 /**
1454 * gsk_transform_to_2d:
1455 * @self: a 2D `GskTransform`
1456 * @out_xx: (out): return location for the xx member
1457 * @out_yx: (out): return location for the yx member
1458 * @out_xy: (out): return location for the xy member
1459 * @out_yy: (out): return location for the yy member
1460 * @out_dx: (out): return location for the x0 member
1461 * @out_dy: (out): return location for the y0 member
1462 *
1463 * Converts a `GskTransform` to a 2D transformation matrix.
1464 *
1465 * @self must be a 2D transformation. If you are not
1466 * sure, use gsk_transform_get_category() >=
1467 * %GSK_TRANSFORM_CATEGORY_2D to check.
1468 *
1469 * The returned values have the following layout:
1470 *
1471 * ```
1472 * | xx yx | | a b 0 |
1473 * | xy yy | = | c d 0 |
1474 * | dx dy | | tx ty 1 |
1475 * ```
1476 *
1477 * This function can be used to convert between a `GskTransform`
1478 * and a matrix type from other 2D drawing libraries, in particular
1479 * Cairo.
1480 */
1481 void
gsk_transform_to_2d(GskTransform * self,float * out_xx,float * out_yx,float * out_xy,float * out_yy,float * out_dx,float * out_dy)1482 gsk_transform_to_2d (GskTransform *self,
1483 float *out_xx,
1484 float *out_yx,
1485 float *out_xy,
1486 float *out_yy,
1487 float *out_dx,
1488 float *out_dy)
1489 {
1490 *out_xx = 1.0f;
1491 *out_yx = 0.0f;
1492 *out_xy = 0.0f;
1493 *out_yy = 1.0f;
1494 *out_dx = 0.0f;
1495 *out_dy = 0.0f;
1496
1497 if (self == NULL)
1498 return;
1499
1500 if (G_UNLIKELY (self->category < GSK_TRANSFORM_CATEGORY_2D))
1501 {
1502 char *s = gsk_transform_to_string (self);
1503 g_warning ("Given transform \"%s\" is not a 2D transform.", s);
1504 g_free (s);
1505 return;
1506 }
1507
1508 gsk_transform_to_2d (self->next,
1509 out_xx, out_yx,
1510 out_xy, out_yy,
1511 out_dx, out_dy);
1512
1513 self->transform_class->apply_2d (self,
1514 out_xx, out_yx,
1515 out_xy, out_yy,
1516 out_dx, out_dy);
1517 }
1518
1519 /**
1520 * gsk_transform_to_affine:
1521 * @self: a `GskTransform`
1522 * @out_scale_x: (out): return location for the scale
1523 * factor in the x direction
1524 * @out_scale_y: (out): return location for the scale
1525 * factor in the y direction
1526 * @out_dx: (out): return location for the translation
1527 * in the x direction
1528 * @out_dy: (out): return location for the translation
1529 * in the y direction
1530 *
1531 * Converts a `GskTransform` to 2D affine transformation factors.
1532 *
1533 * @self must be a 2D transformation. If you are not
1534 * sure, use
1535 *
1536 * gsk_transform_get_category() >= %GSK_TRANSFORM_CATEGORY_2D_AFFINE
1537 *
1538 * to check.
1539 */
1540 void
gsk_transform_to_affine(GskTransform * self,float * out_scale_x,float * out_scale_y,float * out_dx,float * out_dy)1541 gsk_transform_to_affine (GskTransform *self,
1542 float *out_scale_x,
1543 float *out_scale_y,
1544 float *out_dx,
1545 float *out_dy)
1546 {
1547 *out_scale_x = 1.0f;
1548 *out_scale_y = 1.0f;
1549 *out_dx = 0.0f;
1550 *out_dy = 0.0f;
1551
1552 if (self == NULL)
1553 return;
1554
1555 if (G_UNLIKELY (self->category < GSK_TRANSFORM_CATEGORY_2D_AFFINE))
1556 {
1557 char *s = gsk_transform_to_string (self);
1558 g_warning ("Given transform \"%s\" is not an affine 2D transform.", s);
1559 g_free (s);
1560 return;
1561 }
1562
1563 gsk_transform_to_affine (self->next,
1564 out_scale_x, out_scale_y,
1565 out_dx, out_dy);
1566
1567 self->transform_class->apply_affine (self,
1568 out_scale_x, out_scale_y,
1569 out_dx, out_dy);
1570 }
1571
1572 /**
1573 * gsk_transform_to_translate:
1574 * @self: a `GskTransform`
1575 * @out_dx: (out): return location for the translation
1576 * in the x direction
1577 * @out_dy: (out): return location for the translation
1578 * in the y direction
1579 *
1580 * Converts a `GskTransform` to a translation operation.
1581 *
1582 * @self must be a 2D transformation. If you are not
1583 * sure, use
1584 *
1585 * gsk_transform_get_category() >= %GSK_TRANSFORM_CATEGORY_2D_TRANSLATE
1586 *
1587 * to check.
1588 */
1589 void
gsk_transform_to_translate(GskTransform * self,float * out_dx,float * out_dy)1590 gsk_transform_to_translate (GskTransform *self,
1591 float *out_dx,
1592 float *out_dy)
1593 {
1594 *out_dx = 0.0f;
1595 *out_dy = 0.0f;
1596
1597 if (self == NULL)
1598 return;
1599
1600 if (G_UNLIKELY (self->category < GSK_TRANSFORM_CATEGORY_2D_TRANSLATE))
1601 {
1602 char *s = gsk_transform_to_string (self);
1603 g_warning ("Given transform \"%s\" is not an affine 2D translation.", s);
1604 g_free (s);
1605
1606 return;
1607 }
1608
1609 gsk_transform_to_translate (self->next, out_dx, out_dy);
1610
1611 self->transform_class->apply_translate (self, out_dx, out_dy);
1612 }
1613
1614 /**
1615 * gsk_transform_transform:
1616 * @next: (nullable) (transfer full): Transform to apply @other to
1617 * @other: (nullable): Transform to apply
1618 *
1619 * Applies all the operations from @other to @next.
1620 *
1621 * Returns: The new transform
1622 */
1623 GskTransform *
gsk_transform_transform(GskTransform * next,GskTransform * other)1624 gsk_transform_transform (GskTransform *next,
1625 GskTransform *other)
1626 {
1627 if (other == NULL)
1628 return next;
1629
1630 if (next == NULL)
1631 return gsk_transform_ref (other);
1632
1633 if (gsk_transform_is_identity (next))
1634 {
1635 /* ref before unref to avoid catastrophe when other == next */
1636 other = gsk_transform_ref (other);
1637 gsk_transform_unref (next);
1638 return other;
1639 }
1640
1641 next = gsk_transform_transform (next, other->next);
1642 return other->transform_class->apply (other, next);
1643 }
1644
1645 /**
1646 * gsk_transform_invert:
1647 * @self: (nullable) (transfer full): Transform to invert
1648 *
1649 * Inverts the given transform.
1650 *
1651 * If @self is not invertible, %NULL is returned.
1652 * Note that inverting %NULL also returns %NULL, which is
1653 * the correct inverse of %NULL. If you need to differentiate
1654 * between those cases, you should check @self is not %NULL
1655 * before calling this function.
1656 *
1657 * Returns: (nullable): The inverted transform
1658 */
1659 GskTransform *
gsk_transform_invert(GskTransform * self)1660 gsk_transform_invert (GskTransform *self)
1661 {
1662 GskTransform *result = NULL;
1663 GskTransform *cur;
1664
1665 for (cur = self; cur; cur = cur->next)
1666 {
1667 result = cur->transform_class->invert (cur, result);
1668 if (result == NULL)
1669 break;
1670 }
1671
1672 gsk_transform_unref (self);
1673
1674 return result;
1675 }
1676
1677 /**
1678 * gsk_transform_equal:
1679 * @first: (nullable): the first transform
1680 * @second: (nullable): the second transform
1681 *
1682 * Checks two transforms for equality.
1683 *
1684 * Returns: %TRUE if the two transforms perform the same operation
1685 */
1686 gboolean
gsk_transform_equal(GskTransform * first,GskTransform * second)1687 gsk_transform_equal (GskTransform *first,
1688 GskTransform *second)
1689 {
1690 if (first == second)
1691 return TRUE;
1692
1693 if (first == NULL)
1694 return gsk_transform_is_identity (second);
1695
1696 if (second == NULL)
1697 return gsk_transform_is_identity (first);
1698
1699 if (first->transform_class != second->transform_class)
1700 return FALSE;
1701
1702 if (!gsk_transform_equal (first->next, second->next))
1703 return FALSE;
1704
1705 return first->transform_class->equal (first, second);
1706 }
1707
1708 /**
1709 * gsk_transform_get_category:
1710 * @self: (nullable): A `GskTransform`
1711 *
1712 * Returns the category this transform belongs to.
1713 *
1714 * Returns: The category of the transform
1715 **/
GskTransformCategory(gsk_transform_get_category)1716 GskTransformCategory
1717 (gsk_transform_get_category) (GskTransform *self)
1718 {
1719 if (self == NULL)
1720 return GSK_TRANSFORM_CATEGORY_IDENTITY;
1721
1722 return self->category;
1723 }
1724
1725 /*
1726 * gsk_transform_new: (constructor):
1727 *
1728 * Creates a new identity transform.
1729 *
1730 * This function is meant to be used by language
1731 * bindings. For C code, this is equivalent to using %NULL.
1732 *
1733 * Returns: A new identity transform
1734 */
1735 GskTransform *
gsk_transform_new(void)1736 gsk_transform_new (void)
1737 {
1738 return gsk_transform_alloc (&GSK_IDENTITY_TRANSFORM_CLASS, GSK_TRANSFORM_CATEGORY_IDENTITY, NULL);
1739 }
1740
1741 /**
1742 * gsk_transform_transform_bounds:
1743 * @self: a `GskTransform`
1744 * @rect: a `graphene_rect_t`
1745 * @out_rect: (out caller-allocates): return location for the bounds
1746 * of the transformed rectangle
1747 *
1748 * Transforms a `graphene_rect_t` using the given transform @self.
1749 *
1750 * The result is the bounding box containing the coplanar quad.
1751 */
1752 void
gsk_transform_transform_bounds(GskTransform * self,const graphene_rect_t * rect,graphene_rect_t * out_rect)1753 gsk_transform_transform_bounds (GskTransform *self,
1754 const graphene_rect_t *rect,
1755 graphene_rect_t *out_rect)
1756 {
1757 switch (gsk_transform_get_category (self))
1758 {
1759 case GSK_TRANSFORM_CATEGORY_IDENTITY:
1760 graphene_rect_init_from_rect (out_rect, rect);
1761 break;
1762
1763 case GSK_TRANSFORM_CATEGORY_2D_TRANSLATE:
1764 {
1765 float dx, dy;
1766
1767 gsk_transform_to_translate (self, &dx, &dy);
1768 graphene_rect_init (out_rect,
1769 rect->origin.x + dx,
1770 rect->origin.y + dy,
1771 rect->size.width,
1772 rect->size.height);
1773 }
1774 break;
1775
1776 case GSK_TRANSFORM_CATEGORY_2D_AFFINE:
1777 {
1778 float dx, dy, scale_x, scale_y;
1779
1780 gsk_transform_to_affine (self, &scale_x, &scale_y, &dx, &dy);
1781
1782 graphene_rect_init (out_rect,
1783 (rect->origin.x * scale_x) + dx,
1784 (rect->origin.y * scale_y) + dy,
1785 rect->size.width * scale_x,
1786 rect->size.height * scale_y);
1787 }
1788 break;
1789
1790 case GSK_TRANSFORM_CATEGORY_UNKNOWN:
1791 case GSK_TRANSFORM_CATEGORY_ANY:
1792 case GSK_TRANSFORM_CATEGORY_3D:
1793 case GSK_TRANSFORM_CATEGORY_2D:
1794 default:
1795 {
1796 graphene_matrix_t mat;
1797
1798 gsk_transform_to_matrix (self, &mat);
1799 gsk_matrix_transform_bounds (&mat, rect, out_rect);
1800 }
1801 break;
1802 }
1803 }
1804
1805 /**
1806 * gsk_transform_transform_point:
1807 * @self: a `GskTransform`
1808 * @point: a `graphene_point_t`
1809 * @out_point: (out caller-allocates): return location for
1810 * the transformed point
1811 *
1812 * Transforms a `graphene_point_t` using the given transform @self.
1813 */
1814 void
gsk_transform_transform_point(GskTransform * self,const graphene_point_t * point,graphene_point_t * out_point)1815 gsk_transform_transform_point (GskTransform *self,
1816 const graphene_point_t *point,
1817 graphene_point_t *out_point)
1818 {
1819 switch (gsk_transform_get_category (self))
1820 {
1821 case GSK_TRANSFORM_CATEGORY_IDENTITY:
1822 *out_point = *point;
1823 break;
1824
1825 case GSK_TRANSFORM_CATEGORY_2D_TRANSLATE:
1826 {
1827 float dx, dy;
1828
1829 gsk_transform_to_translate (self, &dx, &dy);
1830 out_point->x = point->x + dx;
1831 out_point->y = point->y + dy;
1832 }
1833 break;
1834
1835 case GSK_TRANSFORM_CATEGORY_2D_AFFINE:
1836 {
1837 float dx, dy, scale_x, scale_y;
1838
1839 gsk_transform_to_affine (self, &scale_x, &scale_y, &dx, &dy);
1840
1841 out_point->x = (point->x * scale_x) + dx;
1842 out_point->y = (point->y * scale_y) + dy;
1843 }
1844 break;
1845
1846 case GSK_TRANSFORM_CATEGORY_UNKNOWN:
1847 case GSK_TRANSFORM_CATEGORY_ANY:
1848 case GSK_TRANSFORM_CATEGORY_3D:
1849 case GSK_TRANSFORM_CATEGORY_2D:
1850 default:
1851 {
1852 graphene_matrix_t mat;
1853
1854 gsk_transform_to_matrix (self, &mat);
1855 gsk_matrix_transform_point (&mat, point, out_point);
1856 }
1857 break;
1858 }
1859 }
1860
1861 static guint
gsk_transform_parse_float(GtkCssParser * parser,guint n,gpointer data)1862 gsk_transform_parse_float (GtkCssParser *parser,
1863 guint n,
1864 gpointer data)
1865 {
1866 float *f = data;
1867 double d;
1868
1869 if (!gtk_css_parser_consume_number (parser, &d))
1870 return 0;
1871
1872 f[n] = d;
1873 return 1;
1874 }
1875
1876 static guint
gsk_transform_parse_scale(GtkCssParser * parser,guint n,gpointer data)1877 gsk_transform_parse_scale (GtkCssParser *parser,
1878 guint n,
1879 gpointer data)
1880 {
1881 float *f = data;
1882 double d;
1883
1884 if (!gtk_css_parser_consume_number (parser, &d))
1885 return 0;
1886
1887 f[n] = d;
1888 f[1] = d;
1889 return 1;
1890 }
1891
1892 gboolean
gsk_transform_parser_parse(GtkCssParser * parser,GskTransform ** out_transform)1893 gsk_transform_parser_parse (GtkCssParser *parser,
1894 GskTransform **out_transform)
1895 {
1896 const GtkCssToken *token;
1897 GskTransform *transform = NULL;
1898 float f[16] = { 0, };
1899 gboolean parsed_something = FALSE;
1900
1901 token = gtk_css_parser_get_token (parser);
1902 if (gtk_css_token_is_ident (token, "none"))
1903 {
1904 gtk_css_parser_consume_token (parser);
1905 *out_transform = NULL;
1906 return TRUE;
1907 }
1908
1909 while (TRUE)
1910 {
1911 if (gtk_css_token_is_function (token, "matrix"))
1912 {
1913 graphene_matrix_t matrix;
1914 if (!gtk_css_parser_consume_function (parser, 6, 6, gsk_transform_parse_float, f))
1915 goto fail;
1916
1917 graphene_matrix_init_from_2d (&matrix, f[0], f[1], f[2], f[3], f[4], f[5]);
1918 transform = gsk_transform_matrix_with_category (transform,
1919 &matrix,
1920 GSK_TRANSFORM_CATEGORY_2D);
1921 }
1922 else if (gtk_css_token_is_function (token, "matrix3d"))
1923 {
1924 graphene_matrix_t matrix;
1925 if (!gtk_css_parser_consume_function (parser, 16, 16, gsk_transform_parse_float, f))
1926 goto fail;
1927
1928 graphene_matrix_init_from_float (&matrix, f);
1929 transform = gsk_transform_matrix (transform, &matrix);
1930 }
1931 else if (gtk_css_token_is_function (token, "perspective"))
1932 {
1933 if (!gtk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
1934 goto fail;
1935
1936 transform = gsk_transform_perspective (transform, f[0]);
1937 }
1938 else if (gtk_css_token_is_function (token, "rotate") ||
1939 gtk_css_token_is_function (token, "rotateZ"))
1940 {
1941 if (!gtk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
1942 goto fail;
1943
1944 transform = gsk_transform_rotate (transform, f[0]);
1945 }
1946 else if (gtk_css_token_is_function (token, "rotate3d"))
1947 {
1948 graphene_vec3_t axis;
1949
1950 if (!gtk_css_parser_consume_function (parser, 4, 4, gsk_transform_parse_float, f))
1951 goto fail;
1952
1953 graphene_vec3_init (&axis, f[0], f[1], f[2]);
1954 transform = gsk_transform_rotate_3d (transform, f[3], &axis);
1955 }
1956 else if (gtk_css_token_is_function (token, "rotateX"))
1957 {
1958 if (!gtk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
1959 goto fail;
1960
1961 transform = gsk_transform_rotate_3d (transform, f[0], graphene_vec3_x_axis ());
1962 }
1963 else if (gtk_css_token_is_function (token, "rotateY"))
1964 {
1965 if (!gtk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
1966 goto fail;
1967
1968 transform = gsk_transform_rotate_3d (transform, f[0], graphene_vec3_y_axis ());
1969 }
1970 else if (gtk_css_token_is_function (token, "scale"))
1971 {
1972 if (!gtk_css_parser_consume_function (parser, 1, 2, gsk_transform_parse_scale, f))
1973 goto fail;
1974
1975 transform = gsk_transform_scale (transform, f[0], f[1]);
1976 }
1977 else if (gtk_css_token_is_function (token, "scale3d"))
1978 {
1979 if (!gtk_css_parser_consume_function (parser, 3, 3, gsk_transform_parse_float, f))
1980 goto fail;
1981
1982 transform = gsk_transform_scale_3d (transform, f[0], f[1], f[2]);
1983 }
1984 else if (gtk_css_token_is_function (token, "scaleX"))
1985 {
1986 if (!gtk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
1987 goto fail;
1988
1989 transform = gsk_transform_scale (transform, f[0], 1.f);
1990 }
1991 else if (gtk_css_token_is_function (token, "scaleY"))
1992 {
1993 if (!gtk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
1994 goto fail;
1995
1996 transform = gsk_transform_scale (transform, 1.f, f[0]);
1997 }
1998 else if (gtk_css_token_is_function (token, "scaleZ"))
1999 {
2000 if (!gtk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
2001 goto fail;
2002
2003 transform = gsk_transform_scale_3d (transform, 1.f, 1.f, f[0]);
2004 }
2005 else if (gtk_css_token_is_function (token, "translate"))
2006 {
2007 f[1] = 0.f;
2008 if (!gtk_css_parser_consume_function (parser, 1, 2, gsk_transform_parse_float, f))
2009 goto fail;
2010
2011 transform = gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (f[0], f[1]));
2012 }
2013 else if (gtk_css_token_is_function (token, "translate3d"))
2014 {
2015 if (!gtk_css_parser_consume_function (parser, 3, 3, gsk_transform_parse_float, f))
2016 goto fail;
2017
2018 transform = gsk_transform_translate_3d (transform, &GRAPHENE_POINT3D_INIT (f[0], f[1], f[2]));
2019 }
2020 else if (gtk_css_token_is_function (token, "translateX"))
2021 {
2022 if (!gtk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
2023 goto fail;
2024
2025 transform = gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (f[0], 0.f));
2026 }
2027 else if (gtk_css_token_is_function (token, "translateY"))
2028 {
2029 if (!gtk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
2030 goto fail;
2031
2032 transform = gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (0.f, f[0]));
2033 }
2034 else if (gtk_css_token_is_function (token, "translateZ"))
2035 {
2036 if (!gtk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
2037 goto fail;
2038
2039 transform = gsk_transform_translate_3d (transform, &GRAPHENE_POINT3D_INIT (0.f, 0.f, f[0]));
2040 }
2041 else if (gtk_css_token_is_function (token, "skew"))
2042 {
2043 graphene_matrix_t matrix;
2044
2045 if (!gtk_css_parser_consume_function (parser, 2, 2, gsk_transform_parse_float, f))
2046 goto fail;
2047
2048 f[0] = f[0] / 180.0 * G_PI;
2049 f[1] = f[1] / 180.0 * G_PI;
2050
2051 graphene_matrix_init_skew (&matrix, f[0], f[1]);
2052 transform = gsk_transform_matrix (transform, &matrix);
2053 }
2054 else if (gtk_css_token_is_function (token, "skewX"))
2055 {
2056 graphene_matrix_t matrix;
2057
2058 if (!gtk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
2059 goto fail;
2060
2061 f[0] = f[0] / 180.0 * G_PI;
2062
2063 graphene_matrix_init_skew (&matrix, f[0], 0);
2064 transform = gsk_transform_matrix (transform, &matrix);
2065 }
2066 else if (gtk_css_token_is_function (token, "skewY"))
2067 {
2068 graphene_matrix_t matrix;
2069
2070 if (!gtk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
2071 goto fail;
2072
2073 f[0] = f[0] / 180.0 * G_PI;
2074
2075 graphene_matrix_init_skew (&matrix, 0, f[0]);
2076 transform = gsk_transform_matrix (transform, &matrix);
2077 }
2078 else
2079 {
2080 break;
2081 }
2082
2083 parsed_something = TRUE;
2084 token = gtk_css_parser_get_token (parser);
2085 }
2086
2087 if (!parsed_something)
2088 {
2089 gtk_css_parser_error_syntax (parser, "Expected a transform");
2090 goto fail;
2091 }
2092
2093 *out_transform = transform;
2094 return TRUE;
2095
2096 fail:
2097 gsk_transform_unref (transform);
2098 *out_transform = NULL;
2099 return FALSE;
2100 }
2101
2102 /**
2103 * gsk_transform_parse:
2104 * @string: the string to parse
2105 * @out_transform: (out): The location to put the transform in
2106 *
2107 * Parses the given @string into a transform and puts it in
2108 * @out_transform.
2109 *
2110 * Strings printed via [method@Gsk.Transform.to_string]
2111 * can be read in again successfully using this function.
2112 *
2113 * If @string does not describe a valid transform, %FALSE is
2114 * returned and %NULL is put in @out_transform.
2115 *
2116 * Returns: %TRUE if @string described a valid transform.
2117 */
2118 gboolean
gsk_transform_parse(const char * string,GskTransform ** out_transform)2119 gsk_transform_parse (const char *string,
2120 GskTransform **out_transform)
2121 {
2122 GtkCssParser *parser;
2123 GBytes *bytes;
2124 gboolean result;
2125
2126 g_return_val_if_fail (string != NULL, FALSE);
2127 g_return_val_if_fail (out_transform != NULL, FALSE);
2128
2129 bytes = g_bytes_new_static (string, strlen (string));
2130 parser = gtk_css_parser_new_for_bytes (bytes, NULL, NULL, NULL, NULL, NULL);
2131
2132 result = gsk_transform_parser_parse (parser, out_transform);
2133
2134 if (result && !gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_EOF))
2135 {
2136 g_clear_pointer (out_transform, gsk_transform_unref);
2137 result = FALSE;
2138 }
2139 gtk_css_parser_unref (parser);
2140 g_bytes_unref (bytes);
2141
2142 return result;
2143 }
2144
2145 /* Some of the graphene_matrix_transform apis yield unexpected
2146 * results with projective matrices, since they silently drop
2147 * the w component, so we provide working alternatives here.
2148 */
2149 void
gsk_matrix_transform_point(const graphene_matrix_t * m,const graphene_point_t * p,graphene_point_t * res)2150 gsk_matrix_transform_point (const graphene_matrix_t *m,
2151 const graphene_point_t *p,
2152 graphene_point_t *res)
2153 {
2154 graphene_vec4_t vec4;
2155 float w;
2156
2157 graphene_vec4_init (&vec4, p->x, p->y, 0.0f, 1.0f);
2158 graphene_matrix_transform_vec4 (m, &vec4, &vec4);
2159
2160 w = graphene_vec4_get_w (&vec4);
2161 res->x = graphene_vec4_get_x (&vec4) / w;
2162 res->y = graphene_vec4_get_y (&vec4) / w;
2163 }
2164
2165 void
gsk_matrix_transform_point3d(const graphene_matrix_t * m,const graphene_point3d_t * p,graphene_point3d_t * res)2166 gsk_matrix_transform_point3d (const graphene_matrix_t *m,
2167 const graphene_point3d_t *p,
2168 graphene_point3d_t *res)
2169 {
2170 graphene_vec4_t vec4;
2171 float w;
2172
2173 graphene_vec4_init (&vec4, p->x, p->y, 0.0f, 1.0f);
2174 graphene_matrix_transform_vec4 (m, &vec4, &vec4);
2175
2176 w = graphene_vec4_get_w (&vec4);
2177 res->x = graphene_vec4_get_x (&vec4) / w;
2178 res->y = graphene_vec4_get_y (&vec4) / w;
2179 res->z = graphene_vec4_get_z (&vec4) / w;
2180 }
2181
2182 void
gsk_matrix_transform_rect(const graphene_matrix_t * m,const graphene_rect_t * r,graphene_quad_t * res)2183 gsk_matrix_transform_rect (const graphene_matrix_t *m,
2184 const graphene_rect_t *r,
2185 graphene_quad_t *res)
2186 {
2187 graphene_point_t ret[4];
2188 graphene_rect_t rr;
2189
2190 graphene_rect_normalize_r (r, &rr);
2191
2192 #define TRANSFORM_POINT(matrix, rect, corner, out_p) do {\
2193 graphene_vec4_t __s; \
2194 graphene_point_t __p; \
2195 float w; \
2196 graphene_rect_get_ ## corner (rect, &__p); \
2197 graphene_vec4_init (&__s, __p.x, __p.y, 0.f, 1.f); \
2198 graphene_matrix_transform_vec4 (matrix, &__s, &__s); \
2199 w = graphene_vec4_get_w (&__s); \
2200 out_p.x = graphene_vec4_get_x (&__s) / w; \
2201 out_p.y = graphene_vec4_get_y (&__s) / w; } while (0)
2202
2203 TRANSFORM_POINT (m, &rr, top_left, ret[0]);
2204 TRANSFORM_POINT (m, &rr, top_right, ret[1]);
2205 TRANSFORM_POINT (m, &rr, bottom_right, ret[2]);
2206 TRANSFORM_POINT (m, &rr, bottom_left, ret[3]);
2207
2208 #undef TRANSFORM_POINT
2209
2210 graphene_quad_init (res, &ret[0], &ret[1], &ret[2], &ret[3]);
2211 }
2212 void
gsk_matrix_transform_bounds(const graphene_matrix_t * m,const graphene_rect_t * r,graphene_rect_t * res)2213 gsk_matrix_transform_bounds (const graphene_matrix_t *m,
2214 const graphene_rect_t *r,
2215 graphene_rect_t *res)
2216 {
2217 graphene_quad_t q;
2218
2219 gsk_matrix_transform_rect (m, r, &q);
2220 graphene_quad_bounds (&q, res);
2221 }
2222